edu.stanford.epad.epadws.Main.java Source code

Java tutorial

Introduction

Here is the source code for edu.stanford.epad.epadws.Main.java

Source

/*******************************************************************************
 * Copyright (c) 2015 The Board of Trustees of the Leland Stanford Junior University
 * BY CLICKING ON "ACCEPT," DOWNLOADING, OR OTHERWISE USING EPAD, YOU AGREE TO THE FOLLOWING TERMS AND CONDITIONS:
 * STANFORD ACADEMIC SOFTWARE SOURCE CODE LICENSE FOR
 * "ePAD Annotation Platform for Radiology Images"
 *
 * This Agreement covers contributions to and downloads from the ePAD project ("ePAD") maintained by The Board of Trustees 
 * of the Leland Stanford Junior University ("Stanford"). 
 *
 * *   Part A applies to downloads of ePAD source code and/or data from ePAD. 
 *
 * *   Part B applies to contributions of software and/or data to ePAD (including making revisions of or additions to code 
 * and/or data already in ePAD), which may include source or object code. 
 *
 * Your download, copying, modifying, displaying, distributing or use of any ePAD software and/or data from ePAD 
 * (collectively, the "Software") is subject to Part A. Your contribution of software and/or data to ePAD (including any 
 * that occurred prior to the first publication of this Agreement) is a "Contribution" subject to Part B. Both Parts A and 
 * B shall be governed by and construed in accordance with the laws of the State of California without regard to principles 
 * of conflicts of law. Any legal action involving this Agreement or the Research Program will be adjudicated in the State 
 * of California. This Agreement shall supersede and replace any license terms that you may have agreed to previously with 
 * respect to ePAD.
 *
 * PART A. DOWNLOADING AGREEMENT - LICENSE FROM STANFORD WITH RIGHT TO SUBLICENSE ("SOFTWARE LICENSE").
 * 1. As used in this Software License, "you" means the individual downloading and/or using, reproducing, modifying, 
 * displaying and/or distributing Software and the institution or entity which employs or is otherwise affiliated with you. 
 * Stanford  hereby grants you, with right to sublicense, with respect to Stanford's rights in the Software, a 
 * royalty-free, non-exclusive license to use, reproduce, make derivative works of, display and distribute the Software, 
 * provided that: (a) you adhere to all of the terms and conditions of this Software License; (b) in connection with any 
 * copy, distribution of, or sublicense of all or any portion of the Software, the terms and conditions in this Software 
 * License shall appear in and shall apply to such copy and such sublicense, including without limitation all source and 
 * executable forms and on any user documentation, prefaced with the following words: "All or portions of this licensed 
 * product  have been obtained under license from The Board of Trustees of the Leland Stanford Junior University. and are 
 * subject to the following terms and conditions" AND any user interface to the Software or the "About" information display 
 * in the Software will display the following: "Powered by ePAD http://epad.stanford.edu;" (c) you preserve and maintain 
 * all applicable attributions, copyright notices and licenses included in or applicable to the Software; (d) modified 
 * versions of the Software must be clearly identified and marked as such, and must not be misrepresented as being the 
 * original Software; and (e) you consider making, but are under no obligation to make, the source code of any of your 
 * modifications to the Software freely available to others on an open source basis.
 *
 * 2. The license granted in this Software License includes without limitation the right to (i) incorporate the Software 
 * into your proprietary programs (subject to any restrictions applicable to such programs), (ii) add your own copyright 
 * statement to your modifications of the Software, and (iii) provide additional or different license terms and conditions 
 * in your sublicenses of modifications of the Software; provided that in each case your use, reproduction or distribution 
 * of such modifications otherwise complies with the conditions stated in this Software License.
 * 3. This Software License does not grant any rights with respect to third party software, except those rights that 
 * Stanford has been authorized by a third party to grant to you, and accordingly you are solely responsible for (i) 
 * obtaining any permissions from third parties that you need to use, reproduce, make derivative works of, display and 
 * distribute the Software, and (ii) informing your sublicensees, including without limitation your end-users, of their 
 * obligations to secure any such required permissions.
 * 4. You agree that you will use the Software in compliance with all applicable laws, policies and regulations including, 
 * but not limited to, those applicable to Personal Health Information ("PHI") and subject to the Institutional Review 
 * Board requirements of the your institution, if applicable. Licensee acknowledges and agrees that the Software is not 
 * FDA-approved, is intended only for research, and may not be used for clinical treatment purposes. Any commercialization 
 * of the Software is at the sole risk of you and the party or parties engaged in such commercialization. You further agree 
 * to use, reproduce, make derivative works of, display and distribute the Software in compliance with all applicable 
 * governmental laws, regulations and orders, including without limitation those relating to export and import control.
 * 5. You or your institution, as applicable, will indemnify, hold harmless, and defend Stanford against any third party 
 * claim of any kind made against Stanford arising out of or related to the exercise of any rights granted under this 
 * Agreement, the provision of Software, or the breach of this Agreement. Stanford provides the Software AS IS and WITH ALL 
 * FAULTS.  Stanford makes no representations and extends no warranties of any kind, either express or implied.  Among 
 * other things, Stanford disclaims any express or implied warranty in the Software:
 * (a)  of merchantability, of fitness for a particular purpose,
 * (b)  of non-infringement or 
 * (c)  arising out of any course of dealing.
 *
 * Title and copyright to the Program and any associated documentation shall at all times remain with Stanford, and 
 * Licensee agrees to preserve same. Stanford reserves the right to license the Program at any time for a fee.
 * 6. None of the names, logos or trademarks of Stanford or any of Stanford's affiliates or any of the Contributors, or any 
 * funding agency, may be used to endorse or promote products produced in whole or in part by operation of the Software or 
 * derived from or based on the Software without specific prior written permission from the applicable party.
 * 7. Any use, reproduction or distribution of the Software which is not in accordance with this Software License shall 
 * automatically revoke all rights granted to you under this Software License and render Paragraphs 1 and 2 of this 
 * Software License null and void.
 * 8. This Software License does not grant any rights in or to any intellectual property owned by Stanford or any 
 * Contributor except those rights expressly granted hereunder.
 *
 * PART B. CONTRIBUTION AGREEMENT - LICENSE TO STANFORD WITH RIGHT TO SUBLICENSE ("CONTRIBUTION AGREEMENT").
 * 1. As used in this Contribution Agreement, "you" means an individual providing a Contribution to ePAD and the 
 * institution or entity which employs or is otherwise affiliated with you.
 * 2. This Contribution Agreement applies to all Contributions made to ePAD at any time. By making a Contribution you 
 * represent that: (i) you are legally authorized and entitled by ownership or license to make such Contribution and to 
 * grant all licenses granted in this Contribution Agreement with respect to such Contribution; (ii) if your Contribution 
 * includes any patient data, all such data is de-identified in accordance with U.S. confidentiality and security laws and 
 * requirements, including but not limited to the Health Insurance Portability and Accountability Act (HIPAA) and its 
 * regulations, and your disclosure of such data for the purposes contemplated by this Agreement is properly authorized and 
 * in compliance with all applicable laws and regulations; and (iii) you have preserved in the Contribution all applicable 
 * attributions, copyright notices and licenses for any third party software or data included in the Contribution.
 * 3. Except for the licenses you grant in this Agreement, you reserve all right, title and interest in your Contribution.
 * 4. You hereby grant to Stanford, with the right to sublicense, a perpetual, worldwide, non-exclusive, no charge, 
 * royalty-free, irrevocable license to use, reproduce, make derivative works of, display and distribute the Contribution. 
 * If your Contribution is protected by patent, you hereby grant to Stanford, with the right to sublicense, a perpetual, 
 * worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under your interest in patent rights embodied in 
 * the Contribution, to make, have made, use, sell and otherwise transfer your Contribution, alone or in combination with 
 * ePAD or otherwise.
 * 5. You acknowledge and agree that Stanford ham may incorporate your Contribution into ePAD and may make your 
 * Contribution as incorporated available to members of the public on an open source basis under terms substantially in 
 * accordance with the Software License set forth in Part A of this Agreement. You further acknowledge and agree that 
 * Stanford shall have no liability arising in connection with claims resulting from your breach of any of the terms of 
 * this Agreement.
 * 6. YOU WARRANT THAT TO THE BEST OF YOUR KNOWLEDGE YOUR CONTRIBUTION DOES NOT CONTAIN ANY CODE OBTAINED BY YOU UNDER AN 
 * OPEN SOURCE LICENSE THAT REQUIRES OR PRESCRIBES DISTRBUTION OF DERIVATIVE WORKS UNDER SUCH OPEN SOURCE LICENSE. (By way 
 * of non-limiting example, you will not contribute any code obtained by you under the GNU General Public License or other 
 * so-called "reciprocal" license.)
 *******************************************************************************/
