org.fusesource.meshkeeper.control.ControlServer.java Source code

Java tutorial

Introduction

Here is the source code for org.fusesource.meshkeeper.control.ControlServer.java

Source

/**
 *  Copyright (C) 2009 Progress Software, Inc. All rights reserved.
 *  http://fusesource.com
 *
 *  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 org.fusesource.meshkeeper.control;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
import java.nio.channels.FileLockInterruptionException;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.fusesource.meshkeeper.MeshEvent;
import org.fusesource.meshkeeper.MeshEventListener;
import org.fusesource.meshkeeper.MeshKeeper;
import org.fusesource.meshkeeper.MeshKeeper.Registry;
import org.fusesource.meshkeeper.MeshKeeperFactory;
import org.fusesource.meshkeeper.distribution.DistributorFactory;
import org.fusesource.meshkeeper.distribution.provisioner.Provisioner;
import org.fusesource.meshkeeper.launcher.LaunchAgent;
import org.fusesource.meshkeeper.util.internal.FileSupport;

/**
 * ControlServer
 * <p>
 * Description: The control server hosts the servers used to facilitate the distributed test system.
 * 
 * </p>
 * 
 * @author cmacnaug
 * @version 1.0
 */
public class ControlServer {

    public static final String CONTROLLER_PROP_FILE_NAME = "controller.properties";

    Log log = LogFactory.getLog(ControlServer.class);
    private static final ControlServiceFactory SERVICE_FACTORY = new ControlServiceFactory();

    public static final String DEFAULT_JMS_URI = "activemq:tcp://0.0.0.0:0";
    public static final String DEFAULT_REMOTING_URI = "rmiviajms:" + DEFAULT_JMS_URI;
    public static final String DEFAULT_REGISTRY_URI = "zk:tcp://0.0.0.0:4040";
    public static final String DEFAULT_EVENT_URI = "eventviajms:" + DEFAULT_JMS_URI;
    public static final String DEFAULT_REPOSITORY_URI = "wagon";

    public static final String REMOTING_URI_PATH = Registry.MESH_KEEPER_ROOT + "/control/remoting-uri";
    public static final String EVENTING_URI_PATH = Registry.MESH_KEEPER_ROOT + "/control/eventing-uri";
    public static final String REPOSITORY_URI_PATH = Registry.MESH_KEEPER_ROOT + "/control/repository-uri";

    ControlService rmiServer;
    ControlService registryServer;
    MeshKeeper meshKeeper;

    private String jmsUri = DEFAULT_JMS_URI;
    private String registryUri = DEFAULT_REGISTRY_URI;
    private String repositoryUri = System.getProperty("meshkeeper.repository.uri", DEFAULT_REPOSITORY_URI);

    private String directory = MeshKeeperFactory.getDefaultServerDirectory().getPath();
    private Thread shutdownHook;

    private Runnable preShutdownHook;

    private LaunchAgent embeddedAgent;

    private boolean startEmbeddedAgent;

    private FileLock propsLock;

    public static final String CONTROL_TOPIC = "meshkeeper.control";

    public enum ControlEvent {
        SHUTDOWN; // When received shuts down the control server:

        public MeshEvent createEvent(String source, Object attachment) {
            return new MeshEvent(ordinal(), source, attachment);
        }

        public static ControlEvent getControlEvent(MeshEvent e) {
            if (e == null) {
                return null;
            }

            try {
                return ControlEvent.values()[e.getType()];
            } catch (Throwable thrown) {
                return null;
            }
        }
    }

