com.tesora.dve.server.bootstrap.BootstrapHost.java Source code

Java tutorial

Introduction

Here is the source code for com.tesora.dve.server.bootstrap.BootstrapHost.java

Source

package com.tesora.dve.server.bootstrap;

/*
 * #%L
 * Tesora Inc.
 * Database Virtualization Engine
 * %%
 * Copyright (C) 2011 - 2014 Tesora Inc.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */

import java.lang.management.ManagementFactory;
import java.util.Enumeration;
import java.util.Properties;

import javax.management.MBeanServer;
import javax.management.ObjectName;

import com.tesora.dve.common.catalog.CatalogURL;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.jmx.HierarchyDynamicMBean;
import org.apache.log4j.spi.LoggerRepository;

import com.tesora.dve.common.DBHelper;
import com.tesora.dve.common.PEConstants;
import com.tesora.dve.common.PEFileUtils;
import com.tesora.dve.common.catalog.AutoIncrementTracker;
import com.tesora.dve.common.catalog.CatalogDAO;
import com.tesora.dve.common.catalog.CatalogDAO.CatalogDAOFactory;
import com.tesora.dve.common.catalog.ExternalService;
import com.tesora.dve.common.catalog.Provider;
import com.tesora.dve.common.catalog.TemporaryTable;
import com.tesora.dve.common.catalog.User;
import com.tesora.dve.comms.client.messages.ConnectRequest;
import com.tesora.dve.comms.client.messages.GlobalRecoveryRequest;
import com.tesora.dve.db.mysql.portal.MySqlPortal;
import com.tesora.dve.distribution.RandomDistributionModel;
import com.tesora.dve.distribution.RangeDistributionModel;
import com.tesora.dve.errmap.ErrorMapper;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.externalservice.ExternalServiceFactory;
import com.tesora.dve.groupmanager.GroupManager;
import com.tesora.dve.locking.ClusterLock;
import com.tesora.dve.server.connectionmanager.BroadcastMessageAgent;
import com.tesora.dve.server.connectionmanager.NotificationManager;
import com.tesora.dve.server.connectionmanager.SSConnectionProxy;
import com.tesora.dve.server.global.BootstrapHostService;
import com.tesora.dve.server.global.MySqlPortalService;
import com.tesora.dve.server.statistics.manager.StatisticsManager;
import com.tesora.dve.server.connectionmanager.Transaction2PCTracker;
import com.tesora.dve.singleton.Singletons;
import com.tesora.dve.siteprovider.SiteProviderContextInitialisation;
import com.tesora.dve.siteprovider.SiteProviderPlugin.SiteProviderContext;
import com.tesora.dve.siteprovider.SiteProviderPlugin.SiteProviderFactory;
import com.tesora.dve.smqplugin.SimpleMQPlugin;
import com.tesora.dve.sql.schema.cache.SchemaSourceFactory;
import com.tesora.dve.sql.schema.mt.TableGarbageCollector;
import com.tesora.dve.sql.transexec.CatalogHelper;
import com.tesora.dve.worker.DirectConnectionCache;
import com.tesora.dve.worker.WorkerGroup.WorkerGroupFactory;
import com.tesora.dve.worker.WorkerManager;
import com.tesora.dve.worker.agent.Agent;

public class BootstrapHost extends Host implements BootstrapHostMBean, BootstrapHostService {

    private static final String MBEAN_BOOTSTRAP_HOST = "com.tesora.dve:name=Server";
    private static final String MBEAN_LOG4J_HIERARCHY = "log4j:hierarchy=default";

    static {
        logger = Logger.getLogger(BootstrapHost.class);
    }

    WorkerManager workerManager;
    BroadcastMessageAgent broadcastMessageAgent;
    Transaction2PCTracker transactionManager;
    StatisticsManager statisticsManager;
    NotificationManager notificationManager;
    int statisticsPersisterInterval;
    TableGarbageCollector tgc;
    int tableCleanupInterval;

    public BootstrapHost(String name, Properties props) {
        super(name, props);
        Singletons.replace(BootstrapHostService.class, this);

        try {
            workerManager = new WorkerManager();
            setWorkerManagerAddress(workerManager.getAddress());

            notificationManager = new NotificationManager();
            setNotificationManagerAddress(notificationManager.getAddress());

            statisticsManager = new StatisticsManager();
            setStatisticsManagerAddress(statisticsManager.getAddress());

            broadcastMessageAgent = new BroadcastMessageAgent();
            setBroadcastMessageAgentAddress(broadcastMessageAgent.getAddress());

            WorkerGroupFactory.startup(workerManager);

            initializeCatalogServices();

            if (GroupManager.getCoordinationServices().localMemberIsOldestMember())
                doGlobalRecovery();

            registerMBeans();

        } catch (PEException e) {
            throw new RuntimeException("Failed to start DVE server - " + e.rootCause().getMessage(), e);
        } catch (Throwable e) {
            throw new RuntimeException("Failed to start DVE server - " + e.getMessage(), e);
        }
    }

    public WorkerManager getWorkerManager() {
        return workerManager;
    }

    private void doGlobalRecovery() throws PEException {
        User rootUser = getProject().getRootUser();

        SSConnectionProxy ssConProxy = new SSConnectionProxy();
        try {
            ssConProxy.executeRequest(new ConnectRequest(rootUser.getName(), rootUser.getPlaintextPassword()));
            ssConProxy.executeRequest(new GlobalRecoveryRequest());
        } finally {
            ssConProxy.close();
        }
    }

    @Override
    protected void registerMBeans() {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();

        try {
            server.registerMBean(this, new ObjectName(MBEAN_BOOTSTRAP_HOST));
        } catch (Exception e) {
            logger.error("Unable to register " + MBEAN_BOOTSTRAP_HOST + " mbean", e);
        }

        try {
            HierarchyDynamicMBean hdm = new HierarchyDynamicMBean();
            server.registerMBean(hdm, new ObjectName(MBEAN_LOG4J_HIERARCHY));

            // Add the root logger to the Hierarchy MBean
            hdm.addLoggerMBean(Logger.getRootLogger().getName());

            LoggerRepository r = LogManager.getLoggerRepository();

            @SuppressWarnings("rawtypes")
            Enumeration loggers = r.getCurrentLoggers();
            while (loggers.hasMoreElements()) {
                hdm.addLoggerMBean(((Logger) loggers.nextElement()).getName());
            }
        } catch (Exception e) {
            logger.error("Unable to register " + MBEAN_LOG4J_HIERARCHY + " mbean", e);
        }

        super.registerMBeans();
    }

    @Override
    protected void unregisterMBeans() {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();

        try {
            server.unregisterMBean(new ObjectName(MBEAN_BOOTSTRAP_HOST));
        } catch (Exception e) {
            logger.error("Unable to unregister " + MBEAN_BOOTSTRAP_HOST + " mBean", e);
        }

        try {
            server.unregisterMBean(new ObjectName(MBEAN_LOG4J_HIERARCHY));
        } catch (Exception e) {
            logger.error("Unable to unregister " + MBEAN_LOG4J_HIERARCHY + " mBean", e);
        }

        super.unregisterMBeans();
    }

    public static <T> BootstrapHost startServices(Class<T> bootstrapClass) throws Exception {
        return startServices(bootstrapClass,
                PEFileUtils.loadPropertiesFile(bootstrapClass, PEConstants.CONFIG_FILE_NAME));
    }

    public static <T> BootstrapHost startServices(Class<T> bootstrapClass, Properties props) throws Exception {

        String url = props.getProperty(DBHelper.CONN_URL);
        String database = props.getProperty(DBHelper.CONN_DBNAME, PEConstants.CATALOG);

        // Fail to start if we don't have a valid catalog connection URL
        if (StringUtils.isBlank(url))
            throw new PEException("Value for " + DBHelper.CONN_URL + " not specified in server configuration");

        if (StringUtils.isBlank(database))
            throw new PEException("Value for " + DBHelper.CONN_DBNAME + " not specified in server configuration");

        // Attempt to load the JDBC driver - fail early if it isn't available
        DBHelper.loadDriver(props.getProperty(DBHelper.CONN_DRIVER_CLASS));

        props.put(DBHelper.CONN_URL,
                CatalogURL.buildCatalogBaseUrlFrom(props.getProperty(DBHelper.CONN_URL)).toString());
        props.put(DBHelper.CONN_DBNAME, database);
        DBHelper helper = new DBHelper(props);
        int catalogAccessible = 1;
        try {
            // Check that we can connect to the database and that the catalog database exists
            helper.connect();

            // and also check that it is a valid catalog
            helper.executeQuery("SELECT name FROM " + database + ".user_table");
        } catch (Exception e) {
            String message = e.getMessage();
            if (message != null && message.startsWith("Error using"))
                catalogAccessible = -1;
            else
                catalogAccessible = 0;
            if (catalogAccessible == 0)
                throw new Exception("A DVE catalog couldn't be found at '" + url + "' with name '" + database
                        + "' - use dve_config to set the connection credentials for the server (error was "
                        + message + ")");
        } finally {
            helper.disconnect();
        }

        if (catalogAccessible == -1) {
            new CatalogHelper(bootstrapClass).createBootstrapCatalog();
        }

        // Temporarily set the log level to info so that the below message will print and force the 
        // header to print
        Level logLevel = logger.getLevel(); // this is needed in case an explicit level is set on this category
        Level effectiveLevel = logger.getEffectiveLevel(); // if an explicit level isn't set, this gets the level from the hierarchy
        logger.setLevel(Level.INFO);

        if (catalogAccessible == -1) {
            logger.info("INSTALLED BOOTSTRAP CATALOG " + database + " AT " + url);
        }

        logger.info("Starting DVE server using:");
        logger.info("... Catalog URL      : " + url);
        logger.info("... Catalog database : " + database);
        logger.info("... Catalog User     : " + props.getProperty(DBHelper.CONN_USER));
        logger.info("Resetting log level back to: " + effectiveLevel);
        logger.setLevel(logLevel);

        // We have to initialize the Group Manager before anything else.
        GroupManager.initialize(props);
        GroupManager.getCoordinationServices().registerWithGroup(props);

        CatalogDAOFactory.setup(props);

        SchemaSourceFactory.reset();

        ErrorMapper.initialize();

        Agent.setPluginProvider(SimpleMQPlugin.PROVIDER);
        Agent.startServices(props);

        BootstrapHost host = new BootstrapHost(bootstrapClass.getName(), props);

        ClusterLock genLock = GroupManager.getCoordinationServices()
                .getClusterLock(RangeDistributionModel.GENERATION_LOCKNAME);
        String reason = "boostrapping system";
        genLock.sharedLock(null, reason);
        genLock.sharedUnlock(null, reason);

        TemporaryTable.onStartServices();

        MySqlPortal.start(props);

        return host;
    }

