159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/* 259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Copyright (c) 2011 jMonkeyEngine 359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * All rights reserved. 459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Redistribution and use in source and binary forms, with or without 659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * modification, are permitted provided that the following conditions are 759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * met: 859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * * Redistributions of source code must retain the above copyright 1059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * notice, this list of conditions and the following disclaimer. 1159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 1259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * * Redistributions in binary form must reproduce the above copyright 1359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * notice, this list of conditions and the following disclaimer in the 1459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * documentation and/or other materials provided with the distribution. 1559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 1659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 1759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * may be used to endorse or promote products derived from this software 1859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * without specific prior written permission. 1959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 2059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 2459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 2559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 2659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 2759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 2859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 2959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 3059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 3259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 3359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapackage com.jme3.network.base; 3459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 3559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.network.*; 3659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.network.kernel.Endpoint; 3759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.network.kernel.Kernel; 3859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.network.message.ChannelInfoMessage; 3959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.network.message.ClientRegistrationMessage; 4059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.network.message.DisconnectMessage; 4159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.io.IOException; 4259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.nio.ByteBuffer; 4359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.*; 4459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.concurrent.ConcurrentHashMap; 4559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.concurrent.CopyOnWriteArrayList; 4659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.concurrent.atomic.AtomicInteger; 4759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Level; 4859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Logger; 4959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 5059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/** 5159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * A default implementation of the Server interface that delegates 5259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * its network connectivity to kernel.Kernel. 5359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 5459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @version $Revision: 9114 $ 5559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @author Paul Speed 5659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 5759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapublic class DefaultServer implements Server 5859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta{ 5959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta static Logger log = Logger.getLogger(DefaultServer.class.getName()); 6059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 6159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // First two channels are reserved for reliable and 6259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // unreliable 6359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private static final int CH_RELIABLE = 0; 6459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private static final int CH_UNRELIABLE = 1; 6559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private static final int CH_FIRST = 2; 6659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 6759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private boolean isRunning = false; 6859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private AtomicInteger nextId = new AtomicInteger(0); 6959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private String gameName; 7059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private int version; 7159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private KernelFactory kernelFactory = KernelFactory.DEFAULT; 7259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private KernelAdapter reliableAdapter; 7359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private KernelAdapter fastAdapter; 7459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private List<KernelAdapter> channels = new ArrayList<KernelAdapter>(); 7559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private List<Integer> alternatePorts = new ArrayList<Integer>(); 7659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Redispatch dispatcher = new Redispatch(); 7759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Map<Integer,HostedConnection> connections = new ConcurrentHashMap<Integer,HostedConnection>(); 7859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Map<Endpoint,HostedConnection> endpointConnections 7959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta = new ConcurrentHashMap<Endpoint,HostedConnection>(); 8059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 8159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Keeps track of clients for whom we've only received the UDP 8259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // registration message 8359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Map<Long,Connection> connecting = new ConcurrentHashMap<Long,Connection>(); 8459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 8559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private MessageListenerRegistry<HostedConnection> messageListeners 8659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta = new MessageListenerRegistry<HostedConnection>(); 8759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private List<ConnectionListener> connectionListeners = new CopyOnWriteArrayList<ConnectionListener>(); 8859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 8959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public DefaultServer( String gameName, int version, Kernel reliable, Kernel fast ) 9059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 9159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( reliable == null ) 9259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta throw new IllegalArgumentException( "Default server reqiures a reliable kernel instance." ); 9359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 9459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.gameName = gameName; 9559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.version = version; 9659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 9759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta reliableAdapter = new KernelAdapter( this, reliable, dispatcher, true ); 9859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta channels.add( reliableAdapter ); 9959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( fast != null ) { 10059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta fastAdapter = new KernelAdapter( this, fast, dispatcher, false ); 10159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta channels.add( fastAdapter ); 10259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 10359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 10459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 10559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public String getGameName() 10659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 10759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return gameName; 10859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 10959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 11059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public int getVersion() 11159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 11259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return version; 11359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 11459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 11559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public int addChannel( int port ) 11659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 11759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( isRunning ) 11859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta throw new IllegalStateException( "Channels cannot be added once server is started." ); 11959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 12059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Note: it does bug me that channels aren't 100% universal and 12159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // setup externally but it requires a more invasive set of changes 12259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // for "connection types" and some kind of registry of kernel and 12359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // connector factories. This really would be the best approach and 12459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // would allow all kinds of channel customization maybe... but for 12559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // now, we hard-code the standard connections and treat the +2 extras 12659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // differently. 12759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 12859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Check for consistency with the channels list 12959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( channels.size() - CH_FIRST != alternatePorts.size() ) 13059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta throw new IllegalStateException( "Channel and port lists do not match." ); 13159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 13259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta try { 13359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int result = alternatePorts.size(); 13459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta alternatePorts.add(port); 13559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 13659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Kernel kernel = kernelFactory.createKernel(result, port); 13759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta channels.add( new KernelAdapter(this, kernel, dispatcher, true) ); 13859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 13959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return result; 14059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } catch( IOException e ) { 14159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta throw new RuntimeException( "Error adding channel for port:" + port, e ); 14259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 14359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 14459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 14559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected void checkChannel( int channel ) 14659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 14759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( channel < 0 || channel >= alternatePorts.size() ) 14859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta throw new IllegalArgumentException( "Channel is undefined:" + channel ); 14959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 15059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 15159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void start() 15259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 15359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( isRunning ) 15459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta throw new IllegalStateException( "Server is already started." ); 15559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 15659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Initialize the kernels 15759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for( KernelAdapter ka : channels ) { 15859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ka.initialize(); 15959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 16059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 16159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Start em up 16259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for( KernelAdapter ka : channels ) { 16359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ka.start(); 16459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 16559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 16659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta isRunning = true; 16759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 16859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 16959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public boolean isRunning() 17059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 17159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return isRunning; 17259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 17359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 17459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void close() 17559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 17659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( !isRunning ) 17759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta throw new IllegalStateException( "Server is not started." ); 17859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 17959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta try { 18059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Kill the adpaters, they will kill the kernels 18159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for( KernelAdapter ka : channels ) { 18259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ka.close(); 18359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 18459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 18559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta isRunning = false; 18659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } catch( InterruptedException e ) { 18759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta throw new RuntimeException( "Interrupted while closing", e ); 18859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 18959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 19059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 19159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void broadcast( Message message ) 19259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 19359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta broadcast( null, message ); 19459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 19559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 19659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void broadcast( Filter<? super HostedConnection> filter, Message message ) 19759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 19859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( connections.isEmpty() ) 19959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return; 20059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 20159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null); 20259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 20359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta FilterAdapter adapter = filter == null ? null : new FilterAdapter(filter); 20459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 20559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( message.isReliable() || fastAdapter == null ) { 20659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Don't need to copy the data because message protocol is already 20759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // giving us a fresh buffer 20859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta reliableAdapter.broadcast( adapter, buffer, true, false ); 20959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 21059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta fastAdapter.broadcast( adapter, buffer, false, false ); 21159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 21259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 21359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 21459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void broadcast( int channel, Filter<? super HostedConnection> filter, Message message ) 21559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 21659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( connections.isEmpty() ) 21759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return; 21859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 21959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta checkChannel(channel); 22059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 22159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null); 22259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 22359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta FilterAdapter adapter = filter == null ? null : new FilterAdapter(filter); 22459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 22559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta channels.get(channel+CH_FIRST).broadcast( adapter, buffer, true, false ); 22659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 22759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 22859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public HostedConnection getConnection( int id ) 22959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 23059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return connections.get(id); 23159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 23259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 23359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public boolean hasConnections() 23459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 23559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return !connections.isEmpty(); 23659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 23759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 23859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public Collection<HostedConnection> getConnections() 23959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 24059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return Collections.unmodifiableCollection((Collection<HostedConnection>)connections.values()); 24159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 24259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 24359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void addConnectionListener( ConnectionListener listener ) 24459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 24559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta connectionListeners.add(listener); 24659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 24759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 24859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void removeConnectionListener( ConnectionListener listener ) 24959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 25059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta connectionListeners.remove(listener); 25159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 25259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 25359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void addMessageListener( MessageListener<? super HostedConnection> listener ) 25459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 25559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta messageListeners.addMessageListener( listener ); 25659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 25759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 25859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void addMessageListener( MessageListener<? super HostedConnection> listener, Class... classes ) 25959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 26059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta messageListeners.addMessageListener( listener, classes ); 26159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 26259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 26359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void removeMessageListener( MessageListener<? super HostedConnection> listener ) 26459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 26559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta messageListeners.removeMessageListener( listener ); 26659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 26759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 26859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void removeMessageListener( MessageListener<? super HostedConnection> listener, Class... classes ) 26959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 27059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta messageListeners.removeMessageListener( listener, classes ); 27159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 27259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 27359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected void dispatch( HostedConnection source, Message m ) 27459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 27559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( source == null ) { 27659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta messageListeners.messageReceived( source, m ); 27759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 27859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 27959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // A semi-heavy handed way to make sure the listener 28059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // doesn't get called at the same time from two different 28159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // threads for the same hosted connection. 28259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta synchronized( source ) { 28359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta messageListeners.messageReceived( source, m ); 28459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 28559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 28659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 28759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 28859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected void fireConnectionAdded( HostedConnection conn ) 28959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 29059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for( ConnectionListener l : connectionListeners ) { 29159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta l.connectionAdded( this, conn ); 29259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 29359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 29459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 29559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected void fireConnectionRemoved( HostedConnection conn ) 29659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 29759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for( ConnectionListener l : connectionListeners ) { 29859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta l.connectionRemoved( this, conn ); 29959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 30059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 30159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 30259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected int getChannel( KernelAdapter ka ) 30359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 30459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return channels.indexOf(ka); 30559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 30659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 30759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected void registerClient( KernelAdapter ka, Endpoint p, ClientRegistrationMessage m ) 30859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 30959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Connection addedConnection = null; 31059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 31159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // generally this will only be called by one thread but it's 31259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // important enough I won't take chances 31359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta synchronized( this ) { 31459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Grab the random ID that the client created when creating 31559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // its two registration messages 31659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta long tempId = m.getId(); 31759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 31859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // See if we already have one 31959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Connection c = connecting.remove(tempId); 32059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( c == null ) { 32159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta c = new Connection(channels.size()); 32259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta log.log( Level.FINE, "Registering client for endpoint, pass 1:{0}.", p ); 32359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 32459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta log.log( Level.FINE, "Refining client registration for endpoint:{0}.", p ); 32559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 32659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 32759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Fill in what we now know 32859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int channel = getChannel(ka); 32959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta c.setChannel(channel, p); 33059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta log.log( Level.FINE, "Setting up channel:{0}", channel ); 33159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 33259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // If it's channel 0 then this is the initial connection 33359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // and we will send the connection information 33459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( channel == CH_RELIABLE ) { 33559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Validate the name and version which is only sent 33659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // over the reliable connection at this point. 33759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( !getGameName().equals(m.getGameName()) 33859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta || getVersion() != m.getVersion() ) { 33959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 34059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta log.log( Level.INFO, "Kicking client due to name/version mismatch:{0}.", c ); 34159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 34259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Need to kick them off... I may regret doing this from within 34359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // the sync block but the alternative is more code 34459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta c.close( "Server client mismatch, server:" + getGameName() + " v" + getVersion() 34559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta + " client:" + m.getGameName() + " v" + m.getVersion() ); 34659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return; 34759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 34859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 34959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Else send the extra channel information to the client 35059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( !alternatePorts.isEmpty() ) { 35159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ChannelInfoMessage cim = new ChannelInfoMessage( m.getId(), alternatePorts ); 35259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta c.send(cim); 35359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 35459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 35559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 35659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( c.isComplete() ) { 35759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Then we are fully connected 35859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( connections.put( c.getId(), c ) == null ) { 35959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 36059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for( Endpoint cp : c.channels ) { 36159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( cp == null ) 36259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta continue; 36359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta endpointConnections.put( cp, c ); 36459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 36559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 36659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta addedConnection = c; 36759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 36859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 36959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Need to keep getting channels so we'll keep it in 37059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // the map 37159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta connecting.put(tempId, c); 37259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 37359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 37459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 37559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Best to do this outside of the synch block to avoid 37659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // over synchronizing which is the path to deadlocks 37759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( addedConnection != null ) { 37859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta log.log( Level.INFO, "Client registered:{0}.", addedConnection ); 37959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 38059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Send the ID back to the client letting it know it's 38159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // fully connected. 38259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta m = new ClientRegistrationMessage(); 38359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta m.setId( addedConnection.getId() ); 38459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta m.setReliable(true); 38559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta addedConnection.send(m); 38659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 38759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Now we can notify the listeners about the 38859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // new connection. 38959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta fireConnectionAdded( addedConnection ); 39059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 39159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 39259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 39359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected HostedConnection getConnection( Endpoint endpoint ) 39459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 39559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return endpointConnections.get(endpoint); 39659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 39759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 39859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected void connectionClosed( Endpoint p ) 39959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 40059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( p.isConnected() ) { 40159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta log.log( Level.INFO, "Connection closed:{0}.", p ); 40259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 40359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta log.log( Level.FINE, "Connection closed:{0}.", p ); 40459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 40559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 40659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Try to find the endpoint in all ways that it might 40759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // exist. Note: by this point the raw network channel is 40859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // closed already. 40959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 41059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Also note: this method will be called multiple times per 41159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // HostedConnection if it has multiple endpoints. 41259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 41359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Connection removed = null; 41459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta synchronized( this ) { 41559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Just in case the endpoint was still connecting 41659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta connecting.values().remove(p); 41759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 41859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // And the regular management 41959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta removed = (Connection)endpointConnections.remove(p); 42059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( removed != null ) { 42159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta connections.remove( removed.getId() ); 42259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 42359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 42459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta log.log( Level.FINE, "Connections size:{0}", connections.size() ); 42559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta log.log( Level.FINE, "Endpoint mappings size:{0}", endpointConnections.size() ); 42659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 42759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 42859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Better not to fire events while we hold a lock 42959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // so always do this outside the synch block. 43059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Note: checking removed.closed just to avoid spurious log messages 43159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // since in general we are called back for every endpoint closing. 43259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( removed != null && !removed.closed ) { 43359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 43459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta log.log( Level.INFO, "Client closed:{0}.", removed ); 43559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 43659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta removed.closeConnection(); 43759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 43859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 43959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 44059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected class Connection implements HostedConnection 44159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 44259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private int id; 44359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private boolean closed; 44459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Endpoint[] channels; 44559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private int setChannelCount = 0; 44659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 44759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Map<String,Object> sessionData = new ConcurrentHashMap<String,Object>(); 44859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 44959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public Connection( int channelCount ) 45059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 45159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta id = nextId.getAndIncrement(); 45259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta channels = new Endpoint[channelCount]; 45359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 45459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 45559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta void setChannel( int channel, Endpoint p ) 45659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 45759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( channels[channel] != null && channels[channel] != p ) { 45859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta throw new RuntimeException( "Channel has already been set:" + channel 45959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta + " = " + channels[channel] + ", cannot be set to:" + p ); 46059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 46159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta channels[channel] = p; 46259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( p != null ) 46359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta setChannelCount++; 46459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 46559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 46659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta boolean isComplete() 46759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 46859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return setChannelCount == channels.length; 46959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 47059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 47159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public Server getServer() 47259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 47359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return DefaultServer.this; 47459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 47559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 47659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public int getId() 47759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 47859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return id; 47959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 48059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 48159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public String getAddress() 48259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 48359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return channels[CH_RELIABLE] == null ? null : channels[CH_RELIABLE].getAddress(); 48459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 48559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 48659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void send( Message message ) 48759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 48859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null); 48959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( message.isReliable() || channels[CH_UNRELIABLE] == null ) { 49059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta channels[CH_RELIABLE].send( buffer ); 49159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 49259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta channels[CH_UNRELIABLE].send( buffer ); 49359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 49459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 49559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 49659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void send( int channel, Message message ) 49759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 49859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta checkChannel(channel); 49959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ByteBuffer buffer = MessageProtocol.messageToBuffer(message, null); 50059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta channels[channel+CH_FIRST].send(buffer); 50159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 50259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 50359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected void closeConnection() 50459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 50559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( closed ) 50659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return; 50759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta closed = true; 50859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 50959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Make sure all endpoints are closed. Note: reliable 51059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // should always already be closed through all paths that I 51159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // can conceive... but it doesn't hurt to be sure. 51259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for( Endpoint p : channels ) { 51359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( p == null ) 51459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta continue; 51559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta p.close(); 51659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 51759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 51859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta fireConnectionRemoved( this ); 51959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 52059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 52159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void close( String reason ) 52259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 52359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Send a reason 52459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta DisconnectMessage m = new DisconnectMessage(); 52559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta m.setType( DisconnectMessage.KICK ); 52659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta m.setReason( reason ); 52759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta m.setReliable( true ); 52859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta send( m ); 52959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 53059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Just close the reliable endpoint 53159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // fast will be cleaned up as a side-effect 53259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // when closeConnection() is called by the 53359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // connectionClosed() endpoint callback. 53459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( channels[CH_RELIABLE] != null ) { 53559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // Close with flush so we make sure our 53659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // message gets out 53759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta channels[CH_RELIABLE].close(true); 53859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 53959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 54059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 54159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public Object setAttribute( String name, Object value ) 54259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 54359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( value == null ) 54459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return sessionData.remove(name); 54559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return sessionData.put(name, value); 54659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 54759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 54859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta @SuppressWarnings("unchecked") 54959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public <T> T getAttribute( String name ) 55059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 55159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return (T)sessionData.get(name); 55259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 55359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 55459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public Set<String> attributeNames() 55559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 55659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return Collections.unmodifiableSet(sessionData.keySet()); 55759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 55859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 55959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public String toString() 56059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 56159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return "Connection[ id=" + id + ", reliable=" + channels[CH_RELIABLE] 56259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta + ", fast=" + channels[CH_UNRELIABLE] + " ]"; 56359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 56459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 56559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 56659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected class Redispatch implements MessageListener<HostedConnection> 56759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 56859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void messageReceived( HostedConnection source, Message m ) 56959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 57059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta dispatch( source, m ); 57159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 57259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 57359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 57459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected class FilterAdapter implements Filter<Endpoint> 57559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 57659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Filter<? super HostedConnection> delegate; 57759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 57859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public FilterAdapter( Filter<? super HostedConnection> delegate ) 57959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 58059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.delegate = delegate; 58159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 58259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 58359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public boolean apply( Endpoint input ) 58459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta { 58559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta HostedConnection conn = getConnection( input ); 58659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if( conn == null ) 58759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return false; 58859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return delegate.apply(conn); 58959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 59059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 59159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta} 592