    public void start() throws Exception {

        deleteControllerProps();
        shutdownHook = new Thread("MeshKeeper Control Server Shutdown Hook") {
            public void run() {
                log.debug("Executing Shutdown Hook for " + ControlServer.this);
                try {
                    ControlServer.this.destroy();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };

        // Start the jms server:
        log.info("Creating JMS Server at " + jmsUri);
        final String SLASH = File.separator;
        try {
            rmiServer = SERVICE_FACTORY.create(jmsUri);
            rmiServer.setDirectory(directory + SLASH + "jms");
            rmiServer.start();
            log.info("JMS Server started: " + rmiServer.getName());

        } catch (Exception e) {
            log.error(e);
            destroy();
            throw new Exception("Error starting JMS Server", e);
        }

        // Start the registry server:
        log.info("Creating Registry Server at " + registryUri);
        try {
            registryServer = SERVICE_FACTORY.create(registryUri);
            registryServer.setDirectory(directory + SLASH + "registry");
            registryServer.start();
            log.info("Registry Server started: " + registryServer.getName() + " uri: "
                    + registryServer.getServiceUri());

        } catch (Exception e) {
            log.error("Error starting regisry server", e);
            destroy();
            throw new Exception("Error starting Registry Server", e);
        }

        // Connect to the registry and publish service connection info:
        try {

            log.info("Connecting to registry server at " + registryServer.getServiceUri());
            // registry = new RegistryFactory().create(registryServer.getServiceUri());

            String eventingUri = "eventviajms:" + rmiServer.getServiceUri();
            String remotingUri = "rmiviajms:" + rmiServer.getServiceUri();
            DistributorFactory factory = new DistributorFactory();
            factory.setRegistryUri(registryServer.getServiceUri());
            factory.setEventingUri(eventingUri);
            factory.setRemotingUri(remotingUri);
            factory.setRepositoryUri(repositoryUri);
            factory.setDirectory(getDirectory());

            meshKeeper = factory.create();

            // Register the control services:

            // (note that we delete these first since
            // in some instances zoo-keeper doesn't shutdown cleanly and hangs
            // on to file handles so that the registry isn't purged:
            meshKeeper.registry().removeRegistryData(REMOTING_URI_PATH, true);
            meshKeeper.registry().addRegistryObject(REMOTING_URI_PATH, false, remotingUri);
            log.info("Registered RMI control server at " + REMOTING_URI_PATH + "=" + remotingUri);

            meshKeeper.registry().removeRegistryData(EVENTING_URI_PATH, true);
            meshKeeper.registry().addRegistryObject(EVENTING_URI_PATH, false, eventingUri);
            log.info("Registered event server at " + EVENTING_URI_PATH + "=" + eventingUri);

            meshKeeper.registry().removeRegistryData(REPOSITORY_URI_PATH, true);
            if (repositoryUri != null) {
                meshKeeper.registry().addRegistryObject(REPOSITORY_URI_PATH, false, repositoryUri);
                log.info("Registered repository uri at " + REPOSITORY_URI_PATH + "=" + repositoryUri);
            } else {
                log.info("Central repository uri was not set, some repository services may not be available");
            }

            // Let's save our controller properties to an output file
            // useful for discovering our registry connect url:
            saveControllerProps();

            // Let's open an event listener to handle service commands:
            meshKeeper.eventing().openEventListener(new MeshEventListener() {

                public void onEvent(MeshEvent e) {
                    switch (ControlEvent.getControlEvent(e)) {
                    case SHUTDOWN:
                        log.info("Got shutdown request: " + e);
                        // Fire off in a new thread (not the eventing thread):
                        new Thread("Controller Shutdown") {
                            public void run() {
                                try {
                                    ControlServer.this.destroy();
                                } catch (Exception e) {
                                    log.warn("Error during control server shutdown", e);
                                }
                            }
                        }.start();
                        break;
                    default:
                        log.warn("Got unknown control event: " + e);
                    }
                }
            }, CONTROL_TOPIC);

            if (startEmbeddedAgent && embeddedAgent == null) {
                embeddedAgent = new LaunchAgent();
            }

            if (embeddedAgent != null) {
                embeddedAgent.setMeshKeeper(meshKeeper);
                embeddedAgent.setDirectory(new File(getDirectory()));
                embeddedAgent.start();
            }

            log.info("MeshKeeper Successfully started. The Registry Service is listening on: " + getRegistryUri());

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            destroy();
            throw new Exception("Error registering control server", e);
        }
    }

    public void setEmbeddedLaunchAgent(LaunchAgent embeddedAgent) {
        this.embeddedAgent = embeddedAgent;
    }

    public LaunchAgent getEmbeddedLaunchAgent() {
        return embeddedAgent;
    }

    public void setPreShutdownHook(Runnable runnable) {
        this.preShutdownHook = runnable;
    }

    private File getPropsFile() {
        return new File(getDirectory(), CONTROLLER_PROP_FILE_NAME);
    }

    private void lockPropsFile() {
        if (propsLock == null) {
            File f = getPropsFile();
            if (f.exists()) {

                boolean first = true;

                while (propsLock == null) {
                    try {
                        if (first) {
                            propsLock = new RandomAccessFile(getPropsFile(), "r").getChannel().tryLock(0,
                                    Long.MAX_VALUE, true);
                            if (propsLock == null) {
                                log.warn("Waiting to lock" + f);
                            } else {
                                break;
                            }
                            first = false;
                        }

                        propsLock = new RandomAccessFile(getPropsFile(), "r").getChannel().lock(0, Long.MAX_VALUE,
                                true);
                    } catch (FileLockInterruptionException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    } catch (Exception e) {
                    }
                    log.warn("Couldn't lock " + f);
                }
            }
        }
    }

    private final void deleteControllerProps() {
        if (propsLock != null) {
            try {
                propsLock.release();
                propsLock.channel().close();
                propsLock = null;
            } catch (IOException e) {
                log.warn("Error releasing lock on " + getPropsFile());
            }
        }
        FileSupport.recursiveDelete(new File(getDirectory(), CONTROLLER_PROP_FILE_NAME));
    }

    private final void saveControllerProps() throws IOException {
        // Let's dump some controller properties to our working directory:
        Properties props = new Properties();
        props.put(MeshKeeperFactory.MESHKEEPER_REGISTRY_PROPERTY, registryServer.getServiceUri());
        String provisionerId = System.getProperty(Provisioner.MESHKEEPER_PROVISIONER_ID_PROPERTY);
        if (provisionerId != null) {
            log.info("Writing provisioner id: " + provisionerId);
            props.put(Provisioner.MESHKEEPER_PROVISIONER_ID_PROPERTY, provisionerId);
        } else {
            props.put(Provisioner.MESHKEEPER_PROVISIONER_ID_PROPERTY, "none");
        }

        File f = new File(getDirectory(), CONTROLLER_PROP_FILE_NAME);
        if (f.exists()) {
            f.delete();
        }
        f.getParentFile().mkdirs();
        f.deleteOnExit();
        PrintStream fout = new PrintStream(f);
        props.store(fout, null);
        fout.flush();
        fout.close();

        //Lock the properties file:
        lockPropsFile();
    }

    public void destroy() throws Exception {

        if (Thread.currentThread() != shutdownHook) {
            Runtime.getRuntime().removeShutdownHook(shutdownHook);
        }

        deleteControllerProps();

        if (preShutdownHook != null) {
            preShutdownHook.run();
            preShutdownHook = null;
        }

        Exception first = null;

        if (embeddedAgent != null) {
            try {
                embeddedAgent.stop();
            } catch (Exception e) {
                first = first == null ? e : first;
            }
        }

        if (meshKeeper != null) {
            try {
                meshKeeper.destroy();
            } catch (Exception e) {
                first = first == null ? e : first;
            } finally {
                meshKeeper = null;
            }
        }

        log.info("Shutting down registry server");
        if (registryServer != null) {
            try {
                registryServer.destroy();
            } catch (Exception e) {
                first = first == null ? e : first;
            } finally {
                registryServer = null;
            }
        }

        log.info("Shutting down rmi server");
        if (rmiServer != null) {
            try {
                rmiServer.destroy();
            } catch (Exception e) {
                first = first == null ? e : first;
            } finally {
                rmiServer = null;
            }
        }

        synchronized (this) {
            notifyAll();
        }

        if (first != null) {
            throw first;
        }
    }

    public void join() throws InterruptedException {
        synchronized (this) {
            wait();
        }
    }

    public void setRepositoryUri(String repositoryUri) {
        if (repositoryUri == null || repositoryUri.trim().length() == 0) {
            this.repositoryUri = DEFAULT_REPOSITORY_URI;
        } else {
            this.repositoryUri = repositoryUri;
        }
    }

    public String getRepositoryUri() {
        return repositoryUri;
    }

    public String getJmsUri() {
        return jmsUri;
    }

    public void setJmsUri(String jmsProvider) {
        this.jmsUri = jmsProvider;
    }

    public String getRegistryUri() {
        if (registryServer != null) {
            return registryServer.getServiceUri();
        }
        return registryUri;
    }

    /**
     * @param startEmbeddedAgent
     *          Whether an embedded agent will be started
     */
    public void setStartEmbeddedAgent(boolean startEmbeddedAgent) {
        this.startEmbeddedAgent = startEmbeddedAgent;
    }

    /**
     * @return Whether an embedded agent will be started.
     */
    public boolean getStartEmbeddedAgent() {
        return startEmbeddedAgent;
    }

    /**
     * @return The connect uri that should be used to connect to the registry.
     */
    public String getRegistryConnectUri() {
        if (registryServer != null) {
            return registryServer.getServiceUri();
        } else {
            return registryUri;
        }
    }

    public void setRegistryUri(String registryProvider) {
        this.registryUri = registryProvider;
    }

    public void setDirectory(String directory) {
        this.directory = directory;
    }

    public String getDirectory() {
        return directory;
    }

    /**
     * @return
     */
    public MeshKeeper getMeshKeeper() {
        // TODO Auto-generated method stub
        return meshKeeper;
    }
}