Reworked how client connection teardown happens in the face
of the network connection disappearing or any other random errors on the channel. Now errors that cause the connection to drop will be properly reported as client disconnects... there is also a new error field on the DisconnectInfo that is filled in in these cases. Added an ErrorListener that can be used to more tightly control how errors are handled for the Client. Fixed the double event dispatch that occurred during Client closing. git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7451 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
95315cfd0c
commit
8ed2db29a6
engine/src/networking/com/jme3/network
@ -119,6 +119,19 @@ public interface Client extends MessageConnection
|
||||
*/
|
||||
public void removeMessageListener( MessageListener<? super Client> listener, Class... classes );
|
||||
|
||||
/**
|
||||
* Adds a listener that will be notified when any connection errors
|
||||
* occur. If a client has no error listeners then the default behavior
|
||||
* is to close the connection and provide an appropriate DisconnectInfo
|
||||
* to any ClientStateListeners. If the application adds its own error
|
||||
* listeners then it must take care of closing the connection itself.
|
||||
*/
|
||||
public void addErrorListener( ErrorListener<? super Client> listener );
|
||||
|
||||
/**
|
||||
* Removes a previously registered error listener.
|
||||
*/
|
||||
public void removeErrorListener( ErrorListener<? super Client> listener );
|
||||
}
|
||||
|
||||
|
||||
|
@ -63,5 +63,6 @@ public interface ClientStateListener
|
||||
public class DisconnectInfo
|
||||
{
|
||||
public String reason;
|
||||
public Throwable error;
|
||||
}
|
||||
}
|
||||
|
45
engine/src/networking/com/jme3/network/ErrorListener.java
Normal file
45
engine/src/networking/com/jme3/network/ErrorListener.java
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2011 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.jme3.network;
|
||||
|
||||
|
||||
/**
|
||||
* Notified when errors happen on a connection.
|
||||
*
|
||||
* @version $Revision$
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public interface ErrorListener<S>
|
||||
{
|
||||
public void handleError( S source, Throwable t );
|
||||
}
|
@ -38,6 +38,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import com.jme3.network.ErrorListener;
|
||||
import com.jme3.network.Message;
|
||||
import com.jme3.network.MessageListener;
|
||||
import com.jme3.network.kernel.Connector;
|
||||
@ -65,6 +66,7 @@ public class ConnectorAdapter extends Thread
|
||||
{
|
||||
private Connector connector;
|
||||
private MessageListener<Object> dispatcher;
|
||||
private ErrorListener<Object> errorHandler;
|
||||
private AtomicBoolean go = new AtomicBoolean(true);
|
||||
|
||||
// Writes messages out on a background thread
|
||||
@ -74,11 +76,13 @@ public class ConnectorAdapter extends Thread
|
||||
// through this connector.
|
||||
private boolean reliable;
|
||||
|
||||
public ConnectorAdapter( Connector connector, MessageListener<Object> dispatcher, boolean reliable )
|
||||
public ConnectorAdapter( Connector connector, MessageListener<Object> dispatcher,
|
||||
ErrorListener<Object> errorHandler, boolean reliable )
|
||||
{
|
||||
super( String.valueOf(connector) );
|
||||
this.connector = connector;
|
||||
this.dispatcher = dispatcher;
|
||||
this.errorHandler = errorHandler;
|
||||
this.reliable = reliable;
|
||||
setDaemon(true);
|
||||
writer = Executors.newFixedThreadPool(1,
|
||||
@ -106,10 +110,19 @@ public class ConnectorAdapter extends Thread
|
||||
writer.execute( new MessageWriter(data) );
|
||||
}
|
||||
|
||||
protected void handleError( Exception e )
|
||||
{
|
||||
if( !go.get() )
|
||||
return;
|
||||
|
||||
errorHandler.handleError( this, e );
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
MessageProtocol protocol = new MessageProtocol();
|
||||
|
||||
try {
|
||||
while( go.get() ) {
|
||||
ByteBuffer buffer = connector.read();
|
||||
if( buffer == null ) {
|
||||
@ -130,6 +143,9 @@ public class ConnectorAdapter extends Thread
|
||||
dispatch( m );
|
||||
}
|
||||
}
|
||||
} catch( Exception e ) {
|
||||
handleError( e );
|
||||
}
|
||||
}
|
||||
|
||||
protected class MessageWriter implements Runnable
|
||||
@ -145,7 +161,12 @@ public class ConnectorAdapter extends Thread
|
||||
{
|
||||
if( !go.get() )
|
||||
return;
|
||||
|
||||
try {
|
||||
connector.write(data);
|
||||
} catch( Exception e ) {
|
||||
handleError( e );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,7 @@ public class DefaultClient implements Client
|
||||
private Connector fast;
|
||||
private MessageListenerRegistry<Client> messageListeners = new MessageListenerRegistry<Client>();
|
||||
private List<ClientStateListener> stateListeners = new CopyOnWriteArrayList<ClientStateListener>();
|
||||
private List<ErrorListener<? super Client>> errorListeners = new CopyOnWriteArrayList<ErrorListener<? super Client>>();
|
||||
private Redispatch dispatcher = new Redispatch();
|
||||
private ConnectorAdapter reliableAdapter;
|
||||
private ConnectorAdapter fastAdapter;
|
||||
@ -93,9 +94,9 @@ public class DefaultClient implements Client
|
||||
|
||||
this.reliable = reliable;
|
||||
this.fast = fast;
|
||||
reliableAdapter = new ConnectorAdapter(reliable, dispatcher, true);
|
||||
reliableAdapter = new ConnectorAdapter(reliable, dispatcher, dispatcher, true);
|
||||
if( fast != null ) {
|
||||
fastAdapter = new ConnectorAdapter(fast, dispatcher, false);
|
||||
fastAdapter = new ConnectorAdapter(fast, dispatcher, dispatcher, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,6 +232,14 @@ public class DefaultClient implements Client
|
||||
{
|
||||
checkRunning();
|
||||
|
||||
closeConnections( null );
|
||||
}
|
||||
|
||||
protected void closeConnections( DisconnectInfo info )
|
||||
{
|
||||
if( !isRunning )
|
||||
return;
|
||||
|
||||
// Send a close message
|
||||
|
||||
// Tell the thread it's ok to die
|
||||
@ -246,7 +255,7 @@ public class DefaultClient implements Client
|
||||
// Just in case we never fully connected
|
||||
connecting.countDown();
|
||||
|
||||
fireDisconnected(null);
|
||||
fireDisconnected(info);
|
||||
|
||||
isRunning = false;
|
||||
}
|
||||
@ -281,6 +290,16 @@ public class DefaultClient implements Client
|
||||
messageListeners.removeMessageListener( listener, classes );
|
||||
}
|
||||
|
||||
public void addErrorListener( ErrorListener<? super Client> listener )
|
||||
{
|
||||
errorListeners.add( listener );
|
||||
}
|
||||
|
||||
public void removeErrorListener( ErrorListener<? super Client> listener )
|
||||
{
|
||||
errorListeners.remove( listener );
|
||||
}
|
||||
|
||||
protected void fireConnected()
|
||||
{
|
||||
for( ClientStateListener l : stateListeners ) {
|
||||
@ -295,6 +314,27 @@ public class DefaultClient implements Client
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Either calls the ErrorListener or closes the connection
|
||||
* if there are no listeners.
|
||||
*/
|
||||
protected void handleError( Throwable t )
|
||||
{
|
||||
// If there are no listeners then close the connection with
|
||||
// a reason
|
||||
if( errorListeners.isEmpty() ) {
|
||||
DisconnectInfo info = new DisconnectInfo();
|
||||
info.reason = "Connection Error";
|
||||
info.error = t;
|
||||
closeConnections(info);
|
||||
return;
|
||||
}
|
||||
|
||||
for( ErrorListener l : errorListeners ) {
|
||||
l.handleError( this, t );
|
||||
}
|
||||
}
|
||||
|
||||
protected void dispatch( Message m )
|
||||
{
|
||||
// Pull off the connection management messages we're
|
||||
@ -312,8 +352,7 @@ public class DefaultClient implements Client
|
||||
log.log( Level.SEVERE, "Connection terminated, reason:{0}.", reason );
|
||||
DisconnectInfo info = new DisconnectInfo();
|
||||
info.reason = reason;
|
||||
fireDisconnected(info);
|
||||
close();
|
||||
closeConnections(info);
|
||||
}
|
||||
|
||||
// Make sure client MessageListeners are called single-threaded
|
||||
@ -324,11 +363,19 @@ public class DefaultClient implements Client
|
||||
}
|
||||
}
|
||||
|
||||
protected class Redispatch implements MessageListener<Object>
|
||||
protected class Redispatch implements MessageListener<Object>, ErrorListener<Object>
|
||||
{
|
||||
public void messageReceived( Object source, Message m )
|
||||
{
|
||||
dispatch( m );
|
||||
}
|
||||
|
||||
public void handleError( Object source, Throwable t )
|
||||
{
|
||||
// Only doing the DefaultClient.this to make the code
|
||||
// checker happy... it compiles fine without it but I
|
||||
// don't like red lines in my editor. :P
|
||||
DefaultClient.this.handleError( t );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user