package edu.stanford.epad.epadws;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.BindException;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.xml.sax.SAXException;

import edu.stanford.epad.common.plugins.PluginController;
import edu.stanford.epad.common.plugins.PluginServletHandler;
import edu.stanford.epad.common.util.EPADConfig;
import edu.stanford.epad.common.util.EPADFileUtils;
import edu.stanford.epad.common.util.EPADLogger;
import edu.stanford.epad.dtos.EPADFileList;
import edu.stanford.epad.epadws.aim.AIMUtil;
import edu.stanford.epad.epadws.dcm4chee.Dcm4CheeOperations;
import edu.stanford.epad.epadws.epaddb.EpadDatabase;
import edu.stanford.epad.epadws.epaddb.EpadDatabaseOperations;
import edu.stanford.epad.epadws.handlers.admin.ConvertAIM4Handler;
import edu.stanford.epad.epadws.handlers.admin.CopyAimsToExistHandler;
import edu.stanford.epad.epadws.handlers.admin.ImageCheckHandler;
import edu.stanford.epad.epadws.handlers.admin.ImageReprocessingHandler;
import edu.stanford.epad.epadws.handlers.admin.ResourceCheckHandler;
import edu.stanford.epad.epadws.handlers.admin.ResourceFailureLogHandler;
import edu.stanford.epad.epadws.handlers.admin.ServerStatusHandler;
import edu.stanford.epad.epadws.handlers.admin.StatisticsHandler;
import edu.stanford.epad.epadws.handlers.admin.XNATSyncHandler;
import edu.stanford.epad.epadws.handlers.aim.AimResourceHandler;
import edu.stanford.epad.epadws.handlers.coordination.CoordinationHandler;
import edu.stanford.epad.epadws.handlers.core.EPADHandler;
import edu.stanford.epad.epadws.handlers.dicom.DownloadHandler;
import edu.stanford.epad.epadws.handlers.dicom.ResourcesFileHandler;
import edu.stanford.epad.epadws.handlers.dicom.WadoHandler;
import edu.stanford.epad.epadws.handlers.event.EventHandler;
import edu.stanford.epad.epadws.handlers.event.ProjectEventHandler;
import edu.stanford.epad.epadws.handlers.plugin.EPadPluginHandler;
import edu.stanford.epad.epadws.handlers.plugin.StatusListenerHandler;
import edu.stanford.epad.epadws.handlers.session.EPADSessionHandler;
import edu.stanford.epad.epadws.models.DisabledTemplate;
import edu.stanford.epad.epadws.models.EpadFile;
import edu.stanford.epad.epadws.models.FileType;
import edu.stanford.epad.epadws.models.Plugin;
import edu.stanford.epad.epadws.models.Project;
import edu.stanford.epad.epadws.models.ProjectToTemplate;
import edu.stanford.epad.epadws.models.User;
import edu.stanford.epad.epadws.plugins.PluginConfig;
import edu.stanford.epad.epadws.plugins.PluginHandlerMap;
import edu.stanford.epad.epadws.processing.pipeline.threads.ShutdownHookThread;
import edu.stanford.epad.epadws.processing.pipeline.threads.ShutdownSignal;
import edu.stanford.epad.epadws.processing.pipeline.watcher.QueueAndWatcherManager;
import edu.stanford.epad.epadws.queries.DefaultEpadOperations;
import edu.stanford.epad.epadws.queries.EpadOperations;
import edu.stanford.epad.epadws.service.DefaultEpadProjectOperations;
import edu.stanford.epad.epadws.service.PluginOperations;
import edu.stanford.epad.epadws.service.RemotePACService;

