diff --git a/web/src/main/java/org/openmrs/web/filter/initialization/InitializationFilter.java b/web/src/main/java/org/openmrs/web/filter/initialization/InitializationFilter.java index c47a0aea1e93..6c23bb8246af 100644 --- a/web/src/main/java/org/openmrs/web/filter/initialization/InitializationFilter.java +++ b/web/src/main/java/org/openmrs/web/filter/initialization/InitializationFilter.java @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. - * + * * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS * graphic logo is a trademark of OpenMRS Inc. */ @@ -50,7 +50,6 @@ import org.openmrs.api.context.Context; import org.openmrs.api.context.ContextAuthenticationException; import org.openmrs.api.context.UsernamePasswordCredentials; -import org.openmrs.api.impl.UserServiceImpl; import org.openmrs.liquibase.ChangeLogDetective; import org.openmrs.liquibase.ChangeLogVersionFinder; import org.openmrs.module.MandatoryModuleException; @@ -85,116 +84,116 @@ * {@link Listener} wasn't able to find any runtime properties */ public class InitializationFilter extends StartupFilter { - + private static final org.slf4j.Logger log = LoggerFactory.getLogger(InitializationFilter.class); - + private static final String DATABASE_POSTGRESQL = "postgresql"; - + private static final String DATABASE_MYSQL = "mysql"; - + private static final String DATABASE_SQLSERVER = "sqlserver"; - + private static final String DATABASE_H2 = "h2"; - + private static final String LIQUIBASE_DEMO_DATA = "liquibase-demo-data.xml"; - + /** * The very first page of wizard, that asks user for select his preferred language */ private static final String CHOOSE_LANG = "chooselang.vm"; - + /** * The second page of the wizard that asks for simple or advanced installation. */ private static final String INSTALL_METHOD = "installmethod.vm"; - + /** * The simple installation setup page. */ private static final String SIMPLE_SETUP = "simplesetup.vm"; - + /** * The first page of the advanced installation of the wizard that asks for a current or past * database */ private static final String DATABASE_SETUP = "databasesetup.vm"; - + /** * The page from where the user specifies the url to a remote system, username and password */ private static final String TESTING_REMOTE_DETAILS_SETUP = "remotedetails.vm"; - + /** * The velocity macro page to redirect to if an error occurs or on initial startup */ private static final String DEFAULT_PAGE = CHOOSE_LANG; - + /** * This page asks whether database tables/demo data should be inserted and what the * username/password that will be put into the runtime properties is */ private static final String DATABASE_TABLES_AND_USER = "databasetablesanduser.vm"; - + /** * This page lets the user define the admin user */ private static final String ADMIN_USER_SETUP = "adminusersetup.vm"; - + /** * This page lets the user pick an implementation id */ private static final String IMPLEMENTATION_ID_SETUP = "implementationidsetup.vm"; - + /** * This page asks for settings that will be put into the runtime properties files */ private static final String OTHER_RUNTIME_PROPS = "otherruntimeproperties.vm"; - + /** * A page that tells the user that everything is collected and will now be processed */ private static final String WIZARD_COMPLETE = "wizardcomplete.vm"; - + /** * A page that lists off what is happening while it is going on. This page has ajax that callst he * {@value #PROGRESS_VM_AJAXREQUEST} page */ private static final String PROGRESS_VM = "progress.vm"; - + /** * This url is called by javascript to get the status of the install */ private static final String PROGRESS_VM_AJAXREQUEST = "progress.vm.ajaxRequest"; - + public static final String RELEASE_TESTING_MODULE_PATH = "/module/releasetestinghelper/"; - + /** * The model object that holds all the properties that the rendered templates use. All attributes on * this object are made available to all templates via reflection in the * {@link org.openmrs.web.filter.StartupFilter#renderTemplate(String, Map, HttpServletResponse)} method. */ private InitializationWizardModel wizardModel = null; - + private InitializationCompletion initJob; - + /** * Variable set to true as soon as the installation begins and set to false when the process ends * This thread should only be accesses through the synchronized method. */ private static boolean isInstallationStarted = false; - + // the actual driver loaded by the DatabaseUpdater class private String loadedDriverString; - + /** * Variable set at the end of the wizard when spring is being restarted */ private static boolean initializationComplete = false; - + protected synchronized void setInitializationComplete(boolean initializationComplete) { InitializationFilter.initializationComplete = initializationComplete; } - + /** * Called by {@link #doFilter(ServletRequest, ServletResponse, FilterChain)} on GET requests * @@ -205,20 +204,20 @@ protected synchronized void setInitializationComplete(boolean initializationComp protected void doGet(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { loadInstallationScriptIfPresent(); - + // we need to save current user language in references map since it will be used when template // will be rendered if (httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE) == null) { checkLocaleAttributesForFirstTime(httpRequest); } - + Map referenceMap = new HashMap<>(); String page = httpRequest.getParameter("page"); - + referenceMap.put(FilterUtil.LOCALE_ATTRIBUTE, httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); - + httpResponse.setHeader("Cache-Control", "no-cache"); - + // if any body has already started installation and this is not an ajax request for the progress if (isInstallationStarted() && !PROGRESS_VM_AJAXREQUEST.equals(page)) { referenceMap.put("isInstallationStarted", true); @@ -233,7 +232,7 @@ protected void doGet(HttpServletRequest httpRequest, HttpServletResponse httpRes result.put("errorPage", initJob.getErrorPage()); errors.putAll(initJob.getErrors()); } - + result.put("initializationComplete", isInitializationComplete()); result.put("message", initJob.getMessage()); result.put("actionCounter", initJob.getStepsComplete()); @@ -242,10 +241,10 @@ protected void doGet(HttpServletRequest httpRequest, HttpServletResponse httpRes result.put("executedTasks", initJob.getExecutedTasks()); result.put("completedPercentage", initJob.getCompletedPercentage()); } - + addLogLinesToResponse(result); } - + PrintWriter writer = httpResponse.getWriter(); writer.write(toJSONString(result)); writer.close(); @@ -257,16 +256,16 @@ protected void doGet(HttpServletRequest httpRequest, HttpServletResponse httpRes renderTemplate(PROGRESS_VM, referenceMap, httpResponse); } else if (page == null) { httpResponse.setContentType("text/html");// if any body has already started installation - + //If someone came straight here without setting the hidden page input, // then we need to clear out all the passwords clearPasswords(); - + renderTemplate(DEFAULT_PAGE, referenceMap, httpResponse); } else if (INSTALL_METHOD.equals(page)) { // get props and render the second page File runtimeProperties = getRuntimePropertiesFile(); - + if (!runtimeProperties.exists()) { try { runtimeProperties.createNewFile(); @@ -278,48 +277,48 @@ protected void doGet(HttpServletRequest httpRequest, HttpServletResponse httpRes wizardModel.canCreate = false; wizardModel.cannotCreateErrorMessage = io.getMessage(); } - + // check this before deleting the file again wizardModel.canWrite = runtimeProperties.canWrite(); - + // delete the file again after testing the create/write // so that if the user stops the webapp before finishing // this wizard, they can still get back into it runtimeProperties.delete(); - + } else { wizardModel.canWrite = runtimeProperties.canWrite(); - + wizardModel.databaseConnection = Context.getRuntimeProperties().getProperty("connection.url", wizardModel.databaseConnection); - + wizardModel.currentDatabaseUsername = Context.getRuntimeProperties().getProperty("connection.username", wizardModel.currentDatabaseUsername); - + wizardModel.currentDatabasePassword = Context.getRuntimeProperties().getProperty("connection.password", wizardModel.currentDatabasePassword); } - + wizardModel.runtimePropertiesPath = runtimeProperties.getAbsolutePath(); - + // do step one of the wizard httpResponse.setContentType("text/html"); renderTemplate(INSTALL_METHOD, referenceMap, httpResponse); } } - + private void loadInstallationScriptIfPresent() { Properties script = getInstallationScript(); if (!script.isEmpty()) { wizardModel.installMethod = script.getProperty("install_method", wizardModel.installMethod); - + wizardModel.databaseConnection = script.getProperty("connection.url", wizardModel.databaseConnection); wizardModel.databaseDriver = script.getProperty("connection.driver_class", wizardModel.databaseDriver); wizardModel.currentDatabaseUsername = script.getProperty("connection.username", wizardModel.currentDatabaseUsername); wizardModel.currentDatabasePassword = script.getProperty("connection.password", wizardModel.currentDatabasePassword); - + String hasCurrentOpenmrsDatabase = script.getProperty("has_current_openmrs_database"); if (hasCurrentOpenmrsDatabase != null) { wizardModel.hasCurrentOpenmrsDatabase = Boolean.valueOf(hasCurrentOpenmrsDatabase); @@ -328,38 +327,38 @@ private void loadInstallationScriptIfPresent() { wizardModel.createDatabaseUsername); wizardModel.createDatabasePassword = script.getProperty("create_database_password", wizardModel.createDatabasePassword); - + String createTables = script.getProperty("create_tables"); if (createTables != null) { wizardModel.createTables = Boolean.valueOf(createTables); } - + String createDatabaseUser = script.getProperty("create_database_user"); if (createDatabaseUser != null) { wizardModel.createDatabaseUser = Boolean.valueOf(createDatabaseUser); } wizardModel.createUserUsername = script.getProperty("create_user_username", wizardModel.createUserUsername); wizardModel.createUserPassword = script.getProperty("create_user_password", wizardModel.createUserPassword); - + String addDemoData = script.getProperty("add_demo_data"); if (addDemoData != null) { wizardModel.addDemoData = Boolean.valueOf(addDemoData); } - + String moduleWebAdmin = script.getProperty("module_web_admin"); if (moduleWebAdmin != null) { wizardModel.moduleWebAdmin = Boolean.valueOf(moduleWebAdmin); } - + String autoUpdateDatabase = script.getProperty("auto_update_database"); if (autoUpdateDatabase != null) { wizardModel.autoUpdateDatabase = Boolean.valueOf(autoUpdateDatabase); } - + wizardModel.adminUserPassword = script.getProperty("admin_user_password", wizardModel.adminUserPassword); } } - + private void clearPasswords() { wizardModel.databaseRootPassword = ""; wizardModel.createDatabasePassword = ""; @@ -367,7 +366,7 @@ private void clearPasswords() { wizardModel.currentDatabasePassword = ""; wizardModel.remotePassword = ""; } - + /** * Called by {@link #doFilter(ServletRequest, ServletResponse, FilterChain)} on POST requests * @@ -385,7 +384,7 @@ protected void doPost(HttpServletRequest httpRequest, HttpServletResponse httpRe referenceMap.put(FilterUtil.LOCALE_ATTRIBUTE, httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); } - + // if any body has already started installation if (isInstallationStarted()) { referenceMap.put("isInstallationStarted", true); @@ -393,432 +392,43 @@ protected void doPost(HttpServletRequest httpRequest, HttpServletResponse httpRe renderTemplate(PROGRESS_VM, referenceMap, httpResponse); return; } - if (DEFAULT_PAGE.equals(page)) { - // get props and render the first page - File runtimeProperties = getRuntimePropertiesFile(); - if (!runtimeProperties.exists()) { - try { - runtimeProperties.createNewFile(); - // reset the error objects in case of refresh - wizardModel.canCreate = true; - wizardModel.cannotCreateErrorMessage = ""; - } - catch (IOException io) { - wizardModel.canCreate = false; - wizardModel.cannotCreateErrorMessage = io.getMessage(); - } - // check this before deleting the file again - wizardModel.canWrite = runtimeProperties.canWrite(); - - // delete the file again after testing the create/write - // so that if the user stops the webapp before finishing - // this wizard, they can still get back into it - runtimeProperties.delete(); - } else { - wizardModel.canWrite = runtimeProperties.canWrite(); - - wizardModel.databaseConnection = Context.getRuntimeProperties().getProperty("connection.url", - wizardModel.databaseConnection); - - wizardModel.currentDatabaseUsername = Context.getRuntimeProperties().getProperty("connection.username", - wizardModel.currentDatabaseUsername); - - wizardModel.currentDatabasePassword = Context.getRuntimeProperties().getProperty("connection.password", - wizardModel.currentDatabasePassword); - } - - wizardModel.runtimePropertiesPath = runtimeProperties.getAbsolutePath(); - - checkLocaleAttributes(httpRequest); - referenceMap.put(FilterUtil.LOCALE_ATTRIBUTE, - httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); - log.info("Locale stored in session is " + httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); - - httpResponse.setContentType("text/html"); - // otherwise do step one of the wizard - renderTemplate(INSTALL_METHOD, referenceMap, httpResponse); - } else if (INSTALL_METHOD.equals(page)) { - if (goBack(httpRequest)) { - referenceMap.put(FilterUtil.REMEMBER_ATTRIBUTE, - httpRequest.getSession().getAttribute(FilterUtil.REMEMBER_ATTRIBUTE) != null); - referenceMap.put(FilterUtil.LOCALE_ATTRIBUTE, - httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); - renderTemplate(CHOOSE_LANG, referenceMap, httpResponse); - return; - } - wizardModel.installMethod = httpRequest.getParameter("install_method"); - if (InitializationWizardModel.INSTALL_METHOD_SIMPLE.equals(wizardModel.installMethod)) { - page = SIMPLE_SETUP; - } else if (InitializationWizardModel.INSTALL_METHOD_TESTING.equals(wizardModel.installMethod)) { - page = TESTING_REMOTE_DETAILS_SETUP; - wizardModel.currentStepNumber = 1; - wizardModel.numberOfSteps = skipDatabaseSetupPage() ? 1 : 3; - } else { - page = DATABASE_SETUP; - wizardModel.currentStepNumber = 1; - wizardModel.numberOfSteps = 5; - } - renderTemplate(page, referenceMap, httpResponse); - } // simple method - else if (SIMPLE_SETUP.equals(page)) { - if (goBack(httpRequest)) { - renderTemplate(INSTALL_METHOD, referenceMap, httpResponse); - return; - } - wizardModel.databaseConnection = httpRequest.getParameter("database_connection"); - ; - - wizardModel.createDatabaseUsername = Context.getRuntimeProperties().getProperty("connection.username", - wizardModel.createDatabaseUsername); - - wizardModel.createUserUsername = wizardModel.createDatabaseUsername; - - wizardModel.databaseRootPassword = httpRequest.getParameter("database_root_password"); - checkForEmptyValue(wizardModel.databaseRootPassword, errors, ErrorMessageConstants.ERROR_DB_PSDW_REQ); - - wizardModel.hasCurrentOpenmrsDatabase = false; - wizardModel.createTables = true; - // default wizardModel.databaseName is openmrs - // default wizardModel.createDatabaseUsername is root - wizardModel.createDatabasePassword = wizardModel.databaseRootPassword; - wizardModel.addDemoData = "yes".equals(httpRequest.getParameter("add_demo_data")); - - wizardModel.hasCurrentDatabaseUser = false; - wizardModel.createDatabaseUser = true; - // default wizardModel.createUserUsername is root - wizardModel.createUserPassword = wizardModel.databaseRootPassword; - - wizardModel.moduleWebAdmin = true; - wizardModel.autoUpdateDatabase = false; - - wizardModel.adminUserPassword = InitializationWizardModel.ADMIN_DEFAULT_PASSWORD; - - createSimpleSetup(httpRequest.getParameter("database_root_password"), httpRequest.getParameter("add_demo_data")); - - try { - loadedDriverString = DatabaseUtil.loadDatabaseDriver(wizardModel.databaseConnection, - wizardModel.databaseDriver); - } - catch (ClassNotFoundException e) { - errors.put(ErrorMessageConstants.ERROR_DB_DRIVER_CLASS_REQ, null); - renderTemplate(page, referenceMap, httpResponse); - return; - } - - if (errors.isEmpty()) { - page = WIZARD_COMPLETE; - } - renderTemplate(page, referenceMap, httpResponse); - } // step one - else if (DATABASE_SETUP.equals(page)) { - if (goBack(httpRequest)) { - wizardModel.currentStepNumber -= 1; - if (InitializationWizardModel.INSTALL_METHOD_TESTING.equals(wizardModel.installMethod)) { - renderTemplate(TESTING_REMOTE_DETAILS_SETUP, referenceMap, httpResponse); - } else { - renderTemplate(INSTALL_METHOD, referenceMap, httpResponse); - } - return; - } - - wizardModel.databaseConnection = httpRequest.getParameter("database_connection"); - checkForEmptyValue(wizardModel.databaseConnection, errors, ErrorMessageConstants.ERROR_DB_CONN_REQ); - - wizardModel.databaseDriver = httpRequest.getParameter("database_driver"); - checkForEmptyValue(wizardModel.databaseConnection, errors, ErrorMessageConstants.ERROR_DB_DRIVER_REQ); - - loadedDriverString = loadDriver(wizardModel.databaseConnection, wizardModel.databaseDriver); - if (!StringUtils.hasText(loadedDriverString)) { - errors.put(ErrorMessageConstants.ERROR_DB_DRIVER_CLASS_REQ, null); - renderTemplate(page, referenceMap, httpResponse); - return; - } - - //TODO make each bit of page logic a (unit testable) method - - // asked the user for their desired database name - - if ("yes".equals(httpRequest.getParameter("current_openmrs_database"))) { - wizardModel.databaseName = httpRequest.getParameter("openmrs_current_database_name"); - checkForEmptyValue(wizardModel.databaseName, errors, ErrorMessageConstants.ERROR_DB_CURR_NAME_REQ); - wizardModel.hasCurrentOpenmrsDatabase = true; - // TODO check to see if this is an active database - - } else { - // mark this wizard as a "to create database" (done at the end) - wizardModel.hasCurrentOpenmrsDatabase = false; - - wizardModel.createTables = true; - - wizardModel.databaseName = httpRequest.getParameter("openmrs_new_database_name"); - checkForEmptyValue(wizardModel.databaseName, errors, ErrorMessageConstants.ERROR_DB_NEW_NAME_REQ); - // TODO create database now to check if its possible? - - wizardModel.createDatabaseUsername = httpRequest.getParameter("create_database_username"); - checkForEmptyValue(wizardModel.createDatabaseUsername, errors, ErrorMessageConstants.ERROR_DB_USER_NAME_REQ); - wizardModel.createDatabasePassword = httpRequest.getParameter("create_database_password"); - checkForEmptyValue(wizardModel.createDatabasePassword, errors, ErrorMessageConstants.ERROR_DB_USER_PSWD_REQ); - } - - if (errors.isEmpty()) { - page = DATABASE_TABLES_AND_USER; - - if (InitializationWizardModel.INSTALL_METHOD_TESTING.equals(wizardModel.installMethod)) { - wizardModel.currentStepNumber = 3; - } else { - wizardModel.currentStepNumber = 2; - } - } - - renderTemplate(page, referenceMap, httpResponse); - - } // step two - else if (DATABASE_TABLES_AND_USER.equals(page)) { - - if (goBack(httpRequest)) { - wizardModel.currentStepNumber -= 1; - renderTemplate(DATABASE_SETUP, referenceMap, httpResponse); - return; - } - - if (wizardModel.hasCurrentOpenmrsDatabase) { - wizardModel.createTables = "yes".equals(httpRequest.getParameter("create_tables")); - } - - wizardModel.addDemoData = "yes".equals(httpRequest.getParameter("add_demo_data")); - - if ("yes".equals(httpRequest.getParameter("current_database_user"))) { - wizardModel.currentDatabaseUsername = httpRequest.getParameter("current_database_username"); - checkForEmptyValue(wizardModel.currentDatabaseUsername, errors, - ErrorMessageConstants.ERROR_DB_CUR_USER_NAME_REQ); - wizardModel.currentDatabasePassword = httpRequest.getParameter("current_database_password"); - checkForEmptyValue(wizardModel.currentDatabasePassword, errors, - ErrorMessageConstants.ERROR_DB_CUR_USER_PSWD_REQ); - wizardModel.hasCurrentDatabaseUser = true; - wizardModel.createDatabaseUser = false; - } else { - wizardModel.hasCurrentDatabaseUser = false; - wizardModel.createDatabaseUser = true; - // asked for the root mysql username/password - wizardModel.createUserUsername = httpRequest.getParameter("create_user_username"); - checkForEmptyValue(wizardModel.createUserUsername, errors, ErrorMessageConstants.ERROR_DB_USER_NAME_REQ); - wizardModel.createUserPassword = httpRequest.getParameter("create_user_password"); - checkForEmptyValue(wizardModel.createUserPassword, errors, ErrorMessageConstants.ERROR_DB_USER_PSWD_REQ); - } - - if (errors.isEmpty()) { // go to next page - page = InitializationWizardModel.INSTALL_METHOD_TESTING.equals(wizardModel.installMethod) ? WIZARD_COMPLETE - : OTHER_RUNTIME_PROPS; - } - - renderTemplate(page, referenceMap, httpResponse); - } // step three - else if (OTHER_RUNTIME_PROPS.equals(page)) { - - if (goBack(httpRequest)) { - renderTemplate(DATABASE_TABLES_AND_USER, referenceMap, httpResponse); - return; - } - - wizardModel.moduleWebAdmin = "yes".equals(httpRequest.getParameter("module_web_admin")); - wizardModel.autoUpdateDatabase = "yes".equals(httpRequest.getParameter("auto_update_database")); - - if (wizardModel.createTables) { // go to next page if they are creating tables - page = ADMIN_USER_SETUP; - } else { // skip a page - page = IMPLEMENTATION_ID_SETUP; - } - - renderTemplate(page, referenceMap, httpResponse); - - } // optional step four - else if (ADMIN_USER_SETUP.equals(page)) { - - if (goBack(httpRequest)) { - renderTemplate(OTHER_RUNTIME_PROPS, referenceMap, httpResponse); - return; - } - - wizardModel.adminUserPassword = httpRequest.getParameter("new_admin_password"); - String adminUserConfirm = httpRequest.getParameter("new_admin_password_confirm"); - - // throw back to admin user if passwords don't match - if (!wizardModel.adminUserPassword.equals(adminUserConfirm)) { - errors.put(ErrorMessageConstants.ERROR_DB_ADM_PSWDS_MATCH, null); - renderTemplate(ADMIN_USER_SETUP, referenceMap, httpResponse); - return; - } - - // throw back if the user didn't put in a password - if ("".equals(wizardModel.adminUserPassword)) { - errors.put(ErrorMessageConstants.ERROR_DB_ADM_PSDW_EMPTY, null); - renderTemplate(ADMIN_USER_SETUP, referenceMap, httpResponse); - return; - } - - try { - OpenmrsUtil.validatePassword("admin", wizardModel.adminUserPassword, "admin"); - } - catch (PasswordException p) { - errors.put(ErrorMessageConstants.ERROR_DB_ADM_PSDW_WEAK, null); - renderTemplate(ADMIN_USER_SETUP, referenceMap, httpResponse); - return; - } - - if (errors.isEmpty()) { // go to next page - page = IMPLEMENTATION_ID_SETUP; - } - - renderTemplate(page, referenceMap, httpResponse); - - } // optional step five - else if (IMPLEMENTATION_ID_SETUP.equals(page)) { - - if (goBack(httpRequest)) { - if (wizardModel.createTables) { - renderTemplate(ADMIN_USER_SETUP, referenceMap, httpResponse); - } else { - renderTemplate(OTHER_RUNTIME_PROPS, referenceMap, httpResponse); - } - return; - } - - wizardModel.implementationIdName = httpRequest.getParameter("implementation_name"); - wizardModel.implementationId = httpRequest.getParameter("implementation_id"); - wizardModel.implementationIdPassPhrase = httpRequest.getParameter("pass_phrase"); - wizardModel.implementationIdDescription = httpRequest.getParameter("description"); - - // throw back if the user-specified ID is invalid (contains ^ or |). - if (wizardModel.implementationId.indexOf('^') != -1 || wizardModel.implementationId.indexOf('|') != -1) { - errors.put(ErrorMessageConstants.ERROR_DB_IMPL_ID_REQ, null); - renderTemplate(IMPLEMENTATION_ID_SETUP, referenceMap, httpResponse); - return; - } - - if (errors.isEmpty()) { // go to next page - page = WIZARD_COMPLETE; - } - - renderTemplate(page, referenceMap, httpResponse); - } else if (WIZARD_COMPLETE.equals(page)) { - - if (goBack(httpRequest)) { - - if (InitializationWizardModel.INSTALL_METHOD_SIMPLE.equals(wizardModel.installMethod)) { - page = SIMPLE_SETUP; - } else if (InitializationWizardModel.INSTALL_METHOD_TESTING.equals(wizardModel.installMethod)) { - if (skipDatabaseSetupPage()) { - page = TESTING_REMOTE_DETAILS_SETUP; - } else { - page = DATABASE_TABLES_AND_USER; - } - } else { - page = IMPLEMENTATION_ID_SETUP; - } - renderTemplate(page, referenceMap, httpResponse); - return; - } - - wizardModel.tasksToExecute = new ArrayList<>(); - createDatabaseTask(); - if (InitializationWizardModel.INSTALL_METHOD_TESTING.equals(wizardModel.installMethod)) { - wizardModel.importTestData = true; - wizardModel.createTables = false; - wizardModel.addDemoData = false; - //if we have a runtime properties file - if (skipDatabaseSetupPage()) { - wizardModel.hasCurrentOpenmrsDatabase = false; - wizardModel.hasCurrentDatabaseUser = true; - wizardModel.createDatabaseUser = false; - Properties props = OpenmrsUtil.getRuntimeProperties(WebConstants.WEBAPP_NAME); - wizardModel.currentDatabaseUsername = props.getProperty("connection.username"); - wizardModel.currentDatabasePassword = props.getProperty("connection.password"); - wizardModel.createDatabaseUsername = wizardModel.currentDatabaseUsername; - wizardModel.createDatabasePassword = wizardModel.currentDatabasePassword; - } - - wizardModel.tasksToExecute.add(WizardTask.IMPORT_TEST_DATA); - wizardModel.tasksToExecute.add(WizardTask.ADD_MODULES); - } else { - createTablesTask(); - createDemoDataTask(); - } - wizardModel.tasksToExecute.add(WizardTask.UPDATE_TO_LATEST); - - referenceMap.put("tasksToExecute", wizardModel.tasksToExecute); - startInstallation(); - renderTemplate(PROGRESS_VM, referenceMap, httpResponse); - } else if (TESTING_REMOTE_DETAILS_SETUP.equals(page)) { - if (goBack(httpRequest)) { - wizardModel.currentStepNumber -= 1; - renderTemplate(INSTALL_METHOD, referenceMap, httpResponse); - return; - } - - wizardModel.remoteUrl = httpRequest.getParameter("remoteUrl"); - checkForEmptyValue(wizardModel.remoteUrl, errors, "install.testing.remote.url.required"); - if (errors.isEmpty()) { - //Check if the remote system is running - if (TestInstallUtil.testConnection(wizardModel.remoteUrl)) { - //Check if the test module is installed by connecting to its setting page - if (TestInstallUtil - .testConnection(wizardModel.remoteUrl.concat(RELEASE_TESTING_MODULE_PATH + "settings.htm"))) { - - wizardModel.remoteUsername = httpRequest.getParameter("username"); - wizardModel.remotePassword = httpRequest.getParameter("password"); - checkForEmptyValue(wizardModel.remoteUsername, errors, "install.testing.username.required"); - checkForEmptyValue(wizardModel.remotePassword, errors, "install.testing.password.required"); - - if (errors.isEmpty()) { - //check if the username and password are valid - try { - TestInstallUtil.getResourceInputStream( - wizardModel.remoteUrl + RELEASE_TESTING_MODULE_PATH + "verifycredentials.htm", - wizardModel.remoteUsername, wizardModel.remotePassword); - } - catch (APIAuthenticationException e) { - log.debug("Error generated: ", e); - page = TESTING_REMOTE_DETAILS_SETUP; - errors.put(ErrorMessageConstants.UPDATE_ERROR_UNABLE_AUTHENTICATE, null); - renderTemplate(page, referenceMap, httpResponse); - return; - } - - //If we have a runtime properties file, get the database setup details from it - if (skipDatabaseSetupPage()) { - Properties props = OpenmrsUtil.getRuntimeProperties(WebConstants.WEBAPP_NAME); - wizardModel.databaseConnection = props.getProperty("connection.url"); - loadedDriverString = loadDriver(wizardModel.databaseConnection, wizardModel.databaseDriver); - if (!StringUtils.hasText(loadedDriverString)) { - page = TESTING_REMOTE_DETAILS_SETUP; - errors.put(ErrorMessageConstants.ERROR_DB_DRIVER_CLASS_REQ, null); - renderTemplate(page, referenceMap, httpResponse); - return; - } - - wizardModel.databaseName = InitializationWizardModel.DEFAULT_DATABASE_NAME; - page = WIZARD_COMPLETE; - } else { - page = DATABASE_SETUP; - wizardModel.currentStepNumber = 2; - } - msgs.put("install.testing.testingModuleFound", null); - } else { - renderTemplate(page, referenceMap, httpResponse); - return; - } - } else { - errors.put("install.testing.noTestingModule", null); - } - } else { - errors.put("install.testing.invalidProductionUrl", new Object[] { wizardModel.remoteUrl }); - } - } - - renderTemplate(page, referenceMap, httpResponse); + + switch (page) { + case DEFAULT_PAGE: + handleDefaultPage(httpRequest, httpResponse, referenceMap); + break; + case INSTALL_METHOD: + handleInstallMethodPage(httpRequest, httpResponse, referenceMap, page); + break; + case SIMPLE_SETUP: + handleSimpleSetupPage(httpRequest, httpResponse, referenceMap, page); + break; + case DATABASE_SETUP: + handleDatabaseSetupPage(httpRequest, httpResponse, referenceMap, page); + break; + case DATABASE_TABLES_AND_USER: + handleDatabaseTablesAndUserPage(httpRequest, httpResponse, referenceMap, page); + break; + case OTHER_RUNTIME_PROPS: + handleOtherRuntimePropsPage(httpRequest, httpResponse, referenceMap, page); + break; + case ADMIN_USER_SETUP: + handleAdminUserSetupPage(httpRequest, httpResponse, referenceMap, page); + break; + case IMPLEMENTATION_ID_SETUP: + handleImplementationIdSetupPage(httpRequest, httpResponse, referenceMap, page); + break; + case WIZARD_COMPLETE: + handleWizardCompletePage(httpRequest, httpResponse, referenceMap, page); + break; + case TESTING_REMOTE_DETAILS_SETUP: + handleTestingRemoteDetailsSetupPage(httpRequest, httpResponse, referenceMap, page); + break; + default: + renderTemplate(DEFAULT_PAGE, referenceMap, httpResponse); } } - + private void startInstallation() { //if no one has run any installation if (!isInstallationStarted()) { @@ -827,20 +437,20 @@ private void startInstallation() { initJob.start(); } } - + private void createDemoDataTask() { if (wizardModel.addDemoData) { wizardModel.tasksToExecute.add(WizardTask.ADD_DEMO_DATA); } } - + private void createTablesTask() { if (wizardModel.createTables) { wizardModel.tasksToExecute.add(WizardTask.CREATE_TABLES); wizardModel.tasksToExecute.add(WizardTask.ADD_CORE_DATA); } } - + private void createDatabaseTask() { if (!wizardModel.hasCurrentOpenmrsDatabase) { wizardModel.tasksToExecute.add(WizardTask.CREATE_SCHEMA); @@ -849,56 +459,56 @@ private void createDatabaseTask() { wizardModel.tasksToExecute.add(WizardTask.CREATE_DB_USER); } } - + private void createSimpleSetup(String databaseRootPassword, String addDemoData) { setDatabaseNameIfInTestMode(); wizardModel.databaseConnection = Context.getRuntimeProperties().getProperty("connection.url", wizardModel.databaseConnection); - + wizardModel.createDatabaseUsername = Context.getRuntimeProperties().getProperty("connection.username", wizardModel.createDatabaseUsername); - + wizardModel.createUserUsername = wizardModel.createDatabaseUsername; - + wizardModel.databaseRootPassword = databaseRootPassword; checkForEmptyValue(wizardModel.databaseRootPassword, errors, ErrorMessageConstants.ERROR_DB_PSDW_REQ); - + wizardModel.hasCurrentOpenmrsDatabase = false; wizardModel.createTables = true; // default wizardModel.databaseName is openmrs // default wizardModel.createDatabaseUsername is root wizardModel.createDatabasePassword = wizardModel.databaseRootPassword; wizardModel.addDemoData = "yes".equals(addDemoData); - + wizardModel.hasCurrentDatabaseUser = false; wizardModel.createDatabaseUser = true; // default wizardModel.createUserUsername is root wizardModel.createUserPassword = wizardModel.databaseRootPassword; - + wizardModel.moduleWebAdmin = true; wizardModel.autoUpdateDatabase = false; - + wizardModel.adminUserPassword = InitializationWizardModel.ADMIN_DEFAULT_PASSWORD; } - + private void setDatabaseNameIfInTestMode() { if (OpenmrsUtil.isTestMode()) { wizardModel.databaseName = OpenmrsUtil.getOpenMRSVersionInTestMode(); } } - + private void autoRunOpenMRS(HttpServletRequest httpRequest) { File runtimeProperties = getRuntimePropertiesFile(); wizardModel.runtimePropertiesPath = runtimeProperties.getAbsolutePath(); - + if (!InitializationWizardModel.INSTALL_METHOD_AUTO.equals(wizardModel.installMethod)) { if (httpRequest.getParameter("database_user_name") != null) { wizardModel.createDatabaseUsername = httpRequest.getParameter("database_user_name"); } - + createSimpleSetup(httpRequest.getParameter("database_root_password"), "yes"); } - + checkLocaleAttributes(httpRequest); try { loadedDriverString = DatabaseUtil.loadDatabaseDriver(wizardModel.databaseConnection, wizardModel.databaseDriver); @@ -914,7 +524,7 @@ private void autoRunOpenMRS(HttpServletRequest httpRequest) { wizardModel.tasksToExecute.add(WizardTask.UPDATE_TO_LATEST); startInstallation(); } - + /** * This method should be called after the user has left wizard's first page (i.e. choose language). * It checks if user has changed any of locale related parameters and makes appropriate corrections @@ -951,7 +561,7 @@ private void checkLocaleAttributes(HttpServletRequest httpRequest) { } } } - + /** * It sets locale parameter for current session when user is making first GET http request to * application. It retrieves user locale from request object and checks if this locale is supported @@ -967,7 +577,7 @@ public void checkLocaleAttributesForFirstTime(HttpServletRequest httpRequest) { httpRequest.getSession().setAttribute(FilterUtil.LOCALE_ATTRIBUTE, Locale.ENGLISH.toString()); } } - + /** * Verify the database connection works. * @@ -977,7 +587,7 @@ public void checkLocaleAttributesForFirstTime(HttpServletRequest httpRequest) { * @return true/false whether it was verified or not */ private boolean verifyConnection(String connectionUsername, String connectionPassword, - String databaseConnectionFinalUrl) { + String databaseConnectionFinalUrl) { try { // verify connection //Set Database Driver using driver String @@ -986,7 +596,7 @@ private boolean verifyConnection(String connectionUsername, String connectionPas connectionPassword); tempConnection.close(); return true; - + } catch (Exception e) { errors.put("User account " + connectionUsername + " does not work. " + e.getMessage() @@ -996,7 +606,7 @@ private boolean verifyConnection(String connectionUsername, String connectionPas return false; } } - + /** * Convenience method to load the runtime properties file. * @@ -1004,19 +614,19 @@ private boolean verifyConnection(String connectionUsername, String connectionPas */ private File getRuntimePropertiesFile() { File file; - + String pathName = OpenmrsUtil.getRuntimePropertiesFilePathName(WebConstants.WEBAPP_NAME); if (pathName != null) { file = new File(pathName); } else { file = new File(OpenmrsUtil.getApplicationDataDirectory(), getRuntimePropertiesFileName()); } - + log.debug("Using file: " + file.getAbsolutePath()); - + return file; } - + private String getRuntimePropertiesFileName() { String fileName = OpenmrsUtil.getRuntimePropertiesFileNameInTestMode(); if (fileName == null) { @@ -1024,7 +634,7 @@ private String getRuntimePropertiesFileName() { } return fileName; } - + /** * @see org.openmrs.web.filter.StartupFilter#getTemplatePrefix() */ @@ -1032,7 +642,7 @@ private String getRuntimePropertiesFileName() { protected String getTemplatePrefix() { return "org/openmrs/web/filter/initialization/"; } - + /** * @see org.openmrs.web.filter.StartupFilter#getUpdateFilterModel() */ @@ -1040,7 +650,7 @@ protected String getTemplatePrefix() { protected Object getUpdateFilterModel() { return wizardModel; } - + /** * @see org.openmrs.web.filter.StartupFilter#skipFilter(HttpServletRequest) */ @@ -1051,7 +661,7 @@ public boolean skipFilter(HttpServletRequest httpRequest) { // (otherwise progress.vm is displayed "forever") return !PROGRESS_VM_AJAXREQUEST.equals(httpRequest.getParameter("page")) && !initializationRequired(); } - + /** * Public method that returns true if database+runtime properties initialization is required * @@ -1060,21 +670,21 @@ public boolean skipFilter(HttpServletRequest httpRequest) { public static boolean initializationRequired() { return !isInitializationComplete(); } - + /** * @param isInstallationStarted the value to set */ protected static synchronized void setInstallationStarted(boolean isInstallationStarted) { InitializationFilter.isInstallationStarted = isInstallationStarted; } - + /** * @return true if installation has been started */ protected static boolean isInstallationStarted() { return isInstallationStarted; } - + /** * @see javax.servlet.Filter#init(javax.servlet.FilterConfig) */ @@ -1092,49 +702,49 @@ public void init(FilterConfig filterConfig) throws ServletException { setInitializationComplete(true); } } - + private void importTestDataSet(InputStream in, String connectionUrl, String connectionUsername, - String connectionPassword) throws IOException { + String connectionPassword) throws IOException { File tempFile = null; FileOutputStream fileOut = null; try { ZipInputStream zipIn = new ZipInputStream(in); zipIn.getNextEntry(); - + tempFile = File.createTempFile("testDataSet", "dump"); fileOut = new FileOutputStream(tempFile); - + IOUtils.copy(zipIn, fileOut); - + fileOut.close(); zipIn.close(); - + //Cater for the stand-alone connection url with has :mxj: if (connectionUrl.contains(":mxj:")) { connectionUrl = connectionUrl.replace(":mxj:", ":"); } - + URI uri = URI.create(connectionUrl.substring(5)); //remove 'jdbc:' prefix to conform to the URI format String host = uri.getHost(); int port = uri.getPort(); - + TestInstallUtil.addTestData(host, port, wizardModel.databaseName, connectionUsername, connectionPassword, tempFile.getAbsolutePath()); } finally { IOUtils.closeQuietly(in); IOUtils.closeQuietly(fileOut); - + if (tempFile != null) { tempFile.delete(); } } } - + private boolean isCurrentDatabase(String database) { return wizardModel.databaseConnection.contains(database); } - + /** * @param silent if this statement fails do not display stack trace or record an error in the wizard * object. @@ -1145,12 +755,12 @@ private boolean isCurrentDatabase(String database) { * @return result of executeUpdate or -1 for error */ private int executeStatement(boolean silent, String user, String pw, String sql, String... args) { - + Connection connection = null; Statement statement = null; try { String replacedSql = sql; - + // TODO how to get the driver for the other dbs... if (isCurrentDatabase(DATABASE_MYSQL)) { Class.forName("com.mysql.cj.jdbc.Driver").newInstance(); @@ -1160,7 +770,7 @@ private int executeStatement(boolean silent, String user, String pw, String sql, } else { replacedSql = replacedSql.replaceAll("`", "\""); } - + String tempDatabaseConnection; if (sql.contains("create database")) { tempDatabaseConnection = wizardModel.databaseConnection.replace("@DBNAME@", @@ -1168,19 +778,19 @@ private int executeStatement(boolean silent, String user, String pw, String sql, } else { tempDatabaseConnection = wizardModel.databaseConnection.replace("@DBNAME@", wizardModel.databaseName); } - + connection = DriverManager.getConnection(tempDatabaseConnection, user, pw); - + for (String arg : args) { arg = arg.replace(";", "^"); // to prevent any sql injection replacedSql = replacedSql.replaceFirst("\\?", arg); } - + // run the sql statement statement = connection.createStatement(); - + return statement.executeUpdate(replacedSql); - + } catch (SQLException sqlex) { if (!silent) { @@ -1202,7 +812,7 @@ private int executeStatement(boolean silent, String user, String pw, String sql, log.warn("Error while closing statement"); } try { - + if (connection != null) { connection.close(); } @@ -1211,10 +821,10 @@ private int executeStatement(boolean silent, String user, String pw, String sql, log.warn("Error while closing connection", e); } } - + return -1; } - + /** * Convenience variable to know if this wizard has completed successfully and that this wizard does * not need to be executed again @@ -1224,7 +834,7 @@ private int executeStatement(boolean silent, String user, String pw, String sql, private static synchronized boolean isInitializationComplete() { return initializationComplete; } - + /** * Check if the given value is null or a zero-length String * @@ -1241,49 +851,49 @@ private boolean checkForEmptyValue(String value, Map errors, S errors.put(errorMessageCode, null); return false; } - + /** * Separate thread that will run through all tasks to complete the initialization. The database is * created, user's created, etc here */ private class InitializationCompletion { - + private final Future future; - + private int steps = 0; - + private String message = ""; - + private Map errors = new HashMap<>(); - + private String errorPage = null; - + private boolean erroneous = false; - + private int completedPercentage = 0; - + private WizardTask executingTask; - + private List executedTasks = new ArrayList<>(); - + public synchronized void reportError(String error, String errorPage, Object... params) { errors.put(error, params); this.errorPage = errorPage; erroneous = true; } - + public synchronized boolean hasErrors() { return erroneous; } - + public synchronized String getErrorPage() { return errorPage; } - + public synchronized Map getErrors() { return errors; } - + /** * Start the completion stage. This fires up the thread to do all the work. */ @@ -1291,7 +901,7 @@ public void start() { setStepsComplete(0); setInitializationComplete(false); } - + public void waitForCompletion() { try { future.get(); @@ -1299,45 +909,45 @@ public void waitForCompletion() { throw new RuntimeException(e); } } - + protected synchronized void setStepsComplete(int steps) { this.steps = steps; } - + protected synchronized int getStepsComplete() { return steps; } - + public synchronized String getMessage() { return message; } - + public synchronized void setMessage(String message) { this.message = message; setStepsComplete(getStepsComplete() + 1); } - + /** * @return the executingTask */ protected synchronized WizardTask getExecutingTask() { return executingTask; } - + /** * @return the completedPercentage */ protected synchronized int getCompletedPercentage() { return completedPercentage; } - + /** * @param completedPercentage the completedPercentage to set */ protected synchronized void setCompletedPercentage(int completedPercentage) { this.completedPercentage = completedPercentage; } - + /** * Adds a task that has been completed to the list of executed tasks * @@ -1346,159 +956,47 @@ protected synchronized void setCompletedPercentage(int completedPercentage) { protected synchronized void addExecutedTask(WizardTask task) { this.executedTasks.add(task); } - + /** * @param executingTask the executingTask to set */ protected synchronized void setExecutingTask(WizardTask executingTask) { this.executingTask = executingTask; } - + /** * @return the executedTasks */ protected synchronized List getExecutedTasks() { return this.executedTasks; } - + /** * This class does all the work of creating the desired database, user, updates, etc */ public InitializationCompletion() { Runnable r = new Runnable() { - + /** - * TODO split this up into multiple testable methods - * - * @see java.lang.Runnable#run() + * @see Runnable#run() */ @Override public void run() { try { - String connectionUsername; + String connectionUsername = ""; StringBuilder connectionPassword = new StringBuilder(); ChangeLogDetective changeLogDetective = ChangeLogDetective.getInstance(); ChangeLogVersionFinder changeLogVersionFinder = new ChangeLogVersionFinder(); - - if (!wizardModel.hasCurrentOpenmrsDatabase) { - setMessage("Create database"); - setExecutingTask(WizardTask.CREATE_SCHEMA); - // connect via jdbc and create a database - String sql; - if (isCurrentDatabase(DATABASE_MYSQL)) { - sql = "create database if not exists `?` default character set utf8"; - } else if (isCurrentDatabase(DATABASE_POSTGRESQL)) { - sql = "create database `?` encoding 'utf8'"; - } else if (isCurrentDatabase(DATABASE_H2)) { - sql = null; - } else { - sql = "create database `?`"; - } - - int result; - if (sql != null) { - result = executeStatement(false, wizardModel.createDatabaseUsername, - wizardModel.createDatabasePassword, sql, wizardModel.databaseName); - } else { - result = 1; - } - // throw the user back to the main screen if this error occurs - if (result < 0) { - reportError(ErrorMessageConstants.ERROR_DB_CREATE_NEW, DEFAULT_PAGE); - return; - } else { - wizardModel.workLog.add("Created database " + wizardModel.databaseName); - } - - addExecutedTask(WizardTask.CREATE_SCHEMA); - } - - if (wizardModel.createDatabaseUser) { - setMessage("Create database user"); - setExecutingTask(WizardTask.CREATE_DB_USER); - connectionUsername = wizardModel.databaseName + "_user"; - if (connectionUsername.length() > 16) { - connectionUsername = wizardModel.databaseName.substring(0, 11) - + "_user"; // trim off enough to leave space for _user at the end - } - - connectionPassword.append(""); - // generate random password from this subset of alphabet - // intentionally left out these characters: ufsb$() to prevent certain words forming randomly - String chars = "acdeghijklmnopqrtvwxyzACDEGHIJKLMNOPQRTVWXYZ0123456789.|~@#^&"; - Random r = new Random(); - StringBuilder randomStr = new StringBuilder(""); - for (int x = 0; x < 12; x++) { - randomStr.append(chars.charAt(r.nextInt(chars.length()))); - } - connectionPassword.append(randomStr); - - // connect via jdbc with root user and create an openmrs user - String host = "'%'"; - if (wizardModel.databaseConnection.contains("localhost") - || wizardModel.databaseConnection.contains("127.0.0.1")) { - host = "'localhost'"; - } - - String sql = ""; - if (isCurrentDatabase(DATABASE_MYSQL)) { - sql = "drop user '?'@" + host; - } else if (isCurrentDatabase(DATABASE_POSTGRESQL)) { - sql = "drop user `?`"; - } - - executeStatement(true, wizardModel.createUserUsername, wizardModel.createUserPassword, sql, - connectionUsername); - - if (isCurrentDatabase(DATABASE_MYSQL)) { - sql = "create user '?'@" + host + " identified by '?'"; - } else if (isCurrentDatabase(DATABASE_POSTGRESQL)) { - sql = "create user `?` with password '?'"; - } - - if (-1 != executeStatement(false, wizardModel.createUserUsername, wizardModel.createUserPassword, - sql, connectionUsername, connectionPassword.toString())) { - wizardModel.workLog.add("Created user " + connectionUsername); - } else { - // if error occurs stop - reportError(ErrorMessageConstants.ERROR_DB_CREATE_DB_USER, DEFAULT_PAGE); - return; - } - - // grant the roles - int result = 1; - if (isCurrentDatabase(DATABASE_MYSQL)) { - sql = "GRANT ALL ON `?`.* TO '?'@" + host; - result = executeStatement(false, wizardModel.createUserUsername, - wizardModel.createUserPassword, sql, wizardModel.databaseName, connectionUsername); - } else if (isCurrentDatabase(DATABASE_POSTGRESQL)) { - sql = "ALTER USER `?` WITH SUPERUSER"; - result = executeStatement(false, wizardModel.createUserUsername, - wizardModel.createUserPassword, sql, connectionUsername); - } - - // throw the user back to the main screen if this error occurs - if (result < 0) { - reportError(ErrorMessageConstants.ERROR_DB_GRANT_PRIV, DEFAULT_PAGE); - return; - } else { - wizardModel.workLog.add("Granted user " + connectionUsername + " all privileges to database " - + wizardModel.databaseName); - } - - addExecutedTask(WizardTask.CREATE_DB_USER); - } else { - connectionUsername = wizardModel.currentDatabaseUsername; - connectionPassword.setLength(0); - connectionPassword.append(wizardModel.currentDatabasePassword); - } - + + createDatabaseIfNeeded(); + createDatabaseUserIfNeeded(connectionPassword); + String finalDatabaseConnectionString = wizardModel.databaseConnection.replace("@DBNAME@", wizardModel.databaseName); - + finalDatabaseConnectionString = finalDatabaseConnectionString.replace("@APPLICATIONDATADIR@", OpenmrsUtil.getApplicationDataDirectory().replace("\\", "/")); - + // verify that the database connection works if (!verifyConnection(connectionUsername, connectionPassword.toString(), finalDatabaseConnectionString)) { @@ -1507,369 +1005,36 @@ public void run() { reportError("Unable to connect to database", DEFAULT_PAGE); return; } - + // save the properties for startup purposes - Properties runtimeProperties = new Properties(); - - runtimeProperties.put("connection.url", finalDatabaseConnectionString); - runtimeProperties.put("connection.username", connectionUsername); - runtimeProperties.put("connection.password", connectionPassword.toString()); - if (StringUtils.hasText(wizardModel.databaseDriver)) { - runtimeProperties.put("connection.driver_class", wizardModel.databaseDriver); - } - if (finalDatabaseConnectionString.contains(DATABASE_POSTGRESQL)) { - runtimeProperties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQL82Dialect"); - } - if (finalDatabaseConnectionString.contains(DATABASE_SQLSERVER)) { - runtimeProperties.put("hibernate.dialect", "org.hibernate.dialect.SQLServerDialect"); - } - if (finalDatabaseConnectionString.contains(DATABASE_H2)) { - runtimeProperties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); - } - runtimeProperties.put("module.allow_web_admin", wizardModel.moduleWebAdmin.toString()); - runtimeProperties.put("auto_update_database", wizardModel.autoUpdateDatabase.toString()); - final Encoder base64 = Base64.getEncoder(); - runtimeProperties.put(OpenmrsConstants.ENCRYPTION_VECTOR_RUNTIME_PROPERTY, - new String(base64.encode(Security.generateNewInitVector()), StandardCharsets.UTF_8)); - runtimeProperties.put(OpenmrsConstants.ENCRYPTION_KEY_RUNTIME_PROPERTY, - new String(base64.encode(Security.generateNewSecretKey()), StandardCharsets.UTF_8)); - - Properties properties = Context.getRuntimeProperties(); - properties.putAll(runtimeProperties); - runtimeProperties = properties; - Context.setRuntimeProperties(runtimeProperties); - - /** - * A callback class that prints out info about liquibase changesets - */ - class PrintingChangeSetExecutorCallback implements ChangeSetExecutorCallback { - - private int i = 1; - - private String message; - - public PrintingChangeSetExecutorCallback(String message) { - this.message = message; - } - - /** - * @see ChangeSetExecutorCallback#executing(liquibase.changelog.ChangeSet, int) - */ - @Override - public void executing(ChangeSet changeSet, int numChangeSetsToRun) { - setMessage(message + " (" + i++ + "/" + numChangeSetsToRun + "): Author: " - + changeSet.getAuthor() + " Comments: " + changeSet.getComments() + " Description: " - + changeSet.getDescription()); - float numChangeSetsToRunFloat = (float) numChangeSetsToRun; - float j = (float) i; - setCompletedPercentage(Math.round(j * 100 / numChangeSetsToRunFloat)); - } - - } - - if (wizardModel.createTables) { - // use liquibase to create core data + tables - try { - String liquibaseSchemaFileName = changeLogVersionFinder.getLatestSchemaSnapshotFilename() - .get(); - String liquibaseCoreDataFileName = changeLogVersionFinder.getLatestCoreDataSnapshotFilename() - .get(); - - setMessage("Executing " + liquibaseSchemaFileName); - setExecutingTask(WizardTask.CREATE_TABLES); - - log.debug("executing Liquibase file '{}' ", liquibaseSchemaFileName); - - DatabaseUpdater.executeChangelog(liquibaseSchemaFileName, - new PrintingChangeSetExecutorCallback("OpenMRS schema file")); - addExecutedTask(WizardTask.CREATE_TABLES); - - //reset for this task - setCompletedPercentage(0); - setExecutingTask(WizardTask.ADD_CORE_DATA); - - log.debug("executing Liquibase file '{}' ", liquibaseCoreDataFileName); - - DatabaseUpdater.executeChangelog(liquibaseCoreDataFileName, - new PrintingChangeSetExecutorCallback("OpenMRS core data file")); - wizardModel.workLog.add("Created database tables and added core data"); - addExecutedTask(WizardTask.ADD_CORE_DATA); - - } - catch (Exception e) { - reportError(ErrorMessageConstants.ERROR_DB_CREATE_TABLES_OR_ADD_DEMO_DATA, DEFAULT_PAGE, - e.getMessage()); - log.warn("Error while trying to create tables and demo data", e); - } - } - - if (wizardModel.importTestData) { - try { - setMessage("Importing test data"); - setExecutingTask(WizardTask.IMPORT_TEST_DATA); - setCompletedPercentage(0); - - try { - InputStream inData = TestInstallUtil.getResourceInputStream( - wizardModel.remoteUrl + RELEASE_TESTING_MODULE_PATH + "generateTestDataSet.form", - wizardModel.remoteUsername, wizardModel.remotePassword); - - setCompletedPercentage(40); - setMessage("Loading imported test data..."); - importTestDataSet(inData, finalDatabaseConnectionString, connectionUsername, - connectionPassword.toString()); - wizardModel.workLog.add("Imported test data"); - addExecutedTask(WizardTask.IMPORT_TEST_DATA); - - //reset the progress for the next task - setCompletedPercentage(0); - setMessage("Importing modules from remote server..."); - setExecutingTask(WizardTask.ADD_MODULES); - - InputStream inModules = TestInstallUtil.getResourceInputStream( - wizardModel.remoteUrl + RELEASE_TESTING_MODULE_PATH + "getModules.htm", - wizardModel.remoteUsername, wizardModel.remotePassword); - - setCompletedPercentage(90); - setMessage("Adding imported modules..."); - if (!TestInstallUtil.addZippedTestModules(inModules)) { - reportError(ErrorMessageConstants.ERROR_DB_UNABLE_TO_ADD_MODULES, DEFAULT_PAGE, ""); - return; - } else { - wizardModel.workLog.add("Added Modules"); - addExecutedTask(WizardTask.ADD_MODULES); - } - } - catch (APIAuthenticationException e) { - log.warn("Unable to authenticate as a User with the System Developer role"); - reportError(ErrorMessageConstants.UPDATE_ERROR_UNABLE_AUTHENTICATE, - TESTING_REMOTE_DETAILS_SETUP, ""); - return; - } - } - catch (Exception e) { - reportError(ErrorMessageConstants.ERROR_DB_IMPORT_TEST_DATA, DEFAULT_PAGE, e.getMessage()); - log.warn("Error while trying to import test data", e); - return; - } - } - + Properties runtimeProperties = saveRuntimeProperties(finalDatabaseConnectionString, connectionUsername, connectionPassword); + // use liquibase to create core data + tables + createTablesAndAddCoreData(changeLogVersionFinder); + // load import test data + importTestDataAndModules(finalDatabaseConnectionString, connectionUsername, connectionPassword); // add demo data only if creating tables fresh and user selected the option add demo data - if (wizardModel.createTables && wizardModel.addDemoData) { - try { - setMessage("Adding demo data"); - setCompletedPercentage(0); - setExecutingTask(WizardTask.ADD_DEMO_DATA); - - log.debug("executing Liquibase file '{}' ", LIQUIBASE_DEMO_DATA); - - DatabaseUpdater.executeChangelog(LIQUIBASE_DEMO_DATA, - new PrintingChangeSetExecutorCallback("OpenMRS demo patients, users, and forms")); - wizardModel.workLog.add("Added demo data"); - - addExecutedTask(WizardTask.ADD_DEMO_DATA); - } - catch (Exception e) { - reportError(ErrorMessageConstants.ERROR_DB_CREATE_TABLES_OR_ADD_DEMO_DATA, DEFAULT_PAGE, - e.getMessage()); - log.warn("Error while trying to add demo data", e); - } - } - + addDemoDataIfNeeded(); // update the database to the latest version - try { - setMessage("Updating the database to the latest version"); - setCompletedPercentage(0); - setExecutingTask(WizardTask.UPDATE_TO_LATEST); - - String version = null; - - if (wizardModel.createTables) { - version = changeLogVersionFinder.getLatestSnapshotVersion().get(); - } else { - version = changeLogDetective.getInitialLiquibaseSnapshotVersion(DatabaseUpdater.CONTEXT, - new DatabaseUpdaterLiquibaseProvider()); - } - - log.debug( - "updating the database with versions of liquibase-update-to-latest files greater than '{}'", - version); - - List changelogs = changeLogVersionFinder - .getUpdateFileNames(changeLogVersionFinder.getUpdateVersionsGreaterThan(version)); - - for (String changelog : changelogs) { - log.debug("applying Liquibase changelog '{}'", changelog); - - DatabaseUpdater.executeChangelog(changelog, - new PrintingChangeSetExecutorCallback("executing Liquibase changelog " + changelog)); - } - addExecutedTask(WizardTask.UPDATE_TO_LATEST); - } - catch (Exception e) { - reportError(ErrorMessageConstants.ERROR_DB_UPDATE_TO_LATEST, DEFAULT_PAGE, e.getMessage()); - log.warn("Error while trying to update to the latest database version", e); - return; - } - + updateDatabaseToLatestVersion(changeLogVersionFinder, changeLogDetective); + setExecutingTask(null); setMessage("Starting OpenMRS"); - + // start spring // after this point, all errors need to also call: contextLoader.closeWebApplicationContext(event.getServletContext()) // logic copied from org.springframework.web.context.ContextLoaderListener ContextLoader contextLoader = new ContextLoader(); contextLoader.initWebApplicationContext(filterConfig.getServletContext()); - + // output properties to the openmrs runtime properties file so that this wizard is not run again - FileOutputStream fos = null; - try { - fos = new FileOutputStream(getRuntimePropertiesFile()); - OpenmrsUtil.storeProperties(runtimeProperties, fos, - "Auto generated by OpenMRS initialization wizard"); - wizardModel.workLog.add("Saved runtime properties file " + getRuntimePropertiesFile()); - - /* - * Fix file readability permissions: - * first revoke read permission from everyone, then set read permissions for only the user - * there is no function to set specific readability for only one user - * and revoke everyone else's, therefore this is the only way to accomplish this. - */ - wizardModel.workLog.add("Adjusting file posix properties to user readonly"); - if (getRuntimePropertiesFile().setReadable(false, false) - && getRuntimePropertiesFile().setReadable(true)) { - wizardModel.workLog - .add("Successfully adjusted RuntimePropertiesFile to disallow world to read it"); - } else { - wizardModel.workLog - .add("Unable to adjust RuntimePropertiesFile to disallow world to read it"); - } - // don't need to catch errors here because we tested it at the beginning of the wizard - } - finally { - if (fos != null) { - fos.close(); - } - } - - Context.openSession(); - - if (!"".equals(wizardModel.implementationId)) { - try { - Context.addProxyPrivilege(PrivilegeConstants.MANAGE_GLOBAL_PROPERTIES); - Context.addProxyPrivilege(PrivilegeConstants.MANAGE_CONCEPT_SOURCES); - Context.addProxyPrivilege(PrivilegeConstants.GET_CONCEPT_SOURCES); - Context.addProxyPrivilege(PrivilegeConstants.MANAGE_IMPLEMENTATION_ID); - - ImplementationId implId = new ImplementationId(); - implId.setName(wizardModel.implementationIdName); - implId.setImplementationId(wizardModel.implementationId); - implId.setPassphrase(wizardModel.implementationIdPassPhrase); - implId.setDescription(wizardModel.implementationIdDescription); - - Context.getAdministrationService().setImplementationId(implId); - } - catch (Exception e) { - reportError(ErrorMessageConstants.ERROR_SET_INPL_ID, DEFAULT_PAGE, e.getMessage()); - log.warn("Implementation ID could not be set.", e); - Context.shutdown(); - WebModuleUtil.shutdownModules(filterConfig.getServletContext()); - contextLoader.closeWebApplicationContext(filterConfig.getServletContext()); - return; - } - finally { - Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_GLOBAL_PROPERTIES); - Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_CONCEPT_SOURCES); - Context.removeProxyPrivilege(PrivilegeConstants.GET_CONCEPT_SOURCES); - Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_IMPLEMENTATION_ID); - } - } - - try { - // change the admin user password from "test" to what they input above - if (wizardModel.createTables) { - try { - Context.authenticate(new UsernamePasswordCredentials("admin", "test")); - - Properties props = Context.getRuntimeProperties(); - String initValue = props.getProperty(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY); - props.setProperty(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY, "false"); - Context.setRuntimeProperties(props); - - Context.getUserService().changePassword("test", wizardModel.adminUserPassword); - - if (initValue == null) { - props.remove(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY); - } else { - props.setProperty(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY, initValue); - } - Context.setRuntimeProperties(props); - Context.logout(); - } - catch (ContextAuthenticationException ex) { - log.info("No need to change admin password.", ex); - } - } - } - catch (Exception e) { - Context.shutdown(); - WebModuleUtil.shutdownModules(filterConfig.getServletContext()); - contextLoader.closeWebApplicationContext(filterConfig.getServletContext()); - reportError(ErrorMessageConstants.ERROR_COMPLETE_STARTUP, DEFAULT_PAGE, e.getMessage()); - log.warn("Unable to complete the startup.", e); - return; - } - - try { - // Update PostgreSQL Sequences after insertion of core data - Context.getAdministrationService().updatePostgresSequence(); - } - catch (Exception e) { - log.warn("Not able to update PostgreSQL sequence. Startup failed for PostgreSQL", e); - reportError(ErrorMessageConstants.ERROR_COMPLETE_STARTUP, DEFAULT_PAGE, e.getMessage()); - return; - } - - // set this so that the wizard isn't run again on next page load - Context.closeSession(); - + writeRuntimePropertiesToFile(runtimeProperties); + //provide implementation details as final step + finalizeStartupConfiguration(contextLoader); + // start openmrs - try { - UpdateFilter.setUpdatesRequired(false); - WebDaemon.startOpenmrs(filterConfig.getServletContext()); - } - catch (DatabaseUpdateException updateEx) { - log.warn("Error while running the database update file", updateEx); - reportError(ErrorMessageConstants.ERROR_DB_UPDATE, DEFAULT_PAGE, updateEx.getMessage()); - return; - } - catch (InputRequiredException inputRequiredEx) { - // TODO display a page looping over the required input and ask the user for each. - // When done and the user and put in their say, call DatabaseUpdater.update(Map); - // with the user's question/answer pairs - log.warn( - "Unable to continue because user input is required for the db updates and we cannot do anything about that right now"); - reportError(ErrorMessageConstants.ERROR_INPUT_REQ, DEFAULT_PAGE); - return; - } - catch (MandatoryModuleException mandatoryModEx) { - log.warn( - "A mandatory module failed to start. Fix the error or unmark it as mandatory to continue.", - mandatoryModEx); - reportError(ErrorMessageConstants.ERROR_MANDATORY_MOD_REQ, DEFAULT_PAGE, - mandatoryModEx.getMessage()); - return; - } - catch (OpenmrsCoreModuleException coreModEx) { - log.warn( - "A core module failed to start. Make sure that all core modules (with the required minimum versions) are installed and starting properly.", - coreModEx); - reportError(ErrorMessageConstants.ERROR_CORE_MOD_REQ, DEFAULT_PAGE, coreModEx.getMessage()); - return; - } - + startOpenMRSWithErrorHandling(); // TODO catch openmrs errors here and drop the user back out to the setup screen - + } catch (IOException e) { reportError(ErrorMessageConstants.ERROR_COMPLETE_STARTUP, DEFAULT_PAGE, e.getMessage()); @@ -1886,11 +1051,11 @@ && getRuntimePropertiesFile().setReadable(true)) { } } }; - + future = OpenmrsThreadPoolHolder.threadExecutor.submit(() -> { r.run(); return null; }); } } - + /** * Convenience method that loads the database driver * @@ -1909,10 +1074,10 @@ public static String loadDriver(String connection, String databaseDriver) { + "Please ensure that the database driver jar file is on the class path " + "(like in the webapp's lib folder)"); } - + return loadedDriverString; } - + /** * Utility method that checks if there is a runtime properties file containing database connection * credentials @@ -1925,7 +1090,7 @@ private static boolean skipDatabaseSetupPage() { && StringUtils.hasText(props.getProperty("connection.username")) && StringUtils.hasText(props.getProperty("connection.password"))); } - + /** * Utility methods that checks if the user clicked the back image * @@ -1936,7 +1101,7 @@ private static boolean goBack(HttpServletRequest httpRequest) { return "Back".equals(httpRequest.getParameter("back")) || (httpRequest.getParameter("back.x") != null && httpRequest.getParameter("back.y") != null); } - + /** * Convenience method to get custom installation script * @@ -1945,7 +1110,7 @@ private static boolean goBack(HttpServletRequest httpRequest) { */ private Properties getInstallationScript() { Properties prop = new Properties(); - + String fileName = System.getProperty("OPENMRS_INSTALLATION_SCRIPT"); if (fileName == null) { return prop; @@ -1957,7 +1122,7 @@ private Properties getInstallationScript() { input = getClass().getClassLoader().getResourceAsStream(fileName); prop.load(input); log.info("Using installation script from classpath: " + fileName); - + input.close(); } catch (IOException ex) { @@ -1975,7 +1140,7 @@ private Properties getInstallationScript() { input = new FileInputStream(fileName); prop.load(input); log.info("Using installation script from absolute path: " + file.getAbsolutePath()); - + input.close(); } catch (IOException ex) { @@ -1989,4 +1154,942 @@ private Properties getInstallationScript() { } return prop; } + + private void createDatabaseIfNeeded() { + if (!wizardModel.hasCurrentOpenmrsDatabase) { + initJob.setMessage("Create database"); + initJob.setExecutingTask(WizardTask.CREATE_SCHEMA); + + String sql = getCreateDatabaseSql(); + int result; + + if (sql != null) { + result = executeStatement(false, + wizardModel.createDatabaseUsername, + wizardModel.createDatabasePassword, + sql, + wizardModel.databaseName); + } else { + result = 1; // Assume success for databases like H2 that don't need explicit creation + } + + if (result < 0) { + initJob.reportError(ErrorMessageConstants.ERROR_DB_CREATE_NEW, DEFAULT_PAGE); + return; + } + + wizardModel.workLog.add("Created database " + wizardModel.databaseName); + initJob.addExecutedTask(WizardTask.CREATE_SCHEMA); + } + } + + private void createDatabaseUserIfNeeded(StringBuilder connectionPassword) { + String connectionUsername; + if (wizardModel.createDatabaseUser) { + initJob.setMessage("Create database user"); + initJob.setExecutingTask(WizardTask.CREATE_DB_USER); + + connectionUsername = generateUsername(wizardModel.databaseName); + String password = generateRandomPassword(); + connectionPassword.setLength(0); + connectionPassword.append(password); + + String host = (wizardModel.databaseConnection.contains("localhost") || wizardModel.databaseConnection.contains("127.0.0.1")) + ? "'localhost'" : "'%'"; + + dropExistingUser(host, connectionUsername); + if (!createUser(host, password, connectionUsername)) { + initJob.reportError(ErrorMessageConstants.ERROR_DB_CREATE_DB_USER, DEFAULT_PAGE); + return; + } + + wizardModel.workLog.add("Created user " + connectionUsername); + + if (!grantPrivileges(host, connectionUsername)) { + initJob.reportError(ErrorMessageConstants.ERROR_DB_GRANT_PRIV, DEFAULT_PAGE); + return; + } + + wizardModel.workLog.add("Granted user " + connectionUsername + " all privileges to database " + wizardModel.databaseName); + initJob.addExecutedTask(WizardTask.CREATE_DB_USER); + } else { + connectionUsername = wizardModel.currentDatabaseUsername; + connectionPassword.setLength(0); + connectionPassword.append(wizardModel.currentDatabasePassword); + } + } + + private Properties saveRuntimeProperties(String finalDatabaseConnectionString, String connectionUsername, StringBuilder connectionPassword) { + Properties runtimeProperties = new Properties(); + + runtimeProperties.put("connection.url", finalDatabaseConnectionString); + runtimeProperties.put("connection.username", connectionUsername); + runtimeProperties.put("connection.password", connectionPassword.toString()); + + if (StringUtils.hasText(wizardModel.databaseDriver)) { + runtimeProperties.put("connection.driver_class", wizardModel.databaseDriver); + } + + setHibernateDialect(finalDatabaseConnectionString, runtimeProperties); + + runtimeProperties.put("module.allow_web_admin", wizardModel.moduleWebAdmin.toString()); + runtimeProperties.put("auto_update_database", wizardModel.autoUpdateDatabase.toString()); + + Encoder base64 = Base64.getEncoder(); + runtimeProperties.put(OpenmrsConstants.ENCRYPTION_VECTOR_RUNTIME_PROPERTY, + encodeToBase64(Security.generateNewInitVector(), base64)); + runtimeProperties.put(OpenmrsConstants.ENCRYPTION_KEY_RUNTIME_PROPERTY, + encodeToBase64(Security.generateNewSecretKey(), base64)); + + // Merge with existing runtime properties + Properties mergedProperties = Context.getRuntimeProperties(); + mergedProperties.putAll(runtimeProperties); + Context.setRuntimeProperties(mergedProperties); + return runtimeProperties; + } + + private void createTablesAndAddCoreData(ChangeLogVersionFinder changeLogVersionFinder) { + if (!wizardModel.createTables) { + return; + } + + try { + String liquibaseSchemaFileName = changeLogVersionFinder.getLatestSchemaSnapshotFilename().get(); + String liquibaseCoreDataFileName = changeLogVersionFinder.getLatestCoreDataSnapshotFilename().get(); + + // Create tables + initJob.setMessage("Executing " + liquibaseSchemaFileName); + initJob.setExecutingTask(WizardTask.CREATE_TABLES); + log.debug("Executing Liquibase file '{}'", liquibaseSchemaFileName); + DatabaseUpdater.executeChangelog( + liquibaseSchemaFileName, + new PrintingChangeSetExecutorCallback("OpenMRS schema file") + ); + initJob.addExecutedTask(WizardTask.CREATE_TABLES); + + // Add core data + initJob.setCompletedPercentage(0); // reset progress + initJob.setExecutingTask(WizardTask.ADD_CORE_DATA); + log.debug("Executing Liquibase file '{}'", liquibaseCoreDataFileName); + DatabaseUpdater.executeChangelog( + liquibaseCoreDataFileName, + new PrintingChangeSetExecutorCallback("OpenMRS core data file") + ); + + wizardModel.workLog.add("Created database tables and added core data"); + initJob.addExecutedTask(WizardTask.ADD_CORE_DATA); + } catch (Exception e) { + initJob.reportError( + ErrorMessageConstants.ERROR_DB_CREATE_TABLES_OR_ADD_DEMO_DATA, + DEFAULT_PAGE, + e.getMessage() + ); + log.warn("Error while trying to create tables and demo data", e); + } + } + + private void importTestDataAndModules(String finalDatabaseConnectionString, String connectionUsername, StringBuilder connectionPassword) { + if (!wizardModel.importTestData) { + return; + } + + try { + initJob.setMessage("Importing test data"); + initJob.setExecutingTask(WizardTask.IMPORT_TEST_DATA); + initJob.setCompletedPercentage(0); + + try { + InputStream inData = TestInstallUtil.getResourceInputStream( + wizardModel.remoteUrl + RELEASE_TESTING_MODULE_PATH + "generateTestDataSet.form", + wizardModel.remoteUsername, + wizardModel.remotePassword + ); + + initJob.setCompletedPercentage(40); + initJob.setMessage("Loading imported test data..."); + importTestDataSet(inData, finalDatabaseConnectionString, connectionUsername, connectionPassword.toString()); + wizardModel.workLog.add("Imported test data"); + initJob.addExecutedTask(WizardTask.IMPORT_TEST_DATA); + + // Reset progress and move to next task + initJob.setCompletedPercentage(0); + initJob.setMessage("Importing modules from remote server..."); + initJob.setExecutingTask(WizardTask.ADD_MODULES); + + InputStream inModules = TestInstallUtil.getResourceInputStream( + wizardModel.remoteUrl + RELEASE_TESTING_MODULE_PATH + "getModules.htm", + wizardModel.remoteUsername, + wizardModel.remotePassword + ); + + initJob.setCompletedPercentage(90); + initJob.setMessage("Adding imported modules..."); + if (!TestInstallUtil.addZippedTestModules(inModules)) { + initJob.reportError(ErrorMessageConstants.ERROR_DB_UNABLE_TO_ADD_MODULES, DEFAULT_PAGE, ""); + return; + } else { + wizardModel.workLog.add("Added Modules"); + initJob.addExecutedTask(WizardTask.ADD_MODULES); + } + } catch (APIAuthenticationException e) { + log.warn("Unable to authenticate as a User with the System Developer role"); + initJob.reportError(ErrorMessageConstants.UPDATE_ERROR_UNABLE_AUTHENTICATE, TESTING_REMOTE_DETAILS_SETUP, ""); + return; + } + + } catch (Exception e) { + initJob.reportError(ErrorMessageConstants.ERROR_DB_IMPORT_TEST_DATA, DEFAULT_PAGE, e.getMessage()); + log.warn("Error while trying to import test data", e); + } + } + + private void addDemoDataIfNeeded() { + if (wizardModel.createTables && wizardModel.addDemoData) { + try { + initJob.setMessage("Adding demo data"); + initJob.setCompletedPercentage(0); + initJob.setExecutingTask(WizardTask.ADD_DEMO_DATA); + + log.debug("executing Liquibase file '{}' ", LIQUIBASE_DEMO_DATA); + + DatabaseUpdater.executeChangelog( + LIQUIBASE_DEMO_DATA, + new PrintingChangeSetExecutorCallback("OpenMRS demo patients, users, and forms") + ); + wizardModel.workLog.add("Added demo data"); + + initJob.addExecutedTask(WizardTask.ADD_DEMO_DATA); + } catch (Exception e) { + initJob.reportError(ErrorMessageConstants.ERROR_DB_CREATE_TABLES_OR_ADD_DEMO_DATA, DEFAULT_PAGE, e.getMessage()); + log.warn("Error while trying to add demo data", e); + } + } + } + + private void updateDatabaseToLatestVersion(ChangeLogVersionFinder changeLogVersionFinder, ChangeLogDetective changeLogDetective) { + // Update the database to the latest version + try { + initJob.setMessage("Updating the database to the latest version"); + initJob.setCompletedPercentage(0); + initJob.setExecutingTask(WizardTask.UPDATE_TO_LATEST); + + String version = null; + + if (wizardModel.createTables) { + version = changeLogVersionFinder.getLatestSnapshotVersion().get(); + } else { + version = changeLogDetective.getInitialLiquibaseSnapshotVersion( + DatabaseUpdater.CONTEXT, + new DatabaseUpdaterLiquibaseProvider() + ); + } + + log.debug("updating the database with versions of liquibase-update-to-latest files greater than '{}'", version); + + List changelogs = changeLogVersionFinder.getUpdateFileNames( + changeLogVersionFinder.getUpdateVersionsGreaterThan(version) + ); + + for (String changelog : changelogs) { + log.debug("applying Liquibase changelog '{}'", changelog); + + DatabaseUpdater.executeChangelog( + changelog, + new PrintingChangeSetExecutorCallback("executing Liquibase changelog " + changelog) + ); + } + + initJob.addExecutedTask(WizardTask.UPDATE_TO_LATEST); + } catch (Exception e) { + initJob.reportError(ErrorMessageConstants.ERROR_DB_UPDATE_TO_LATEST, DEFAULT_PAGE, e.getMessage()); + log.warn("Error while trying to update to the latest database version", e); + } + } + + private void writeRuntimePropertiesToFile(Properties runtimeProperties) throws IOException { + try (FileOutputStream fos = new FileOutputStream(getRuntimePropertiesFile())) { + OpenmrsUtil.storeProperties(runtimeProperties, fos, "Auto generated by OpenMRS initialization wizard"); + wizardModel.workLog.add("Saved runtime properties file " + getRuntimePropertiesFile()); + + /* + * Fix file readability permissions: + * first revoke read permission from everyone, then set read permissions for only the user + * there is no function to set specific readability for only one user + * and revoke everyone else's, therefore this is the only way to accomplish this. + */ + wizardModel.workLog.add("Adjusting file posix properties to user readonly"); + if (getRuntimePropertiesFile().setReadable(false, false) && + getRuntimePropertiesFile().setReadable(true)) { + wizardModel.workLog.add("Successfully adjusted RuntimePropertiesFile to disallow world to read it"); + } else { + wizardModel.workLog.add("Unable to adjust RuntimePropertiesFile to disallow world to read it"); + } + } + } + + private void finalizeStartupConfiguration(ContextLoader contextLoader) { + Context.openSession(); + + if (!"".equals(wizardModel.implementationId)) { + try { + Context.addProxyPrivilege(PrivilegeConstants.MANAGE_GLOBAL_PROPERTIES); + Context.addProxyPrivilege(PrivilegeConstants.MANAGE_CONCEPT_SOURCES); + Context.addProxyPrivilege(PrivilegeConstants.GET_CONCEPT_SOURCES); + Context.addProxyPrivilege(PrivilegeConstants.MANAGE_IMPLEMENTATION_ID); + + ImplementationId implId = new ImplementationId(); + implId.setName(wizardModel.implementationIdName); + implId.setImplementationId(wizardModel.implementationId); + implId.setPassphrase(wizardModel.implementationIdPassPhrase); + implId.setDescription(wizardModel.implementationIdDescription); + + Context.getAdministrationService().setImplementationId(implId); + } catch (Exception e) { + initJob.reportError(ErrorMessageConstants.ERROR_SET_INPL_ID, DEFAULT_PAGE, e.getMessage()); + log.warn("Implementation ID could not be set.", e); + Context.shutdown(); + WebModuleUtil.shutdownModules(filterConfig.getServletContext()); + contextLoader.closeWebApplicationContext(filterConfig.getServletContext()); + return; + } finally { + Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_GLOBAL_PROPERTIES); + Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_CONCEPT_SOURCES); + Context.removeProxyPrivilege(PrivilegeConstants.GET_CONCEPT_SOURCES); + Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_IMPLEMENTATION_ID); + } + } + + try { + if (wizardModel.createTables) { + try { + Context.authenticate(new UsernamePasswordCredentials("admin", "test")); + + Properties props = Context.getRuntimeProperties(); + String initValue = props.getProperty(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY); + props.setProperty(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY, "false"); + Context.setRuntimeProperties(props); + + Context.getUserService().changePassword("test", wizardModel.adminUserPassword); + + if (initValue == null) { + props.remove(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY); + } else { + props.setProperty(UserService.ADMIN_PASSWORD_LOCKED_PROPERTY, initValue); + } + Context.setRuntimeProperties(props); + Context.logout(); + } catch (ContextAuthenticationException ex) { + log.info("No need to change admin password.", ex); + } + } + } catch (Exception e) { + Context.shutdown(); + WebModuleUtil.shutdownModules(filterConfig.getServletContext()); + contextLoader.closeWebApplicationContext(filterConfig.getServletContext()); + initJob.reportError(ErrorMessageConstants.ERROR_COMPLETE_STARTUP, DEFAULT_PAGE, e.getMessage()); + log.warn("Unable to complete the startup.", e); + return; + } + + try { + Context.getAdministrationService().updatePostgresSequence(); + } catch (Exception e) { + log.warn("Not able to update PostgreSQL sequence. Startup failed for PostgreSQL", e); + initJob.reportError(ErrorMessageConstants.ERROR_COMPLETE_STARTUP, DEFAULT_PAGE, e.getMessage()); + return; + } + + Context.closeSession(); + } + + private void startOpenMRSWithErrorHandling() { + try { + UpdateFilter.setUpdatesRequired(false); + WebDaemon.startOpenmrs(filterConfig.getServletContext()); + } catch (DatabaseUpdateException updateEx) { + log.warn("Error while running the database update file", updateEx); + initJob.reportError(ErrorMessageConstants.ERROR_DB_UPDATE, DEFAULT_PAGE, updateEx.getMessage()); + return; + } catch (InputRequiredException inputRequiredEx) { + // TODO display a page looping over the required input and ask the user for each. + // When done and the user and put in their say, call DatabaseUpdater.update(Map); + // with the user's question/answer pairs + log.warn("Unable to continue because user input is required for the db updates and we cannot do anything about that right now"); + initJob.reportError(ErrorMessageConstants.ERROR_INPUT_REQ, DEFAULT_PAGE); + return; + } catch (MandatoryModuleException mandatoryModEx) { + log.warn("A mandatory module failed to start. Fix the error or unmark it as mandatory to continue.", mandatoryModEx); + initJob.reportError(ErrorMessageConstants.ERROR_MANDATORY_MOD_REQ, DEFAULT_PAGE, mandatoryModEx.getMessage()); + return; + } catch (OpenmrsCoreModuleException coreModEx) { + log.warn("A core module failed to start. Make sure that all core modules (with the required minimum versions) are installed and starting properly.", coreModEx); + initJob.reportError(ErrorMessageConstants.ERROR_CORE_MOD_REQ, DEFAULT_PAGE, coreModEx.getMessage()); + return; + } + } + + /** + * A callback class that prints out info about liquibase changesets + */ + class PrintingChangeSetExecutorCallback implements ChangeSetExecutorCallback { + + private int i = 1; + + private String message; + + public PrintingChangeSetExecutorCallback(String message) { + this.message = message; + } + + /** + * @see ChangeSetExecutorCallback#executing(ChangeSet, int) + */ + @Override + public void executing(ChangeSet changeSet, int numChangeSetsToRun) { + initJob.setMessage(message + " (" + i++ + "/" + numChangeSetsToRun + "): Author: " + + changeSet.getAuthor() + " Comments: " + changeSet.getComments() + " Description: " + + changeSet.getDescription()); + float numChangeSetsToRunFloat = (float) numChangeSetsToRun; + float j = (float) i; + initJob.setCompletedPercentage(Math.round(j * 100 / numChangeSetsToRunFloat)); + } + } + + private void setHibernateDialect(String connectionString, Properties props) { + if (connectionString.contains(DATABASE_POSTGRESQL)) { + props.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQL82Dialect"); + } else if (connectionString.contains(DATABASE_SQLSERVER)) { + props.put("hibernate.dialect", "org.hibernate.dialect.SQLServerDialect"); + } else if (connectionString.contains(DATABASE_H2)) { + props.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); + } + } + + private String encodeToBase64(byte[] bytes, Encoder base64) { + return new String(base64.encode(bytes), StandardCharsets.UTF_8); + } + + private String generateUsername(String baseName) { + String username = baseName + "_user"; + return (username.length() > 16) ? baseName.substring(0, 11) + "_user" : username; + } + + private String generateRandomPassword() { + String chars = "acdeghijklmnopqrtvwxyzACDEGHIJKLMNOPQRTVWXYZ0123456789.|~@#^&"; + Random r = new Random(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 12; i++) { + sb.append(chars.charAt(r.nextInt(chars.length()))); + } + return sb.toString(); + } + + private void dropExistingUser(String host, String connectionUsername) { + String sql; + if (isCurrentDatabase(DATABASE_MYSQL)) { + sql = "drop user '?'@" + host; + } else if (isCurrentDatabase(DATABASE_POSTGRESQL)) { + sql = "drop user `?`"; + } else { + return; + } + + executeStatement(true, wizardModel.createUserUsername, wizardModel.createUserPassword, sql, connectionUsername); + } + + private boolean createUser(String host, String password, String connectionUsername) { + String sql; + if (isCurrentDatabase(DATABASE_MYSQL)) { + sql = "create user '?'@" + host + " identified by '?'"; + } else if (isCurrentDatabase(DATABASE_POSTGRESQL)) { + sql = "create user `?` with password '?'"; + } else { + return false; + } + + return executeStatement(false, wizardModel.createUserUsername, wizardModel.createUserPassword, sql, + connectionUsername, password) != -1; + } + + private boolean grantPrivileges(String host, String connectionUsername) { + String sql; + int result = 1; + + if (isCurrentDatabase(DATABASE_MYSQL)) { + sql = "GRANT ALL ON `?`.* TO '?'@" + host; + result = executeStatement(false, wizardModel.createUserUsername, wizardModel.createUserPassword, + sql, wizardModel.databaseName, connectionUsername); + } else if (isCurrentDatabase(DATABASE_POSTGRESQL)) { + sql = "ALTER USER `?` WITH SUPERUSER"; + result = executeStatement(false, wizardModel.createUserUsername, wizardModel.createUserPassword, + sql, connectionUsername); + } + + return result >= 0; + } + + private String getCreateDatabaseSql() { + if (isCurrentDatabase(DATABASE_MYSQL)) { + return "create database if not exists `?` default character set utf8"; + } else if (isCurrentDatabase(DATABASE_POSTGRESQL)) { + return "create database `?` encoding 'utf8'"; + } else if (isCurrentDatabase(DATABASE_H2)) { + return null; + } else { + return "create database `?`"; + } + } + + /** + * Handles the default page (chooselang.vm) logic + */ + private void handleDefaultPage(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Map referenceMap) throws IOException { + // get props and render the first page + File runtimeProperties = getRuntimePropertiesFile(); + if (!runtimeProperties.exists()) { + try { + runtimeProperties.createNewFile(); + // reset the error objects in case of refresh + wizardModel.canCreate = true; + wizardModel.cannotCreateErrorMessage = ""; + } + catch (IOException io) { + wizardModel.canCreate = false; + wizardModel.cannotCreateErrorMessage = io.getMessage(); + } + // check this before deleting the file again + wizardModel.canWrite = runtimeProperties.canWrite(); + + // delete the file again after testing the create/write + // so that if the user stops the webapp before finishing + // this wizard, they can still get back into it + runtimeProperties.delete(); + } else { + wizardModel.canWrite = runtimeProperties.canWrite(); + + wizardModel.databaseConnection = Context.getRuntimeProperties().getProperty("connection.url", + wizardModel.databaseConnection); + + wizardModel.currentDatabaseUsername = Context.getRuntimeProperties().getProperty("connection.username", + wizardModel.currentDatabaseUsername); + + wizardModel.currentDatabasePassword = Context.getRuntimeProperties().getProperty("connection.password", + wizardModel.currentDatabasePassword); + } + + wizardModel.runtimePropertiesPath = runtimeProperties.getAbsolutePath(); + + checkLocaleAttributes(httpRequest); + referenceMap.put(FilterUtil.LOCALE_ATTRIBUTE, + httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); + log.info("Locale stored in session is {}", httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); + + httpResponse.setContentType("text/html"); + // otherwise do step one of the wizard + renderTemplate(INSTALL_METHOD, referenceMap, httpResponse); + } + + /** + * Handles the installation method page (installmethod.vm) logic + */ + private void handleInstallMethodPage(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Map referenceMap, String page) throws IOException { + if (goBack(httpRequest)) { + referenceMap.put(FilterUtil.REMEMBER_ATTRIBUTE, + httpRequest.getSession().getAttribute(FilterUtil.REMEMBER_ATTRIBUTE) != null); + referenceMap.put(FilterUtil.LOCALE_ATTRIBUTE, + httpRequest.getSession().getAttribute(FilterUtil.LOCALE_ATTRIBUTE)); + renderTemplate(CHOOSE_LANG, referenceMap, httpResponse); + return; + } + wizardModel.installMethod = httpRequest.getParameter("install_method"); + if (InitializationWizardModel.INSTALL_METHOD_SIMPLE.equals(wizardModel.installMethod)) { + page = SIMPLE_SETUP; + } else if (InitializationWizardModel.INSTALL_METHOD_TESTING.equals(wizardModel.installMethod)) { + page = TESTING_REMOTE_DETAILS_SETUP; + wizardModel.currentStepNumber = 1; + wizardModel.numberOfSteps = skipDatabaseSetupPage() ? 1 : 3; + } else { + page = DATABASE_SETUP; + wizardModel.currentStepNumber = 1; + wizardModel.numberOfSteps = 5; + } + renderTemplate(page, referenceMap, httpResponse); + } + + /** + * Handles the simple setup page (simplesetup.vm) logic + */ + private void handleSimpleSetupPage(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Map referenceMap, String page) throws IOException { + if (goBack(httpRequest)) { + renderTemplate(INSTALL_METHOD, referenceMap, httpResponse); + return; + } + wizardModel.databaseConnection = httpRequest.getParameter("database_connection"); + ; + + wizardModel.createDatabaseUsername = Context.getRuntimeProperties().getProperty("connection.username", + wizardModel.createDatabaseUsername); + + wizardModel.createUserUsername = wizardModel.createDatabaseUsername; + + wizardModel.databaseRootPassword = httpRequest.getParameter("database_root_password"); + checkForEmptyValue(wizardModel.databaseRootPassword, errors, ErrorMessageConstants.ERROR_DB_PSDW_REQ); + + wizardModel.hasCurrentOpenmrsDatabase = false; + wizardModel.createTables = true; + // default wizardModel.databaseName is openmrs + // default wizardModel.createDatabaseUsername is root + wizardModel.createDatabasePassword = wizardModel.databaseRootPassword; + wizardModel.addDemoData = "yes".equals(httpRequest.getParameter("add_demo_data")); + + wizardModel.hasCurrentDatabaseUser = false; + wizardModel.createDatabaseUser = true; + // default wizardModel.createUserUsername is root + wizardModel.createUserPassword = wizardModel.databaseRootPassword; + + wizardModel.moduleWebAdmin = true; + wizardModel.autoUpdateDatabase = false; + + wizardModel.adminUserPassword = InitializationWizardModel.ADMIN_DEFAULT_PASSWORD; + + createSimpleSetup(httpRequest.getParameter("database_root_password"), httpRequest.getParameter("add_demo_data")); + + try { + loadedDriverString = DatabaseUtil.loadDatabaseDriver(wizardModel.databaseConnection, + wizardModel.databaseDriver); + } + catch (ClassNotFoundException e) { + errors.put(ErrorMessageConstants.ERROR_DB_DRIVER_CLASS_REQ, null); + renderTemplate(page, referenceMap, httpResponse); + return; + } + + if (errors.isEmpty()) { + page = WIZARD_COMPLETE; + } + renderTemplate(page, referenceMap, httpResponse); + } + + /** + * Handles the database setup page (databasesetup.vm) logic + */ + private void handleDatabaseSetupPage(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Map referenceMap, String page) throws IOException { + if (goBack(httpRequest)) { + wizardModel.currentStepNumber -= 1; + if (InitializationWizardModel.INSTALL_METHOD_TESTING.equals(wizardModel.installMethod)) { + renderTemplate(TESTING_REMOTE_DETAILS_SETUP, referenceMap, httpResponse); + } else { + renderTemplate(INSTALL_METHOD, referenceMap, httpResponse); + } + return; + } + + wizardModel.databaseConnection = httpRequest.getParameter("database_connection"); + checkForEmptyValue(wizardModel.databaseConnection, errors, ErrorMessageConstants.ERROR_DB_CONN_REQ); + + wizardModel.databaseDriver = httpRequest.getParameter("database_driver"); + checkForEmptyValue(wizardModel.databaseConnection, errors, ErrorMessageConstants.ERROR_DB_DRIVER_REQ); + + loadedDriverString = loadDriver(wizardModel.databaseConnection, wizardModel.databaseDriver); + if (!StringUtils.hasText(loadedDriverString)) { + errors.put(ErrorMessageConstants.ERROR_DB_DRIVER_CLASS_REQ, null); + renderTemplate(DATABASE_SETUP, referenceMap, httpResponse); + return; + } + + // asked the user for their desired database name + if ("yes".equals(httpRequest.getParameter("current_openmrs_database"))) { + wizardModel.databaseName = httpRequest.getParameter("openmrs_current_database_name"); + checkForEmptyValue(wizardModel.databaseName, errors, ErrorMessageConstants.ERROR_DB_CURR_NAME_REQ); + wizardModel.hasCurrentOpenmrsDatabase = true; + // TODO check to see if this is an active database + } else { + // mark this wizard as a "to create database" (done at the end) + wizardModel.hasCurrentOpenmrsDatabase = false; + wizardModel.createTables = true; + wizardModel.databaseName = httpRequest.getParameter("openmrs_new_database_name"); + checkForEmptyValue(wizardModel.databaseName, errors, ErrorMessageConstants.ERROR_DB_NEW_NAME_REQ); + // TODO create database now to check if its possible? + wizardModel.createDatabaseUsername = httpRequest.getParameter("create_database_username"); + checkForEmptyValue(wizardModel.createDatabaseUsername, errors, ErrorMessageConstants.ERROR_DB_USER_NAME_REQ); + wizardModel.createDatabasePassword = httpRequest.getParameter("create_database_password"); + checkForEmptyValue(wizardModel.createDatabasePassword, errors, ErrorMessageConstants.ERROR_DB_USER_PSWD_REQ); + } + + if (errors.isEmpty()) { + page = DATABASE_TABLES_AND_USER; + + if (InitializationWizardModel.INSTALL_METHOD_TESTING.equals(wizardModel.installMethod)) { + wizardModel.currentStepNumber = 3; + } else { + wizardModel.currentStepNumber = 2; + } + } + + renderTemplate(page, referenceMap, httpResponse); + } + + /** + * Handles the database tables and user page (databasetablesanduser.vm) logic + */ + private void handleDatabaseTablesAndUserPage(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Map referenceMap, String page) throws IOException { + if (goBack(httpRequest)) { + wizardModel.currentStepNumber -= 1; + renderTemplate(DATABASE_SETUP, referenceMap, httpResponse); + return; + } + + if (wizardModel.hasCurrentOpenmrsDatabase) { + wizardModel.createTables = "yes".equals(httpRequest.getParameter("create_tables")); + } + + wizardModel.addDemoData = "yes".equals(httpRequest.getParameter("add_demo_data")); + + if ("yes".equals(httpRequest.getParameter("current_database_user"))) { + wizardModel.currentDatabaseUsername = httpRequest.getParameter("current_database_username"); + checkForEmptyValue(wizardModel.currentDatabaseUsername, errors, + ErrorMessageConstants.ERROR_DB_CUR_USER_NAME_REQ); + wizardModel.currentDatabasePassword = httpRequest.getParameter("current_database_password"); + checkForEmptyValue(wizardModel.currentDatabasePassword, errors, + ErrorMessageConstants.ERROR_DB_CUR_USER_PSWD_REQ); + wizardModel.hasCurrentDatabaseUser = true; + wizardModel.createDatabaseUser = false; + } else { + wizardModel.hasCurrentDatabaseUser = false; + wizardModel.createDatabaseUser = true; + // asked for the root mysql username/password + wizardModel.createUserUsername = httpRequest.getParameter("create_user_username"); + checkForEmptyValue(wizardModel.createUserUsername, errors, ErrorMessageConstants.ERROR_DB_USER_NAME_REQ); + wizardModel.createUserPassword = httpRequest.getParameter("create_user_password"); + checkForEmptyValue(wizardModel.createUserPassword, errors, ErrorMessageConstants.ERROR_DB_USER_PSWD_REQ); + } + + if (errors.isEmpty()) { // go to next page + page = InitializationWizardModel.INSTALL_METHOD_TESTING.equals(wizardModel.installMethod) ? WIZARD_COMPLETE : OTHER_RUNTIME_PROPS; + } + + renderTemplate(page, referenceMap, httpResponse); + } + + /** + * Handles the other runtime properties page (otherruntimeproperties.vm) logic + */ + private void handleOtherRuntimePropsPage(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Map referenceMap, String page) throws IOException { + if (goBack(httpRequest)) { + renderTemplate(DATABASE_TABLES_AND_USER, referenceMap, httpResponse); + return; + } + + wizardModel.moduleWebAdmin = "yes".equals(httpRequest.getParameter("module_web_admin")); + wizardModel.autoUpdateDatabase = "yes".equals(httpRequest.getParameter("auto_update_database")); + + if (wizardModel.createTables) { // go to next page if they are creating tables + page = ADMIN_USER_SETUP; + } else { // skip a page + page = IMPLEMENTATION_ID_SETUP; + } + + renderTemplate(page, referenceMap, httpResponse); + } + + /** + * Handles the admin user setup page (adminusersetup.vm) logic + */ + private void handleAdminUserSetupPage(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Map referenceMap, String page) throws IOException { + if (goBack(httpRequest)) { + renderTemplate(OTHER_RUNTIME_PROPS, referenceMap, httpResponse); + return; + } + + wizardModel.adminUserPassword = httpRequest.getParameter("new_admin_password"); + String adminUserConfirm = httpRequest.getParameter("new_admin_password_confirm"); + + // throw back to admin user if passwords don't match + if (!wizardModel.adminUserPassword.equals(adminUserConfirm)) { + errors.put(ErrorMessageConstants.ERROR_DB_ADM_PSWDS_MATCH, null); + renderTemplate(ADMIN_USER_SETUP, referenceMap, httpResponse); + return; + } + + // throw back if the user didn't put in a password + if ("".equals(wizardModel.adminUserPassword)) { + errors.put(ErrorMessageConstants.ERROR_DB_ADM_PSDW_EMPTY, null); + renderTemplate(ADMIN_USER_SETUP, referenceMap, httpResponse); + return; + } + + try { + OpenmrsUtil.validatePassword("admin", wizardModel.adminUserPassword, "admin"); + } + catch (PasswordException p) { + errors.put(ErrorMessageConstants.ERROR_DB_ADM_PSDW_WEAK, null); + renderTemplate(ADMIN_USER_SETUP, referenceMap, httpResponse); + return; + } + + if (errors.isEmpty()) { // go to next page + page = IMPLEMENTATION_ID_SETUP; + } + + renderTemplate(page, referenceMap, httpResponse); + } + + /** + * Handles the testing remote details setup page (remotedetails.vm) logic + */ + private void handleTestingRemoteDetailsSetupPage(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Map referenceMap, String page) throws IOException { + if (goBack(httpRequest)) { + wizardModel.currentStepNumber -= 1; + renderTemplate(INSTALL_METHOD, referenceMap, httpResponse); + return; + } + + wizardModel.remoteUrl = httpRequest.getParameter("remoteUrl"); + checkForEmptyValue(wizardModel.remoteUrl, errors, "install.testing.remote.url.required"); + if (errors.isEmpty()) { + //Check if the remote system is running + if (TestInstallUtil.testConnection(wizardModel.remoteUrl)) { + //Check if the test module is installed by connecting to its setting page + if (TestInstallUtil + .testConnection(wizardModel.remoteUrl.concat(RELEASE_TESTING_MODULE_PATH + "settings.htm"))) { + + wizardModel.remoteUsername = httpRequest.getParameter("username"); + wizardModel.remotePassword = httpRequest.getParameter("password"); + checkForEmptyValue(wizardModel.remoteUsername, errors, "install.testing.username.required"); + checkForEmptyValue(wizardModel.remotePassword, errors, "install.testing.password.required"); + + if (errors.isEmpty()) { + //check if the username and password are valid + try { + TestInstallUtil.getResourceInputStream( + wizardModel.remoteUrl + RELEASE_TESTING_MODULE_PATH + "verifycredentials.htm", + wizardModel.remoteUsername, wizardModel.remotePassword); + } + catch (APIAuthenticationException e) { + log.debug("Error generated: ", e); + page = TESTING_REMOTE_DETAILS_SETUP; + errors.put(ErrorMessageConstants.UPDATE_ERROR_UNABLE_AUTHENTICATE, null); + renderTemplate(page, referenceMap, httpResponse); + return; + } + + //If we have a runtime properties file, get the database setup details from it + if (skipDatabaseSetupPage()) { + Properties props = OpenmrsUtil.getRuntimeProperties(WebConstants.WEBAPP_NAME); + if (props == null) return; + wizardModel.databaseConnection = props.getProperty("connection.url"); + loadedDriverString = loadDriver(wizardModel.databaseConnection, wizardModel.databaseDriver); + if (!StringUtils.hasText(loadedDriverString)) { + page = TESTING_REMOTE_DETAILS_SETUP; + errors.put(ErrorMessageConstants.ERROR_DB_DRIVER_CLASS_REQ, null); + renderTemplate(page, referenceMap, httpResponse); + return; + } + + wizardModel.databaseName = InitializationWizardModel.DEFAULT_DATABASE_NAME; + page = WIZARD_COMPLETE; + } else { + page = DATABASE_SETUP; + wizardModel.currentStepNumber = 2; + } + msgs.put("install.testing.testingModuleFound", null); + } else { + renderTemplate(page, referenceMap, httpResponse); + return; + } + } else { + errors.put("install.testing.noTestingModule", null); + } + } else { + errors.put("install.testing.invalidProductionUrl", new Object[] { wizardModel.remoteUrl }); + } + } + + renderTemplate(page, referenceMap, httpResponse); + } + + /** + * Handles the implementation ID setup page (implementationidsetup.vm) logic + */ + private void handleImplementationIdSetupPage(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Map referenceMap, String page) throws IOException { + if (goBack(httpRequest)) { + if (wizardModel.createTables) { + renderTemplate(ADMIN_USER_SETUP, referenceMap, httpResponse); + } else { + renderTemplate(OTHER_RUNTIME_PROPS, referenceMap, httpResponse); + } + return; + } + + wizardModel.implementationIdName = httpRequest.getParameter("implementation_name"); + wizardModel.implementationId = httpRequest.getParameter("implementation_id"); + wizardModel.implementationIdPassPhrase = httpRequest.getParameter("pass_phrase"); + wizardModel.implementationIdDescription = httpRequest.getParameter("description"); + + // throw back if the user-specified ID is invalid (contains ^ or |). + if (wizardModel.implementationId.indexOf('^') != -1 || wizardModel.implementationId.indexOf('|') != -1) { + errors.put(ErrorMessageConstants.ERROR_DB_IMPL_ID_REQ, null); + renderTemplate(IMPLEMENTATION_ID_SETUP, referenceMap, httpResponse); + return; + } + + if (errors.isEmpty()) { // go to next page + page = WIZARD_COMPLETE; + } + + renderTemplate(page, referenceMap, httpResponse); + } + + /** + * Handles the wizard complete page (wizardcomplete.vm) logic + */ + private void handleWizardCompletePage(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Map referenceMap, String page) throws IOException { + if (goBack(httpRequest)) { + + if (InitializationWizardModel.INSTALL_METHOD_SIMPLE.equals(wizardModel.installMethod)) { + page = SIMPLE_SETUP; + } else if (InitializationWizardModel.INSTALL_METHOD_TESTING.equals(wizardModel.installMethod)) { + if (skipDatabaseSetupPage()) { + page = TESTING_REMOTE_DETAILS_SETUP; + } else { + page = DATABASE_TABLES_AND_USER; + } + } else { + page = IMPLEMENTATION_ID_SETUP; + } + renderTemplate(page, referenceMap, httpResponse); + return; + } + + wizardModel.tasksToExecute = new ArrayList<>(); + createDatabaseTask(); + if (InitializationWizardModel.INSTALL_METHOD_TESTING.equals(wizardModel.installMethod)) { + wizardModel.importTestData = true; + wizardModel.createTables = false; + wizardModel.addDemoData = false; + //if we have a runtime properties file + if (skipDatabaseSetupPage()) { + wizardModel.hasCurrentOpenmrsDatabase = false; + wizardModel.hasCurrentDatabaseUser = true; + wizardModel.createDatabaseUser = false; + Properties props = OpenmrsUtil.getRuntimeProperties(WebConstants.WEBAPP_NAME); + if (props == null) return; + wizardModel.currentDatabaseUsername = props.getProperty("connection.username"); + wizardModel.currentDatabasePassword = props.getProperty("connection.password"); + wizardModel.createDatabaseUsername = wizardModel.currentDatabaseUsername; + wizardModel.createDatabasePassword = wizardModel.currentDatabasePassword; + } + + wizardModel.tasksToExecute.add(WizardTask.IMPORT_TEST_DATA); + wizardModel.tasksToExecute.add(WizardTask.ADD_MODULES); + } else { + createTablesTask(); + createDemoDataTask(); + } + wizardModel.tasksToExecute.add(WizardTask.UPDATE_TO_LATEST); + + referenceMap.put("tasksToExecute", wizardModel.tasksToExecute); + startInstallation(); + renderTemplate(PROGRESS_VM, referenceMap, httpResponse); + } }