All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.threerings.presents.server.PresentsServer Maven / Gradle / Ivy

//
// $Id: PresentsServer.java 6625 2011-04-12 01:23:53Z andrzej $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2011 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.presents.server;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Singleton;

import com.samskivert.util.Invoker;
import com.samskivert.util.Lifecycle;
import com.samskivert.util.RunQueue;
import com.samskivert.util.SystemInfo;

import com.threerings.presents.annotation.AuthInvoker;
import com.threerings.presents.annotation.EventQueue;
import com.threerings.presents.annotation.MainInvoker;
import com.threerings.presents.annotation.PeerInvoker;
import com.threerings.presents.client.Client;
import com.threerings.presents.dobj.AccessController;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.presents.dobj.RootDObjectManager;
import com.threerings.presents.server.PresentsDObjectMgr.LongRunnable;
import com.threerings.presents.server.net.DatagramChannelReader;
import com.threerings.presents.server.net.PresentsConnectionManager;

import com.threerings.crowd.server.PlaceManager;

import com.threerings.nio.conman.ConnectionManager;
import com.threerings.nio.conman.ServerSocketChannelAcceptor;

import static com.threerings.presents.Log.log;

/**
 * The presents server provides a central point of access to the various facilities that make up
 * the presents framework. To facilitate extension and customization, a single instance of the
 * presents server should be created and initialized in a process. To facilitate easy access to the
 * services provided by the presents server, static references to the various managers are made
 * available in the PresentsServer class. These will be configured when the singleton
 * instance is initialized.
 */
@Singleton
public class PresentsServer
{
    /** Configures dependencies needed by the Presents services. */
    public static class PresentsModule extends AbstractModule
    {
        @Override protected void configure () {
            bindInvokers();
            bindPeerInvoker();
            bind(ConnectionManager.class).to(PresentsConnectionManager.class);
            bind(RunQueue.class).annotatedWith(EventQueue.class).to(PresentsDObjectMgr.class);
            bind(DObjectManager.class).to(PresentsDObjectMgr.class);
            bind(RootDObjectManager.class).to(PresentsDObjectMgr.class);
            bind(Lifecycle.class).toInstance(new Lifecycle());
        }

        /**
         * Binds just the invokers so this can be overridden if desired.
         */
        protected void bindInvokers () {
            bind(Invoker.class).annotatedWith(MainInvoker.class).to(PresentsInvoker.class);
            bind(Invoker.class).annotatedWith(AuthInvoker.class).to(PresentsAuthInvoker.class);
        }

        /**
         * Binds just the peer invoker - we separate this from the others for legacy reasons.
         */
        protected void bindPeerInvoker () {
            bind(Invoker.class).annotatedWith(PeerInvoker.class).to(
                Key.get(Invoker.class, MainInvoker.class));
        }
    }

    /**
     * Binds PresentsServer to a particular class.
     */
    public static class PresentsServerModule extends AbstractModule
    {
        public PresentsServerModule (Class serverType) {
            _serverType = serverType;
        }

        @Override protected void configure () {
            bind(PresentsServer.class).to(_serverType);
        }

        protected final Class _serverType;
    }

    /**
     * Inits and runs the PresentsServer bound in the given modules.  This blocks until the server
     * is shut down.
     */
    public static void runServer (Module... modules)
    {
        Injector injector = Guice.createInjector(modules);
        PresentsServer server = injector.getInstance(PresentsServer.class);
        try {
            // initialize the server
            server.init(injector);

            // check to see if we should load and invoke a test module before running the server
            String testmod = System.getProperty("test_module");
            if (testmod != null) {
                try {
                    log.info("Invoking test module", "mod", testmod);
                    Class tmclass = Class.forName(testmod);
                    Runnable trun = (Runnable)tmclass.newInstance();
                    trun.run();
                } catch (Exception e) {
                    log.warning("Unable to invoke test module '" + testmod + "'.", e);
                }
            }

            // start the server to running (this method call won't return until the server is shut
            // down)
            server.run();

        } catch (Throwable t) {
            log.warning("Unable to initialize server.", t);
            System.exit(-1);
        }
    }

    /**
     * The default entry point for the server.
     */
    public static void main (String[] args)
    {
        runServer(new PresentsModule());
    }

    /** Legacy static reference to the main distributed object manager. Don't use this. If you're
     * writing a game, use {@link PlaceManager#_omgr}. */
    @Deprecated public static PresentsDObjectMgr omgr;

    /** Legacy static reference to the invocation manager. Don't use this. If you're
     * writing a game, use {@link PlaceManager#_invmgr}. */
    @Deprecated public static InvocationManager invmgr;

