/* * Copyright 2012 Nodeable Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.streamreduce.datasource; import com.streamreduce.Constants; import com.streamreduce.core.ApplicationManager; import com.streamreduce.core.dao.AccountDAO; import com.streamreduce.core.dao.RoleDAO; import com.streamreduce.core.dao.SystemInfoDAO; import com.streamreduce.core.model.Account; import com.streamreduce.core.model.Role; import com.streamreduce.core.model.SystemInfo; import com.streamreduce.core.model.User; import com.streamreduce.core.service.UserService; import com.streamreduce.core.service.exception.UserNotFoundException; import com.streamreduce.datasource.patch.Patch; import com.streamreduce.datasource.patch.PatchException; import com.streamreduce.security.Permissions; import com.streamreduce.security.Roles; import com.streamreduce.util.JSONObjectBuilder; import java.util.ResourceBundle; import net.sf.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * The BootstrapDatabaseDataPopulator class is a Spring @see org.springframework.beans.factory.InitializingBean that will * bootstrap the database with the necessary data for Nodeable to run. * * @since 1.0 */ public class BootstrapDatabaseDataPopulator implements InitializingBean, ApplicationContextAware { private String superUserPassword; private boolean bootstrapSkip; private String latestPatch; private String patchMaster; protected transient Logger logger = LoggerFactory.getLogger(BootstrapDatabaseDataPopulator.class); @Autowired private RoleDAO roleDAO; @Autowired private AccountDAO accountDAO; @Autowired private UserService userService; @Autowired private SystemInfoDAO systemStatusDAO; @Autowired private ApplicationManager applicationManager; private ApplicationContext applicationContext; /** * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ @Override public void afterPropertiesSet() throws Exception { if (bootstrapSkip) { return; } bootstrapSystemInfo(); doPatch(); bootstrapRoles(); bootstrapUsersAndAccounts(); } private void bootstrapSystemInfo() { ResourceBundle resourceBundle = ResourceBundle.getBundle("application"); SystemInfo systemStatus = systemStatusDAO.getLatest(); if (systemStatus == null) { systemStatus = new SystemInfo(); } systemStatus.setAppVersion(resourceBundle.getString("nodeable.version")); systemStatus.setBuildNumber(resourceBundle.getString("nodeable.build")); systemStatusDAO.save(systemStatus); } private void doPatch() { SystemInfo systemInfo = systemStatusDAO.getLatest(); // the current patch version application is running Integer currentPatchLevel = systemInfo.getPatchLevel(); // if it's not set in the db... should only ever happen once (unless you dropped your db) if (currentPatchLevel == null) { currentPatchLevel = 0; } // from the properties, this is what we should apply Integer latestPatchAvailable = new Integer(latestPatch); logger.info("[BOOTSTRAP] db is at patch level " + currentPatchLevel); logger.info("[BOOTSTRAP] available patch number is " + latestPatchAvailable); // See if there is anything to do if (!isPatchRequired()) { logger.info("[BOOTSTRAP] nothing to do, exiting doPatch()"); return; } // check to see if this host is the PatchMaster, all others are blocked until completion try { String hostname = java.net.InetAddress.getLocalHost().getHostName(); if (!hostname.equals(patchMaster)) { // sleep, waking periodically to see if the patch is done. while (isPatchRequired()) { logger.info("[BOOTSTRAP]" + hostname + " sleeping while db is locked and patches are applied."); Thread.sleep(3000); // 3 second nap } logger.info("[BOOTSTRAP]" + hostname + " waking from sleep and booting, patch master is done."); // done, continue the boot process return; } } catch (Exception e) { logger.error("[BOOTSTRAP] patch failure: " + e.getMessage()); throw new RuntimeException("[BOOTSTRAP] patch failure, terminating." + e.getMessage()); } // apply patches if you are the patch master while (currentPatchLevel < latestPatchAvailable) { try { currentPatchLevel = currentPatchLevel + 1; Patch patch = (Patch) applicationContext.getBean("patch" + currentPatchLevel); if (patch == null) { logger.info("[BOOTSTRAP] class not found for patch" + currentPatchLevel); logger.info("[BOOTSTRAP] patch " + currentPatchLevel + " not applied, exiting patch process"); break; } patch.applyPatch(applicationManager, applicationContext); // if it fails do no set this.. there is no "continue" here. systemInfo.setPatchLevel(currentPatchLevel); systemStatusDAO.save(systemInfo); } catch (PatchException e) { logger.info("[BOOTSTRAP] " + e.getMessage() + " patch " + currentPatchLevel + " not applied, exiting patch process"); break; } logger.info("[BOOTSTRAP] patch " + currentPatchLevel + " successfully applied"); } } private boolean isPatchRequired() { SystemInfo systemInfo = systemStatusDAO.getLatest(); Integer currentPatchLevel = systemInfo.getPatchLevel(); return currentPatchLevel == null || currentPatchLevel < new Integer(latestPatch); } /** * Bootstraps the necessary user accounts. */ private void bootstrapUsersAndAccounts() { // create it if it doesn't exist // always create the system user first if (getUser(Constants.NODEABLE_SYSTEM_USERNAME) == null) { Account rootAccount = accountDAO.findByName(Constants.NODEABLE_SUPER_ACCOUNT_NAME); // create it if it doesn't exist, should only happen with a clean db if (rootAccount == null) { Account account = new Account.Builder() .url("http://nodeable.com") .description("Core Nodeable Account") .name(Constants.NODEABLE_SUPER_ACCOUNT_NAME) .build(); rootAccount = userService.createAccount(account); } JSONObject config = new JSONObjectBuilder().add("icon", "/images/nodebelly.jpg").build(); // we are bypassing the lifecycle here, but still firing proper events User user = new User.Builder() .username(Constants.NODEABLE_SYSTEM_USERNAME) .password(superUserPassword) .accountLocked(false) .fullname("Nodeable") .userStatus(User.UserStatus.ACTIVATED) .roles(userService.getAdminRoles()) .account(rootAccount) .alias("nodeable") .userConfig(config) .build(); userService.createUser(user); } // create it if it doesn't exist if (getUser(Constants.NODEABLE_SUPER_USERNAME) == null) { JSONObject config = new JSONObjectBuilder().add("icon", "/images/nodebelly.jpg").build(); // we are bypassing the lifecycle here, but still firing proper events User user = new User.Builder() .username(Constants.NODEABLE_SUPER_USERNAME) .password(superUserPassword) .accountLocked(false) .fullname("Nodeable Insight") .userStatus(User.UserStatus.ACTIVATED) .roles(userService.getAdminRoles()) .account(accountDAO.findByName(Constants.NODEABLE_SUPER_ACCOUNT_NAME)) .alias("insight") .userConfig(config) .build(); userService.createUser(user); } } public void bootstrapMinimumData() { bootstrapSystemInfo(); bootstrapRoles(); bootstrapUsersAndAccounts(); } private User getUser(String username) { try { return userService.getUser(username); } catch (UserNotFoundException unfe) { return null; } } /** * Bootstrap the initial Roles and their Permissions. */ private void bootstrapRoles() { if (roleDAO.findRole(Roles.ADMIN_ROLE) == null) { Role admin = new Role(Roles.ADMIN_ROLE, "Administrator Role"); admin.setPermissions(Permissions.ALL); roleDAO.save(admin); } if (roleDAO.findRole(Roles.DEVELOPER_ROLE) == null) { Role dev = new Role(Roles.DEVELOPER_ROLE, "Developer Role"); dev.addPermissions(Permissions.APP_USER); roleDAO.save(dev); } if (roleDAO.findRole(Roles.USER_ROLE) == null) { Role user = new Role(Roles.USER_ROLE, "Required Role to Login"); user.addPermissions(Permissions.APP_USER); roleDAO.save(user); } } public void setSuperUserPassword(String superUserPassword) { this.superUserPassword = superUserPassword; } public void setLatestPatch(String latestPatch) { this.latestPatch = latestPatch; } public void setBootstrapSkip(boolean bootstrapSkip) { this.bootstrapSkip = bootstrapSkip; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public void setPatchMaster(String patchMaster) { this.patchMaster = patchMaster; } }