Nixed the notion of a ConnectionObserver. The only observer was the

ClientManager and it is already pretty tightly coupled to the ConnectionManager
so we weren't really fooling anyone with that ham-fisted attempt at
abstraction.

Also cleaned up more mid-shutdown behavior. If a session is unmapped after the
omgr exits, avoid generating a warning by trying to hop onto the omgr thread to
clean up after ourselves. We still do all the actual socket related closing so
that the party on the other end of the socket benefits from a clean shutdown
where possible.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5537 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2008-11-11 01:52:29 +00:00
parent 2c0ca57551
commit a0cc2fd348
4 changed files with 45 additions and 165 deletions
@@ -45,8 +45,6 @@ import com.threerings.presents.net.AuthResponse;
import com.threerings.presents.net.Credentials;
import com.threerings.presents.server.net.AuthingConnection;
import com.threerings.presents.server.net.Connection;
import com.threerings.presents.server.net.ConnectionManager;
import com.threerings.presents.server.net.ConnectionObserver;
import static com.threerings.presents.Log.log;
@@ -61,8 +59,7 @@ import static com.threerings.presents.Log.log;
*/
@Singleton
public class ClientManager
implements ConnectionObserver, ClientResolutionListener,
ReportManager.Reporter, ShutdownManager.Shutdowner
implements ClientResolutionListener, ReportManager.Reporter, ShutdownManager.Shutdowner
{
/**
* Used by {@link ClientManager#applyToClient}.
@@ -100,11 +97,8 @@ public class ClientManager
/**
* Constructs a client manager that will interact with the supplied connection manager.
*/
@Inject public ClientManager (ConnectionManager conmgr, ReportManager repmgr,
ShutdownManager shutmgr)
@Inject public ClientManager (ReportManager repmgr, ShutdownManager shutmgr)
{
// register as a connection observer, a "state of server" reporter and a shutdowner
conmgr.addConnectionObserver(this);
repmgr.registerReporter(this);
shutmgr.registerShutdowner(this);
@@ -367,7 +361,9 @@ public class ClientManager
_penders.remove(username);
}
// from interface ConnectionObserver
/**
* Called by the connection manager to let us know when a new connection has been established.
*/
public synchronized void connectionEstablished (
Connection conn, AuthRequest req, AuthResponse rsp)
{
@@ -397,7 +393,9 @@ public class ClientManager
_conmap.put(conn, client);
}
// from interface ConnectionObserver
/**
* Called by the connection manager to let us know when a connection has failed.
*/
public synchronized void connectionFailed (Connection conn, IOException fault)
{
// remove the client from the connection map
@@ -415,7 +413,9 @@ public class ClientManager
}
}
// from interface ConnectionObserver
/**
* Called by the connection manager to let us know when a connection has been closed.
*/
public synchronized void connectionClosed (Connection conn)
{
// remove the client from the connection map
@@ -734,15 +734,19 @@ public class PresentsSession
// clear out our connection reference
setConnection(null);
// clear out our subscriptions. we need to do this on the dobjmgr thread. it is important
// that we do this *after* we clear out our connection reference. once the connection ref
// is null, no more subscriptions will be processed (even those that were queued up before
// the connection went away)
_omgr.postRunnable(new Runnable() {
public void run () {
sessionConnectionClosed();
}
});
// if we are being closed after the omgr has shutdown, then just stop here; the whole world
// is about to come to a screeching halt anyway
if (_omgr.isRunning()) {
// clear out our subscriptions: we need to do this on the dobjmgr thread. it is
// important that we do this *after* we clear out our connection reference. once the
// connection ref is null, no more subscriptions will be processed (even those that
// were queued up before the connection went away)
_omgr.postRunnable(new Runnable() {
public void run () {
sessionConnectionClosed();
}
});
}
}
/**
@@ -25,7 +25,6 @@ import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -41,7 +40,6 @@ import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -68,6 +66,7 @@ import com.threerings.presents.net.AuthResponse;
import com.threerings.presents.net.Message;
import com.threerings.presents.server.Authenticator;
import com.threerings.presents.server.ChainedAuthenticator;
import com.threerings.presents.server.ClientManager;
import com.threerings.presents.server.DummyAuthenticator;
import com.threerings.presents.server.PresentsDObjectMgr;
import com.threerings.presents.server.ReportManager;
@@ -162,29 +161,6 @@ public class ConnectionManager extends LoopingThread
return (ConMgrStats)_stats.clone();
}
/**
* Adds the specified connection observer to the observers list. Connection observers will be
* notified of connection-related events. An observer will not be added to the list twice.
*
* @see ConnectionObserver
*/
public void addConnectionObserver (ConnectionObserver observer)
{
synchronized (_observers) {
_observers.add(observer);
}
}
/**
* Removes the specified connection observer from the observers list.
*/
public void removeConnectionObserver (ConnectionObserver observer)
{
synchronized (_observers) {
_observers.remove(observer);
}
}
/**
* Opens an outgoing connection to the supplied address. The connection will be opened in a
* non-blocking manner and added to the connection manager's select set. Messages posted to the
@@ -350,31 +326,6 @@ public class ConnectionManager extends LoopingThread
}
}
/**
* Notifies the connection observers of a connection event. Used internally.
*/
protected void notifyObservers (
int code, Connection conn, Object arg1, Object arg2)
{
synchronized (_observers) {
for (ConnectionObserver obs : _observers) {
switch (code) {
case CONNECTION_ESTABLISHED:
obs.connectionEstablished(conn, (AuthRequest)arg1, (AuthResponse)arg2);
break;
case CONNECTION_FAILED:
obs.connectionFailed(conn, (IOException)arg1);
break;
case CONNECTION_CLOSED:
obs.connectionClosed(conn);
break;
default:
throw new RuntimeException("Invalid code supplied to notifyObservers: " + code);
}
}
}
}
@Override
protected void willStart ()
{
@@ -497,9 +448,9 @@ public class ConnectionManager extends LoopingThread
@Override
protected void iterate ()
{
long iterStamp = System.currentTimeMillis();
final long iterStamp = System.currentTimeMillis();
// note whether or not we're generating
// note whether or not we're generating a debug report
boolean generateDebugReport = (iterStamp - _lastDebugStamp > DEBUG_REPORT_INTERVAL);
if (DEBUG_REPORT && generateDebugReport) {
_lastDebugStamp = iterStamp;
@@ -515,12 +466,6 @@ public class ConnectionManager extends LoopingThread
}
}
// start up any outgoing connections that need to be connected
Tuple<Connection, InetSocketAddress> pconn;
while ((pconn = _connectq.getNonBlocking()) != null) {
startOutgoingConnection(pconn.left, pconn.right);
}
// close connections that have had no network traffic for too long
for (NetEventHandler handler : _handlers.values()) {
if (handler.checkIdle(iterStamp)) {
@@ -532,11 +477,17 @@ public class ConnectionManager extends LoopingThread
// send any messages that are waiting on the outgoing overflow and message queues
sendOutgoingMessages(iterStamp);
// if we have been shutdown, but we're still around because the DObjectManager is still
// running (and we want to deliver any outgoing events queued up during shutdown), then we
// don't process authenticated connections or read incoming network events; the only thing
// we want to do during the shutdown phase is send outgoing messages to existing clients
// we may be in the middle of shutting down (in which case super.isRunning() is false but
// isRunning() is true); this is because we stick around until the dobject manager is
// totally done so that we can send shutdown-related events out to our clients; during
// those last moments we don't want to accept new connections or read any incoming messages
if (super.isRunning()) {
// start up any outgoing connections that need to be connected
Tuple<Connection, InetSocketAddress> pconn;
while ((pconn = _connectq.getNonBlocking()) != null) {
startOutgoingConnection(pconn.left, pconn.right);
}
// check for connections that have completed authentication
processAuthedConnections(iterStamp);
@@ -551,7 +502,7 @@ public class ConnectionManager extends LoopingThread
/**
* Converts connections that have completed the authentication process into full running
* connections and notifies observers that a new connection has been established.
* connections and notifies the client manager that new connections have been established.
*/
protected void processAuthedConnections (long iterStamp)
{
@@ -581,9 +532,8 @@ public class ConnectionManager extends LoopingThread
_oflowqs.put(rconn, oflowHandler);
}
// and let our observers know about our new connection
notifyObservers(CONNECTION_ESTABLISHED, rconn,
conn.getAuthRequest(), conn.getAuthResponse());
// and let the client manager know about our new connection
_clmgr.connectionEstablished(rconn, conn.getAuthRequest(), conn.getAuthResponse());
} catch (IOException ioe) {
log.warning("Failure upgrading authing connection to running.", ioe);
@@ -1028,8 +978,8 @@ public class ConnectionManager extends LoopingThread
_stats.disconnects++;
}
// let our observers know what's up
notifyObservers(CONNECTION_FAILED, conn, ioe, null);
// let the client manager know what's up
_clmgr.connectionFailed(conn, ioe);
}
/**
@@ -1046,8 +996,8 @@ public class ConnectionManager extends LoopingThread
_stats.closes++;
}
// let our observers know what's up
notifyObservers(CONNECTION_CLOSED, conn, null, null);
// let the client manager know what's up
_clmgr.connectionClosed(conn);
}
@Override
@@ -1236,7 +1186,6 @@ public class ConnectionManager extends LoopingThread
protected ByteBuffer _databuf = ByteBuffer.allocateDirect(Client.MAX_DATAGRAM_SIZE);
protected Map<Connection, OverflowQueue> _oflowqs = Maps.newHashMap();
protected List<ConnectionObserver> _observers = Lists.newArrayList();
/** Our current runtime stats. */
protected ConMgrStats _stats;
@@ -1253,6 +1202,7 @@ public class ConnectionManager extends LoopingThread
// some dependencies
@Inject @AuthInvoker protected Invoker _authInvoker;
@Inject protected PresentsDObjectMgr _omgr;
@Inject protected ClientManager _clmgr;
@Inject protected ShutdownManager _shutmgr;
/** How long we wait for network events before checking our running flag to see if we should
@@ -1261,11 +1211,6 @@ public class ConnectionManager extends LoopingThread
* the queue. */
protected static final int SELECT_LOOP_TIME = 100;
// codes for notifyObservers()
protected static final int CONNECTION_ESTABLISHED = 0;
protected static final int CONNECTION_FAILED = 1;
protected static final int CONNECTION_CLOSED = 2;
/** Used to denote asynchronous close requests. */
protected static final byte[] ASYNC_CLOSE_REQUEST = new byte[0];
@@ -1,69 +0,0 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/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.net;
import java.io.IOException;
import com.threerings.presents.net.AuthRequest;
import com.threerings.presents.net.AuthResponse;
/**
* A connection observer can be registered with the connection manager to
* hear about new connections and to be notified when connections fail or
* are closed. Only fully authenticated connections will be passed on to
* the connection observer. Connections that fail to authenticate will be
* handled entirely within the confines of the connection manager.
*
* @see ConnectionManager
* @see Connection
*/
public interface ConnectionObserver
{
/**
* Called when a new connection is established with the connection
* manager. Only fully authenticated connections will be passed on to
* the connection observer.
*
* @param conn The newly established connection.
* @param req The auth request provided by the client.
* @param rsp The auth response provided to the client.
*/
void connectionEstablished (Connection conn, AuthRequest req, AuthResponse rsp);
/**
* Called if a connection fails for any reason. If a connection fails,
* <code>connectionClosed</code> will not be called. This call to
* <code>connectionFailed</code> is the last the observers will hear
* about it.
*
* @param conn The connection in that failed.
* @param fault The exception associated with the failure.
*/
void connectionFailed (Connection conn, IOException fault);
/**
* Called when a connection has been closed in an orderly manner.
*
* @param conn The recently closed connection.
*/
void connectionClosed (Connection conn);
}