    /**
     * Initializes all of the server services and prepares for operation.
     */
    public void init (Injector injector)
        throws Exception
    {
        // output general system information
        SystemInfo si = new SystemInfo();
        log.info("Starting up", "server", getClass().getSimpleName(), "os", si.osToString(),
            "jvm", si.jvmToString());

        registerSignalHandlers(injector);

        // initialize our deprecated legacy static references
        omgr = _omgr;
        invmgr = _invmgr;

        // configure the dobject manager with our access controller
        _omgr.setDefaultAccessController(createDefaultObjectAccessController());

        // start the main and auth invoker threads
        _invoker.start();
        _authInvoker.start();
        ((PresentsInvoker)_invoker).addInterdependentInvoker(_authInvoker);

        // provide our client manager with the injector it needs
        _clmgr.setInjector(injector);

        // Create our socket opening objects now that we know what ports we're using.
        _lifecycle.addComponent(_socketAcceptor = createSocketAcceptor());
        _datagramReader =
            new DatagramChannelReader(getDatagramHostname(), getDatagramPorts(), _conmgr);
        _lifecycle.addComponent(_datagramReader);

        // initialize the time base services
        TimeBaseProvider.init(_invmgr, _omgr);

        // make the client manager shut down before the invoker and dobj threads; this helps
        // application code to avoid long chains of shutdown constraints
        _lifecycle.addShutdownConstraint(
            _clmgr, Lifecycle.Constraint.RUNS_BEFORE, (PresentsInvoker)_invoker);

        // initialize all of our registered components
        _lifecycle.init();
    }

    protected void registerSignalHandlers (Injector injector)
    {
        // register SIGTERM, SIGINT (ctrl-c) and a SIGHUP handlers
        try {
            injector.getInstance(SunSignalHandler.class).init();
        } catch (Throwable t) {
            log.warning("Unable to register Sun signal handlers", "error", t);
        }
    }

    protected ServerSocketChannelAcceptor createSocketAcceptor ()
    {
        return new ServerSocketChannelAcceptor(getBindHostname(), getListenPorts(), _conmgr);
    }

    /**
     * Starts up all of the server services and enters the main server event loop.
     */
    public void run ()
    {
        // wait till everything in the dobjmgr queue and invokers are processed and then start the
        // connection manager
        ((PresentsInvoker)_invoker).postRunnableWhenEmpty(new Runnable() {
            public void run () {
                // Take as long as you like opening to the public
                _omgr.postRunnable(new LongRunnable() {
                    public void run () {
                        openToThePublic();
                    }
                });
            }
        });
        // invoke the dobjmgr event loop
        _omgr.run();
    }

    /**
     * Opens the server for connections after the event thread is running and all the invokers are
     * clear after starting up.
     */
    protected void openToThePublic ()
    {
        if (getListenPorts().length != 0 && !_socketAcceptor.bind()) {
            queueShutdown();
        } else {
            _datagramReader.bind();
            _conmgr.start();
        }
    }

    /**
     * Queues up a request to shutdown on the event thread. This method may be safely called from
     * any thread.
     */
    public void queueShutdown ()
    {
        _omgr.postRunnable(new PresentsDObjectMgr.LongRunnable() {
            public void run () {
                _lifecycle.shutdown();
            }
        });
    }

    /**
     * Defines the default object access policy for all {@link DObject} instances. The default
     * default policy is to allow all subscribers but reject all modifications by the client.
     */
    protected AccessController createDefaultObjectAccessController ()
    {
        return PresentsObjectAccess.DEFAULT;
    }

    /**
     * Returns the hostname on which the connection manager will listen for TCP traffic, or
     * null to bind to the wildcard address.
     */
    protected String getBindHostname ()
    {
        return null;
    }

    /**
     * Returns the hostname on which the connection will listen for datagram traffic, or
     * null to bind to the wildcard address.
     */
    protected String getDatagramHostname ()
    {
        return null;
    }

    /**
     * Returns the port on which the connection manager will listen for client connections or an
     * empty array to skip binding and have an external entity transfer accepted sockets into
     * the connection manager.
     */
    protected int[] getListenPorts ()
    {
        return Client.DEFAULT_SERVER_PORTS;
    }

    /**
     * Returns the ports on which the connection manager will listen for datagrams or an empty
     * array if no datagrams are desired.
     */
    protected int[] getDatagramPorts ()
    {
        return Client.DEFAULT_DATAGRAM_PORTS;
    }

    /**
     * Called once the invoker and distributed object manager have both completed processing all
     * remaining events and are fully shutdown. Note: this is called as the last act of
     * the invoker on the invoker thread. In theory no other (important) threads are
     * running, so thread safety should not be an issue, but be careful!
     */
    protected void invokerDidShutdown ()
    {
    }

    protected ServerSocketChannelAcceptor _socketAcceptor;

    protected DatagramChannelReader _datagramReader;

    /** The manager of distributed objects. */
    @Inject protected PresentsDObjectMgr _omgr;

    /** The manager of network connections. */
    @Inject protected PresentsConnectionManager _conmgr;

    /** The manager of clients. */
    @Inject protected ClientManager _clmgr;

    /** The manager of invocation services. */
    @Inject protected InvocationManager _invmgr;

    /** Handles orderly initialization and shutdown of our managers, etc. */
    @Inject protected Lifecycle _lifecycle;

    /** Used to invoke background tasks that should not be allowed to tie up the distributed object
     * manager thread (generally talking to databases and other relatively slow entities). */
    @Inject @MainInvoker protected Invoker _invoker;

    /** Used to invoke authentication tasks. */
    @Inject @AuthInvoker protected Invoker _authInvoker;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy