com.tc.server.TCServerImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.tc.server.TCServerImpl.java

Source

/*
 *
 *  The contents of this file are subject to the Terracotta Public 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://terracotta.org/legal/terracotta-public-license.
 *
 *  Software distributed under the License is distributed on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 *  the specific language governing rights and limitations under the License.
 *
 *  The Covered Software is Terracotta Core.
 *
 *  The Initial Developer of the Covered Software is
 *  Terracotta, Inc., a Software AG company
 *
 */
package com.tc.server;

import org.apache.commons.io.IOUtils;

import com.tc.async.api.SEDA;
import com.tc.config.schema.ActiveServerGroupConfig;
import com.tc.config.schema.CommonL2Config;
import com.tc.config.schema.L2Info;
import com.tc.config.schema.ServerGroupInfo;
import com.tc.config.schema.setup.ConfigurationSetupException;
import com.tc.config.schema.setup.L2ConfigurationSetupManager;
import com.tc.l2.context.StateChangedEvent;
import com.tc.l2.state.StateChangeListener;
import com.tc.l2.state.StateManager;
import com.tc.lang.StartupHelper;
import com.tc.lang.StartupHelper.StartupAction;
import com.tc.lang.TCThreadGroup;
import com.tc.lang.ThrowableHandlerImpl;
import com.tc.logging.CustomerLogging;
import com.tc.logging.TCLogger;
import com.tc.logging.TCLogging;
import com.tc.management.beans.L2Dumper;
import com.tc.management.beans.L2MBeanNames;
import com.tc.management.beans.TCDumper;
import com.tc.management.beans.TCServerInfo;
import com.tc.net.TCSocketAddress;
import com.tc.net.core.security.TCSecurityManager;
import com.tc.net.protocol.HttpConnectionContext;
import com.tc.net.protocol.transport.ConnectionPolicy;
import com.tc.net.protocol.transport.ConnectionPolicyImpl;
import com.tc.objectserver.core.api.ServerConfigurationContext;
import com.tc.objectserver.core.impl.ServerManagementContext;
import com.tc.objectserver.impl.DistributedObjectServer;
import com.tc.operatorevent.TerracottaOperatorEventHistoryProvider;
import com.tc.stats.DSO;
import com.tc.stats.api.DSOMBean;
import com.tc.text.StringUtils;
import com.tc.util.Assert;
import com.tc.util.ProductInfo;
import com.tc.util.State;

import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;

public class TCServerImpl extends SEDA<HttpConnectionContext> implements TCServer, StateChangeListener {
    public static final String CONNECTOR_NAME_TERRACOTTA = "terracotta";

    private static final TCLogger logger = TCLogging.getLogger(TCServer.class);
    private static final TCLogger consoleLogger = CustomerLogging.getConsoleLogger();

    private volatile long startTime = -1;
    private volatile long activateTime = -1;

    protected DistributedObjectServer dsoServer;

    private final Object stateLock = new Object();
    private State serverState = StateManager.START_STATE;

    private final L2ConfigurationSetupManager configurationSetupManager;
    protected final ConnectionPolicy connectionPolicy;
    private boolean shutdown = false;
    protected final TCSecurityManager securityManager;

    /**
     * This should only be used for tests.
     */
    public TCServerImpl(L2ConfigurationSetupManager configurationSetupManager) {
        this(configurationSetupManager,
                new TCThreadGroup(new ThrowableHandlerImpl(TCLogging.getLogger(TCServer.class))));
    }

    public TCServerImpl(L2ConfigurationSetupManager configurationSetupManager, TCThreadGroup threadGroup) {
        this(configurationSetupManager, threadGroup, new ConnectionPolicyImpl(Integer.MAX_VALUE));
    }