/**
 * Note: When the ePAD webservices and ePAD client is split into two separate webapps, this class becomes obsolete and is not used
 * 
 * Entry point for the ePAD Web Service.
 * <p>
 * Start an embedded Jetty server and all the threads required for this application. The application listens on the port
 * indicated by the property <i>ePadClientPort</i> in proxy-config.properties.
 * <p>
 * NOTE: The current directory must be set to the ePAD bin subdirectory (~epad/DicomProxy/bin) before calling the start
 * scripts associated with this application. Code in the WAR file needs to be updated to remove this restriction.
 */
public class Main {
    private static final EPADLogger log = EPADLogger.getInstance();

    public static boolean separateWebServicesApp = true;

    public static void main(String[] args) {
        ShutdownSignal shutdownSignal = ShutdownSignal.getInstance();
        Server server = null;

        try {
            checkPropertiesFile();
            checkResourcesFolders();
            checkPluginsFile();
            RemotePACService.checkPropertiesFile();
            int epadPort = EPADConfig.epadPort;
            Dcm4CheeOperations.checkScriptFiles();
            separateWebServicesApp = "true".equalsIgnoreCase(EPADConfig.getParamValue("SeparateWebServicesApp"));
            if (!separateWebServicesApp) {
                log.info("#####################################################");
                log.info("############# Starting ePAD Web Service #############");
                log.info("#####################################################");
                initializePlugins();
                startSupportThreads();
            } else {
                log.info("#####################################################");
                log.info("############# Starting ePAD GWT FrontEnd ############");
                log.info("#####################################################");
            }
            server = new Server(epadPort);
            configureJettyServer(server);
            addHandlers(server);
            Runtime.getRuntime().addShutdownHook(new ShutdownHookThread());
            log.info("Starting Jetty on port " + epadPort);
            server.start();
            setupTestFiles();
            server.join();
        } catch (BindException be) {
            log.severe("Bind exception", be);
            Throwable t = be.getCause();
            log.warning("Bind exception cause: " + be.getMessage(), t);
        } catch (SocketException se) {
            log.severe("Cannot bind to all sockets", se);
        } catch (Exception e) {
            log.severe("Fatal Exception. Shutting down ePAD Web Service", e);
        } catch (Error err) {
            log.severe("Fatal Error. Shutting down ePAD Web Service", err);
        } finally {
            log.info("#####################################################");
            log.info("############# Shutting down ePAD  ###################");
            log.info("#####################################################");

            shutdownSignal.shutdownNow();
            stopServer(server);
            if (!separateWebServicesApp) {
                EpadDatabase.getInstance().shutdown();
                QueueAndWatcherManager.getInstance().shutdown();
            }
            try { // Wait just long enough for some messages to be printed out.
                TimeUnit.MILLISECONDS.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        log.info("#####################################################");
        log.info("################## Exit ePAD Web Service ###########");
        log.info("#####################################################");
    }

    public static void checkPropertiesFile() {
        String macdockerhost = System.getenv("MACDOCKER_HOST");
        String dockerhost = macdockerhost;
        if (dockerhost == null)
            dockerhost = System.getenv("DOCKER_HOST");
        if (dockerhost != null && dockerhost.startsWith("tcp://"))
            dockerhost = dockerhost.substring(6);
        if (dockerhost != null && dockerhost.contains(":"))
            dockerhost = dockerhost.substring(0, dockerhost.indexOf(":"));
        String hostname = dockerhost;
        if (hostname == null || hostname.length() == 0)
            hostname = System.getenv("HOSTNAME");
        if (hostname == null || hostname.length() == 0)
            hostname = "localhost";
        File propertiesFile = new File(System.getProperty("user.home") + "/DicomProxy/etc/",
                EPADConfig.configFileName);
        File clientProperties = null;
        if (!propertiesFile.exists()) {
            File macproperties = new File(System.getProperty("user.home") + "/mac/etc/", EPADConfig.configFileName);
            if (macproperties.exists() || (macdockerhost != null && macdockerhost.length() > 0)) {
                clientProperties = propertiesFile;
                propertiesFile = macproperties;
            }
        }
        if (!propertiesFile.exists()) {
            propertiesFile.getParentFile().mkdirs();
            BufferedReader reader = null;
            InputStream is = null;
            StringBuilder sb = new StringBuilder();
            try {
                is = EPADFileUtils.class.getClassLoader().getResourceAsStream(propertiesFile.getName());
                reader = new BufferedReader(new InputStreamReader(is, "UTF8"));
                String line = null;
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                    sb.append("\n");
                }
            } catch (Exception x) {
                log.warning("Error creating epad properties file", x);
                return;
            } finally {
                if (reader != null)
                    IOUtils.closeQuietly(reader);
                else if (is != null)
                    IOUtils.closeQuietly(is);
            }
            String propsStr = sb.toString().replace("_HOSTNAME_", hostname);
            EPADFileUtils.write(propertiesFile, propsStr);
        }
        if (clientProperties != null) {
            EPADFileUtils.copyFile(propertiesFile, clientProperties);
        }
    }

    public static void checkPluginsFile() {
        File pluginsFile = new File(EPADConfig.getEPADWebServerPluginConfigFilePath());
        if (!pluginsFile.exists()) {
            pluginsFile.getParentFile().mkdirs();
            BufferedReader reader = null;
            InputStream is = null;
            StringBuilder sb = new StringBuilder();
            try {
                is = EPADFileUtils.class.getClassLoader().getResourceAsStream(pluginsFile.getName());
                reader = new BufferedReader(new InputStreamReader(is, "UTF8"));
                String line = null;
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                    sb.append("\n");
                }
            } catch (Exception x) {
                log.warning("Error creating plugin config file", x);
                return;
            } finally {
                if (reader != null)
                    IOUtils.closeQuietly(reader);
                else if (is != null)
                    IOUtils.closeQuietly(is);
            }
            EPADFileUtils.write(pluginsFile, sb.toString());
        }
    }