    public static void stopServices() throws Exception {
        MySqlPortal.stop();

        TemporaryTable.onStopServices();

        if (GroupManager.getCoordinationServices() != null)
            GroupManager.getCoordinationServices().unRegisterWithGroup();

        Host.hostStop();

        Agent.stopServices();
        CatalogDAOFactory.shutdown();
        AutoIncrementTracker.clearCache();
        RangeDistributionModel.clearCache();
        RandomDistributionModel.clearCache();
        SiteProviderFactory.closeSiteProviders();
        ExternalServiceFactory.closeExternalServices();
        GroupManager.shutdown();
        DirectConnectionCache.clearConnectionCache();
    }

    @Override
    protected void stop() {
        stopTableCleanup();

        super.stop();
    }

    @Override
    protected void close() {
        unregisterMBeans();

        super.close();

        // No shutdown methods should throw exceptions as we could do nothing but print it
        notificationManager.shutdown();
        WorkerGroupFactory.shutdown(workerManager);
        workerManager.shutdown();
        statisticsManager.shutdown();
        broadcastMessageAgent.shutdown();
    }

    private void initializeCatalogServices() throws Exception {
        CatalogDAO c = CatalogDAOFactory.newInstance();
        try {
            c.recoverTransactions();

            // Create and configure all Dynamic Site Providers
            for (Provider provider : c.findAllProviders()) {
                SiteProviderContext ctxt = new SiteProviderContextInitialisation(provider.getName(), c);
                SiteProviderFactory.addProvider(ctxt, provider.getPlugin(), provider.getName(),
                        provider.isEnabled(), provider.getConfig());
            }

            // Create and configure External Services
            for (ExternalService es : c.findAllExternalServices()) {
                // enclose in try/catch in case an early service fails we still
                // start the later ones
                try {
                    ExternalServiceFactory.register(es.getName(), es.getPlugin());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            //         GlobalConfigVariableHandler.initializeGlobalVariables(getGlobalVariables(c));
        } finally {
            c.close();
        }
    }

    // -------------------------------------------------------------------------

    public void startTableCleanup() {
        if (tableCleanupInterval > 0) {
            if (tgc != null)
                tgc.stopTableCleanup();

            tgc = new TableGarbageCollector(tableCleanupInterval);
            tgc.startTableCleanup();
        }
    }

    private void stopTableCleanup() {
        if (tgc != null)
            tgc.stopTableCleanup();
    }

    public void setTableCleanupInterval(int value, boolean action) {
        int oldValue = tableCleanupInterval;
        tableCleanupInterval = value;

        if (action) {
            if (oldValue == 0)
                startTableCleanup();
            else if (tableCleanupInterval == 0)
                stopTableCleanup();
        }
    }

    // -------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // The following methods are for the BootstrapHost MBean

    @Override
    public int getPortalWorkerGroupCount() {
        MySqlPortalService portal = Singletons.lookup(MySqlPortalService.class);
        return portal == null ? 0 : portal.getWorkerGroupCount();
    }

    @Override
    public int getPortalClientExecutorActiveCount() {
        MySqlPortalService portal = Singletons.lookup(MySqlPortalService.class);
        return portal == null ? 0 : portal.getClientExecutorActiveCount();
    }

    @Override
    public int getPortalClientExecutorPoolSize() {
        MySqlPortalService portal = Singletons.lookup(MySqlPortalService.class);
        return portal == null ? 0 : portal.getClientExecutorPoolSize();
    }

    @Override
    public int getPortalClientExecutorLargestPoolSize() {
        MySqlPortalService portal = Singletons.lookup(MySqlPortalService.class);
        return portal == null ? 0 : portal.getClientExecutorLargestPoolSize();
    }

    @Override
    public int getPortalClientExecutorMaximumPoolSize() {
        MySqlPortalService portal = Singletons.lookup(MySqlPortalService.class);
        return portal == null ? 0 : portal.getClientExecutorMaximumPoolSize();
    }

    @Override
    public int getPortalClientExecutorQueueSize() {
        MySqlPortalService portal = Singletons.lookup(MySqlPortalService.class);
        return portal == null ? 0 : portal.getClientExecutorQueueSize();
    }

    @Override
    public void onGarbageEvent() {
        if (tgc != null)
            tgc.onGarbageEvent();
    }

}