    public TCServerImpl(L2ConfigurationSetupManager manager, TCThreadGroup group,
            ConnectionPolicy connectionPolicy) {
        super(group);

        this.connectionPolicy = connectionPolicy;
        Assert.assertNotNull(manager);
        this.configurationSetupManager = manager;

        if (configurationSetupManager.isSecure()) {
            // no security implemention
            this.securityManager = null;
        } else {
            this.securityManager = null;
        }
    }

    public synchronized void setState(State state) {
        if (!validateState(state)) {
            throw new AssertionError("Unrecognized server state: [" + state.getName() + "]");
        }

        serverState = state;
    }

    private boolean validateState(State state) {
        return StateManager.VALID_STATES.contains(state);
    }

    @Override
    public ServerGroupInfo getStripeInfo() {
        L2Info[] l2Infos = infoForAllL2s();
        ActiveServerGroupConfig groupInfo = this.configurationSetupManager.getActiveServerGroupForThisL2();

        List<L2Info> memberList = new ArrayList<>();
        for (L2Info l2Info : l2Infos) {
            if (groupInfo.isMember(l2Info.name())) {
                memberList.add(l2Info);
            }
        }

        return new ServerGroupInfo(memberList.toArray(new L2Info[0]), groupInfo.getGroupName(), true);
    }

    @Override
    public L2Info[] infoForAllL2s() {
        String[] allKnownL2s = this.configurationSetupManager.allCurrentlyKnownServers();
        L2Info[] out = new L2Info[allKnownL2s.length];

        for (int i = 0; i < out.length; ++i) {
            try {
                CommonL2Config config = this.configurationSetupManager.commonL2ConfigFor(allKnownL2s[i]);

                String name = allKnownL2s[i];
                if (name == null) {
                    name = L2Info.IMPLICIT_L2_NAME;
                }

                String host = config.tsaPort().getBind();
                if (TCSocketAddress.WILDCARD_IP.equals(host)) {
                    host = config.host();
                }
                if (StringUtils.isBlank(host)) {
                    host = name;
                }
                //XXX hard coded jmx port
                out[i] = new L2Info(name, host, config.tsaPort().getValue() + 10, config.tsaPort().getValue(),
                        config.tsaGroupPort().getBind(), config.tsaGroupPort().getValue(),
                        config.tsaGroupPort().getValue() + 1, "");
            } catch (ConfigurationSetupException cse) {
                throw Assert.failure("This should be impossible here", cse);
            }
        }

        return out;
    }

    @Override
    public String getL2Identifier() {
        return configurationSetupManager.getL2Identifier();
    }

    @Override
    public String getDescriptionOfCapabilities() {
        if (ProductInfo.getInstance().isEnterprise()) {
            return "Enterprise capabilities";
        } else {
            return "Open source capabilities";
        }
    }

    /**
     * I realize this is wrong, since the server can still be starting but we'll have to deal with the whole stopping
     * issue later, and there's the TCStop feature which should be removed.
     */
    @Override
    public void stop() {
        // XXX: This is part of the now-deprecated JMX 4.x Management interface called from TCServerInfo.
        // TODO:  Remove the com.tc.mangement.beans package and then this method.
        throw new UnsupportedOperationException("Management can no longer request a server shutdown");
    }

    @Override
    public void start() {
        synchronized (this.stateLock) {
            if (!this.isStarted()) {
                try {
                    startServer();
                } catch (Throwable t) {
                    if (t instanceof RuntimeException) {
                        throw (RuntimeException) t;
                    }
                    throw new RuntimeException(t);
                }
            } else {
                logger.warn("Server in incorrect state (" + this.serverState + ") to be started.");
            }
        }
    }

    @Override
    public boolean canShutdown() {
        synchronized (this.stateLock) {
            return serverState.equals(StateManager.PASSIVE_STANDBY)
                    || serverState.equals(StateManager.ACTIVE_COORDINATOR)
                    || serverState.equals(StateManager.PASSIVE_UNINITIALIZED)
                    || serverState.equals(StateManager.PASSIVE_SYNCING);
        }
    }