    static final String[] epadDockerScripts = { "epad_docker.sh", "stop_dockerepad.sh", "start_dockerepad.sh",
            "uninstall_dockerepad.sh", };
    static final String[] epadOtherScripts = { "plugin-manager.sh", //plugin manager script file
    };

    public static void checkResourcesFolders() {
        File folder = new File(EPADConfig.getEPADWebServerBaseDir() + "bin/");
        if (!folder.exists())
            folder.mkdirs();
        folder = new File(EPADConfig.getEPADWebServerBaseDir() + "tmp/");
        if (!folder.exists())
            folder.mkdirs();
        folder = new File(EPADConfig.getEPADWebServerResourcesDir());
        if (!folder.exists())
            folder.mkdirs();
        folder = new File(EPADConfig.getEPADWebServerEtcDir());
        if (!folder.exists())
            folder.mkdirs();
        folder = new File(EPADConfig.getEPADWebServerDICOMScriptsDir());
        if (!folder.exists())
            folder.mkdirs();
        folder = new File(EPADConfig.getEPADWebServerUploadDir());
        //ml if (!folder.exists()) folder.mkdirs(); //remove temp dirs
        if (folder.exists()) {
            log.info("Deleting existing temp upload directory " + folder.getName());
            EPADFileUtils.deleteDirectoryContents(folder);
        } else
            folder.mkdirs();
        folder = new File(EPADConfig.getEPADWebServerDownloadDir());
        if (folder.exists()) {
            log.info("Deleting existing temp download directory " + folder.getName());
            EPADFileUtils.deleteDirectoryContents(folder);
        } else
            folder.mkdirs();
        folder = new File(EPADConfig.getEPADWebServerDownloadWSDir());
        if (folder.exists()) {
            log.info("Deleting existing temp downloadWS directory " + folder.getName());
            EPADFileUtils.deleteDirectoryContents(folder);
        } else
            folder.mkdirs();
        folder = new File(EPADConfig.getEPADWebServerFileUploadDir());
        if (!folder.exists())
            folder.mkdirs();
        folder = new File(EPADConfig.getEPADWebServerAnnotationsDir());
        if (!folder.exists())
            folder.mkdirs();
        folder = new File(EPADConfig.getEPADWebServerAnnotationsUploadDir());
        if (!folder.exists())
            folder.mkdirs();
        folder = new File(EPADConfig.getEPADWebServerSchemaDir());
        if (!folder.exists())
            folder.mkdirs();
        log.info("checking and copying startup scripts");
        if (System.getenv("DCM4CHEE_NAME") != null) { //this is docker instance. copy docker versions of scripts
            log.info("getting docker versions");
            copyFiles(System.getProperty("user.home") + "/mac/bin/", EPADConfig.getEPADWebServerBaseDir() + "bin/",
                    "scripts/docker/", epadDockerScripts);
        }
        //copy other scripts
        copyFiles(System.getProperty("user.home") + "/mac/bin/", EPADConfig.getEPADWebServerBaseDir() + "bin/",
                "scripts/", epadOtherScripts);

    }

    private static void copyFiles(String macDir, String dicomProxyDir, String resourceDir, String[] scripts) {
        try {
            // Copy start/stop scripts over (mainly for docker)
            File binDir = new File(macDir);
            if (!binDir.exists())
                binDir = new File(dicomProxyDir);
            for (String scriptFile : scripts) {
                File file = new File(binDir, scriptFile);
                if (!file.exists()) {
                    InputStream in = null;
                    OutputStream out = null;
                    try {
                        in = new Dcm4CheeOperations().getClass().getClassLoader()
                                .getResourceAsStream(resourceDir + scriptFile);
                        out = new FileOutputStream(file);

                        // Transfer bytes from in to out
                        byte[] buf = new byte[1024];
                        int len;
                        while ((len = in.read(buf)) > 0) {
                            out.write(buf, 0, len);
                        }
                    } catch (Exception x) {

                    } finally {
                        IOUtils.closeQuietly(in);
                        IOUtils.closeQuietly(out);
                    }
                }
                if (file.exists())
                    file.setExecutable(true);
            }
        } catch (Exception x) {
            log.warning("Exception in docker script copy", x);
        }

    }

    private static void configureJettyServer(Server server) {
        FileInputStream jettyConfigFileStream = null;
        try {
            String jettyConfigFilePath = EPADConfig.getEPADWebServerJettyConfigFilePath();
            jettyConfigFileStream = new FileInputStream(jettyConfigFilePath);
            XmlConfiguration configuration = new XmlConfiguration(jettyConfigFileStream);
            configuration.configure(server);
            log.info("Jetty server configured using configuration file " + jettyConfigFilePath);
        } catch (FileNotFoundException e) {
            log.warning(
                    "Could not find Jetty configuration file " + EPADConfig.getEPADWebServerJettyConfigFilePath());
        } catch (SAXException e) {
            log.warning("SAX error reading Jetty configuration file "
                    + EPADConfig.getEPADWebServerJettyConfigFilePath(), e);
        } catch (IOException e) {
            log.warning(
                    "IO error reading Jetty configuration file " + EPADConfig.getEPADWebServerJettyConfigFilePath(),
                    e);
        } catch (Exception e) {
            log.warning(
                    "Error processing Jetty configuration file " + EPADConfig.getEPADWebServerJettyConfigFilePath(),
                    e);
        } finally {
            IOUtils.closeQuietly(jettyConfigFileStream);
        }
    }

    public static void initializePlugins() {
        PluginController.getInstance();
    }

    public static void startSupportThreads() {
        log.info("Starting support threads....");

        try {
            QueueAndWatcherManager.getInstance().buildAndStart();
            String version = new EPadWebServerVersion().getVersion();
            String db_version = version;
            if (db_version.indexOf(".") != db_version.lastIndexOf(".")) {
                // remove second period;
                db_version = db_version.substring(0, db_version.lastIndexOf("."))
                        + db_version.substring(db_version.lastIndexOf(".") + 1);
            }
            log.info("EpadWS version:" + version + " Database version:" + db_version);
            EpadDatabase.getInstance().startup(db_version);
            log.info("Startup of database was successful");
            EpadDatabaseOperations databaseOperations = EpadDatabase.getInstance().getEPADDatabaseOperations();
            //log.info("Checking annotations table");
            databaseOperations.checkAndRefreshAnnotationsTable();
            log.info("Done with database/queues init");
            List<User> users = DefaultEpadProjectOperations.getInstance().getAllUsers();
            if (EPADConfig.UseEPADUsersProjects && users.size() <= 1) {
                // Sync XNAT to Epad if needed
                try {
                    XNATSyncHandler.syncXNATtoEpad("admin", "");
                } catch (Exception x) {
                    log.warning("Error syncing XNAT data", x);
                }
            }
            AIMUtil.checkSchemaFiles();
            AIMUtil.checkTemplateFiles();
            checkTemplateProjectRel();

        } catch (Exception e) {
            log.warning("Failed to start database", e);
            System.exit(1);
        }
    }

    private static void checkTemplateProjectRel() {
        try {
            if (new ProjectToTemplate().getCount("") <= 0) {
                EpadOperations epadOperations = DefaultEpadOperations.getInstance();
                epadOperations.migrateTemplates();
            }

        } catch (Exception e) {
            log.warning("Couldn't get project template relation,", e);
        }

    }

    private static void addHandlers(Server server) {
        List<Handler> handlerList = new ArrayList<Handler>();

        if (!separateWebServicesApp) {

            loadPluginClasses();

            addHandlerAtContextPath(new EPADSessionHandler(), "/epad/session", handlerList);

            addHandlerAtContextPath(new EPADHandler(), "/epad/v2", handlerList);

        }

        //      My Attempt to replace above EPADHandler with Spring Controllers
        //      - Does not work with embedded jetty, controller methods don't map correctly, controller mapping works fine under tomcat
        //      - If someone can make it work, that would be great (comment out add EPADHandler above)
        //      try {
        //         handlerList.add(getServletContextHandler(getContext()));
        //      } catch (IOException e) {
        //         // TODO Auto-generated catch block
        //         log.warning("Error setting up Spring Handle", e);
        //         e.printStackTrace();
        //      }
        String webAppPath = System.getProperty("user.home") + "/mac/webapps/" + "ePad.war"; // This is specially for docker on macs
        if (!new File(webAppPath).exists()) {
            webAppPath = EPADConfig.getEPADWebServerWebappsDir() + "ePad.war"; // This is the usual, normal path
        }
        if (!new File(webAppPath).exists()) {
            webAppPath = EPADConfig.getEPADWebServerWebappsDir() + "epad-1.1.war"; // This is for development/testing
        }
        if (!new File(webAppPath).exists()) {
            webAppPath = System.getProperty("user.home") + "/epad/webapps/" + "ePad.war"; // Fallback for docker
            if (!new File(webAppPath).exists())
                throw new RuntimeException("ePad.war not found");
        }
        addWebAppAtContextPath(handlerList, webAppPath, "/epad");

        addHandlerAtContextPath(new ResourceCheckHandler(), "/epad/resources", handlerList);
        addFileServerAtContextPath(EPADConfig.getEPADWebServerResourcesDir(), handlerList, "/epad/resources");

        if (!separateWebServicesApp) {
            addHandlerAtContextPath(new WadoHandler(), "/epad/wado", handlerList);

            addHandlerAtContextPath(new AimResourceHandler(), "/epad/aimresource", handlerList);

            if (!"true".equalsIgnoreCase(EPADConfig.getParamValue("DISABLE_PLUGINS"))) {
                addHandlerAtContextPath(new EPadPluginHandler(), "/epad/plugin", handlerList);
            }
            addHandlerAtContextPath(new EventHandler(), "/epad/eventresource", handlerList);
            addHandlerAtContextPath(new ProjectEventHandler(), "/epad/events", handlerList);

            addHandlerAtContextPath(new ServerStatusHandler(), "/epad/status", handlerList);
            addHandlerAtContextPath(new ImageCheckHandler(), "/epad/imagecheck", handlerList);
            addHandlerAtContextPath(new ImageReprocessingHandler(), "/epad/imagereprocess", handlerList);
            addHandlerAtContextPath(new ConvertAIM4Handler(), "/epad/convertaim4", handlerList);
            addHandlerAtContextPath(new CopyAimsToExistHandler(), "/epad/copyToExist", handlerList);
            addHandlerAtContextPath(new XNATSyncHandler(), "/epad/syncxnat", handlerList);
            addHandlerAtContextPath(new StatisticsHandler(), "/epad/statistics", handlerList);
            addHandlerAtContextPath(new ResourcesFileHandler(), "/epad/resourcesFile", handlerList);
            addHandlerAtContextPath(new DownloadHandler(), "/epad/download", handlerList);

            addHandlerAtContextPath(new StatusListenerHandler(), "/epad/statuslistener", handlerList);

            // TODO This call will disappear when we switch to AIM4
            addHandlerAtContextPath(new CoordinationHandler(), "/epad/coordination", handlerList);
        }

        ContextHandlerCollection contexts = new ContextHandlerCollection();
        contexts.setHandlers(handlerList.toArray(new Handler[handlerList.size()]));
        server.setHandler(contexts);
        log.info("Done setting up restapi handlers");
    }

    private static void addHandlerAtContextPath(Handler handler, String contextPath, List<Handler> handlerList) {
        ContextHandler contextHandler = new ContextHandler(contextPath);

        contextHandler.setResourceBase(".");
        contextHandler.setClassLoader(Thread.currentThread().getContextClassLoader());
        contextHandler.setHandler(handler);
        handlerList.add(contextHandler);

        log.info("Added " + handler.getClass().getName() + " at context " + contextPath);
    }

    private static final String CONTEXT_PATH = "/epad/v2";
    private static final String CONFIG_LOCATION = "edu.stanford.epad.epadws.config.SpringConfig";
    private static final String MAPPING_URL = "/*";
    private static final String DEFAULT_PROFILE = "dev";

    /**
     * Function for setting up Spring Context with embedded Jetty
     * 
     */
    private static ServletContextHandler getServletContextHandler(WebApplicationContext context)
            throws IOException {
        ServletContextHandler contextHandler = new ServletContextHandler();
        contextHandler.setErrorHandler(null);
        contextHandler.setContextPath(CONTEXT_PATH);
        contextHandler.addServlet(new ServletHolder(new DispatcherServlet(context)), MAPPING_URL);
        contextHandler.addEventListener(new ContextLoaderListener(context));
        //contextHandler.setResourceBase(EPADConfig.getEPADWebServerResourcesDir());
        return contextHandler;
    }

    private static WebApplicationContext getContext() {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.setConfigLocation(CONFIG_LOCATION);
        context.getEnvironment().setDefaultProfiles(DEFAULT_PROFILE);
        return context;
    }

    /**
     * Adds a WAR file from the webapps directory at a context path.
     * 
     * @param handlerList List of handlers
     * @param webAppPath String war file name, with or without extension (e.g., ePad.war)
     * @param contextPath The context to add the war file (e.g., /epad)
     */
    private static void addWebAppAtContextPath(List<Handler> handlerList, String webAppPath, String contextPath) {
        if (!contextPath.startsWith("/")) {
            contextPath = "/" + contextPath;
        }
        WebAppContext webAppContext = new WebAppContext(webAppPath, contextPath);
        String home = System.getProperty("user.home");
        webAppContext.setTempDirectory(new File(home + "/DicomProxy/jetty")); // TODO Read from config file
        webAppContext.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
        if (new File(EPADConfig.getEPADWebServerEtcDir() + "webdefault.xml").exists()) {
            log.info("Adding webdefault.xml");
            webAppContext.setDefaultsDescriptor(EPADConfig.getEPADWebServerEtcDir() + "webdefault.xml");
        }
        log.info("WebAuthFilter:'" + EPADConfig.getParamValue("WebAuthFilter", null) + "'");
        if (EPADConfig.webAuthPassword != null && EPADConfig.getParamValue("WebAuthFilter", null) != null) {
            try {
                Class filter = Class.forName(EPADConfig.getParamValue("WebAuthFilter",
                        "edu.stanford.epad.epadws.security.WebAuthFilter"));
                webAppContext.addFilter(filter, "/*",
                        EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.FORWARD));
            } catch (ClassNotFoundException e) {
                log.warning("WebAuth Authentication Filter " + EPADConfig.getParamValue("WebAuthFilter")
                        + " not found");
            }
        }
        handlerList.add(webAppContext);
        log.info("Added WAR " + webAppPath + " at context path " + contextPath);
    }

    private static void addFileServerAtContextPath(String baseDirectory, List<Handler> handlerList,
            String contextPath) {
        ResourceHandler resourceHandler = new ResourceHandler();
        resourceHandler.setDirectoriesListed(false);
        resourceHandler.setWelcomeFiles(new String[] { "index.html" });
        resourceHandler.setResourceBase(baseDirectory);

        HandlerList handlers = new HandlerList();
        handlers.setHandlers(
                new Handler[] { resourceHandler, new ResourceFailureLogHandler(), new DefaultHandler() });

        addHandlerAtContextPath(handlers, contextPath, handlerList);

        log.info("Added file server for " + baseDirectory + " directory.");
    }

    public static boolean testPages = false;

    private static void setupTestFiles() {
        String deployPath = EPADConfig.getEPADWebServerBaseDir() + "jetty/webapp/test/";
        File testFilesDir = new File(EPADConfig.getEPADWebServerBaseDir() + "webapps/test/");
        try {
            if (testFilesDir.exists() && testFilesDir.isDirectory()) {
                File deployDir = new File(deployPath);
                if (!deployDir.exists())
                    deployDir.mkdirs();
                File[] testFiles = testFilesDir.listFiles();
                for (File f : testFiles) {
                    if (f.isDirectory())
                        continue;
                    File outFile = new File(deployDir, f.getName());
                    EPADFileUtils.copyFile(f, outFile);
                }
                testPages = true;
            }
        } catch (Exception x) {

        }
        File pluginsDir = new File(EPADConfig.getEPADWebServerBaseDir() + "webapps/plugins/");
        deployPath = EPADConfig.getEPADWebServerBaseDir() + "jetty/webapp/plugins/";
        try {
            if (pluginsDir.exists() && pluginsDir.isDirectory()) {
                File deployDir = new File(deployPath);
                if (!deployDir.exists())
                    deployDir.mkdirs();
                File[] pluginFiles = pluginsDir.listFiles();
                for (File f : pluginFiles) {
                    if (f.isDirectory())
                        continue;
                    File outFile = new File(deployDir, f.getName());
                    EPADFileUtils.copyFile(f, outFile);
                }
            }
        } catch (Exception x) {

        }
    }

    /**
     * Load all the plugins into a map.
     */
    public static void loadPluginClasses() {
        try {
            PluginHandlerMap pluginHandlerMap = PluginHandlerMap.getInstance();
            PluginConfig pluginConfig = PluginConfig.getInstance();
            List<String> pluginHandlerList = pluginConfig.getPluginHandlerList();
            PluginOperations pluginOps = PluginOperations.getInstance();
            List<Plugin> plugins = new ArrayList<Plugin>();
            try {
                plugins = pluginOps.getPlugins();
            } catch (Exception x) {
            }
            ;

            for (String pluginClassName : pluginHandlerList) {
                log.info("Loading plugin class: " + pluginClassName);
                try {
                    PluginServletHandler psh = pluginHandlerMap.loadFromClassName(pluginClassName);
                    if (psh != null) {
                        String pluginName = psh.getName();
                        pluginHandlerMap.setPluginServletHandler(pluginName, psh);
                    } else {
                        log.warning("Could not find plugin class: " + pluginClassName);
                    }
                } catch (Exception x) {
                    for (Plugin plugin : plugins) {
                        if (plugin.getJavaclass().equals(pluginClassName)) {
                            plugin.setStatus("Error loading class:" + x.getMessage());
                            try {
                                plugin.save();
                            } catch (Exception x2) {
                            }
                        }
                    }
                }
            }
        } catch (Exception x) {
            log.warning("Error loading plugin", x);
        }
        log.info("Done loading plugins");
    }

    private static void stopServer(Server server) {
        try {
            if (server != null) {
                log.info("#####################################################");
                log.info("############### Stopping Jetty server ###############");
                log.info("#####################################################");
                server.stop();
            }
        } catch (Exception e) {
            log.warning("Failed to stop the Jetty server", e);
        }
    }
}