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