com.streamreduce.datasource.BootstrapDatabaseDataPopulator.java Source code

Java tutorial

Introduction

Here is the source code for com.streamreduce.datasource.BootstrapDatabaseDataPopulator.java

Source

/*
 * 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;
    }
}