    @Override
    public synchronized void shutdown() {
        if (canShutdown()) {
            setState(StateManager.STOP_STATE);
            consoleLogger.info("Server exiting...");
            notifyShutdown();
            Runtime.getRuntime().exit(0);
        } else {
            logger.warn("Server in incorrect state (" + serverState + ") to be shutdown.");
        }
    }

    @Override
    public long getStartTime() {
        return this.startTime;
    }

    @Override
    public void updateActivateTime() {
        if (this.activateTime == -1) {
            this.activateTime = System.currentTimeMillis();
        }
    }

    @Override
    public long getActivateTime() {
        return this.activateTime;
    }

    @Override
    public String getConfig() {
        InputStream is = null;
        try {
            is = this.configurationSetupManager.rawConfigFile();
            return IOUtils.toString(is);
        } catch (IOException ioe) {
            return ioe.getLocalizedMessage();
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    @Override
    public int getTSAListenPort() {
        if (this.dsoServer != null) {
            return this.dsoServer.getListenPort();
        }
        throw new IllegalStateException("TSA Server not running");
    }

    @Override
    public int getTSAGroupPort() {
        if (this.dsoServer != null) {
            return this.dsoServer.getGroupPort();
        }
        throw new IllegalStateException("TSA Server not running");
    }

    public DistributedObjectServer getDSOServer() {
        return this.dsoServer;
    }

    @Override
    public boolean isStarted() {
        synchronized (this.stateLock) {
            return !this.serverState.equals(StateManager.START_STATE);
        }
    }

    @Override
    public boolean isActive() {
        synchronized (this.stateLock) {
            return this.serverState.equals(StateManager.ACTIVE_COORDINATOR);
        }
    }

    @Override
    public boolean isStopped() {
        // XXX:: introduce a new state when stop is officially supported.
        synchronized (this.stateLock) {
            return this.serverState.equals(StateManager.START_STATE);
        }
    }

    @Override
    public boolean isPassiveUnitialized() {
        synchronized (this.stateLock) {
            return this.serverState.equals(StateManager.PASSIVE_UNINITIALIZED);
        }
    }

    @Override
    public boolean isPassiveStandby() {
        synchronized (this.stateLock) {
            return this.serverState.equals(StateManager.PASSIVE_STANDBY);
        }
    }

    @Override
    public State getState() {
        return this.serverState;
    }

    @Override
    public String toString() {
        StringBuffer buf = new StringBuffer();
        buf.append("Server: ").append(super.toString()).append("\n");
        if (isActive()) {
            buf.append("Active since ").append(new Date(getStartTime())).append("\n");
        } else if (isStarted()) {
            buf.append("Started at ").append(new Date(getStartTime())).append("\n");
        } else {
            buf.append("Server is stopped").append("\n");
        }

        return buf.toString();
    }

    private class StartAction implements StartupAction {
        @Override
        public void execute() throws Throwable {
            if (logger.isDebugEnabled()) {
                logger.debug("Starting Terracotta server instance...");
            }

            TCServerImpl.this.startTime = System.currentTimeMillis();

            if (Runtime.getRuntime().maxMemory() != Long.MAX_VALUE) {
                consoleLogger.info(
                        "Available Max Runtime Memory: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB");
            }

            // the following code starts the jmx server as well
            startDSOServer();

            if (isActive()) {
                updateActivateTime();
                if (TCServerImpl.this.activationListener != null) {
                    TCServerImpl.this.activationListener.serverActivated();
                }
            }

            String l2Identifier = TCServerImpl.this.configurationSetupManager.getL2Identifier();
            if (l2Identifier != null) {
                logger.info("Server started as " + l2Identifier);
            }
        }
    }

    protected void startServer() throws Exception {
        new StartupHelper(getThreadGroup(), new StartAction()).startUp();
    }

    private void startDSOServer() throws Exception {
        Assert.assertTrue(this.isStopped());
        this.dsoServer = createDistributedObjectServer(this.configurationSetupManager, this.connectionPolicy, this);
        this.dsoServer.start();
        registerDSOServer(dsoServer);
    }

    protected DistributedObjectServer createDistributedObjectServer(L2ConfigurationSetupManager configSetupManager,
            ConnectionPolicy policy, TCServerImpl serverImpl) {
        DistributedObjectServer dso = new DistributedObjectServer(configSetupManager, getThreadGroup(), policy,
                this, this, securityManager);
        try {
            registerServerMBeans(dso, ManagementFactory.getPlatformMBeanServer());
        } catch (NotCompliantMBeanException | InstanceAlreadyExistsException | MBeanRegistrationException exp) {
            throw new RuntimeException(exp);
        }
        return dso;
    }

    @Override
    public void dump() {
        if (this.dsoServer != null) {
            this.dsoServer.dump();
        }
    }

    private void registerDSOServer(TCDumper dumper) throws InstanceAlreadyExistsException,
            MBeanRegistrationException, NotCompliantMBeanException, NullPointerException {

        ServerManagementContext mgmtContext = this.dsoServer.getManagementContext();
        ServerConfigurationContext configContext = this.dsoServer.getContext();
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
        registerDSOMBeans(mgmtContext, configContext, dumper, mBeanServer);
    }

    protected void registerServerMBeans(TCDumper tcDumper, MBeanServer mBeanServer)
            throws NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException {
        mBeanServer.registerMBean(new TCServerInfo(this), L2MBeanNames.TC_SERVER_INFO);
        mBeanServer.registerMBean(new L2Dumper(tcDumper, mBeanServer), L2MBeanNames.DUMPER);
    }

    protected void unregisterServerMBeans(MBeanServer mbs)
            throws MBeanRegistrationException, InstanceNotFoundException {
        mbs.unregisterMBean(L2MBeanNames.TC_SERVER_INFO);
        mbs.unregisterMBean(L2MBeanNames.DUMPER);
    }

    protected void registerDSOMBeans(ServerManagementContext mgmtContext, ServerConfigurationContext configContext,
            TCDumper tcDumper, MBeanServer mBeanServer)
            throws NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException {
        TerracottaOperatorEventHistoryProvider operatorEventHistoryProvider = this.dsoServer
                .getOperatorEventsHistoryProvider();
        DSOMBean dso = new DSO(mgmtContext, configContext, mBeanServer, operatorEventHistoryProvider);
        mBeanServer.registerMBean(dso, L2MBeanNames.DSO);
    }

    protected void unregisterDSOMBeans(MBeanServer mbs)
            throws MBeanRegistrationException, InstanceNotFoundException {
        mbs.unregisterMBean(L2MBeanNames.DSO);
    }

    // TODO: check that this is not needed then remove
    private TCServerActivationListener activationListener;

    public void setActivationListener(TCServerActivationListener listener) {
        this.activationListener = listener;
    }

    private synchronized void notifyShutdown() {
        shutdown = true;
        notifyAll();
    }

    @Override
    public synchronized void waitUntilShutdown() {
        while (!shutdown) {
            try {
                wait();
            } catch (InterruptedException e) {
                throw new AssertionError(e);
            }
        }
    }

    @Override
    public void reloadConfiguration() throws ConfigurationSetupException {
        dsoServer.reloadConfiguration();
    }

    @Override
    public String[] processArguments() {
        return configurationSetupManager.processArguments();
    }

    @Override
    public void dumpClusterState() {
        if (this.dsoServer != null) {
            this.dsoServer.dumpClusterState();
        }
    }

    @Override
    public String getResourceState() {
        return "";
    }

    @Override
    public void l2StateChanged(StateChangedEvent sce) {
        synchronized (this.stateLock) {
            this.serverState = sce.getCurrentState();
        }
    }
}