package org.threadly.litesockets;
import java.io.Closeable;
import java.nio.channels.SelectableChannel;
import java.util.concurrent.atomic.AtomicBoolean;
import org.threadly.concurrent.event.ListenerHelper;
/**
* This is the main Server Interface for litesockets.
*
* <p>Any type of connection/open port will use this to "Accept" new client connections on.
* The Server has an Acceptor callback for new clients and a Closer callback for clean up when the socket is closed.</p>
*
* <p>Both the {@link ClientAcceptor} and {@link ServerCloseListener} callbacks can happen on multiple threads so use thread safety when dealing
* with those callbacks.</p>
*
*
*/
public abstract class Server implements Closeable {
private final SocketExecuter sei;
private final AtomicBoolean closed = new AtomicBoolean(false);
private volatile ClientAcceptor clientAcceptor;
private volatile ListenerHelper<ServerCloseListener> closer =
new ListenerHelper<ServerCloseListener>(ServerCloseListener.class);
protected Server(final SocketExecuter sei) {
this.sei = sei;
}
/**
* <p>This is how the extending server receives the {@link SelectableChannel}.
* At this point it needs to do what is needed to turn this Channel into
* A Client object for this type of server.</p>
*
* @param c The {@link SelectableChannel} that was just accepted by this Server.
*/
protected abstract void acceptChannel(SelectableChannel c);
/**
* <p>Get the {@link SelectableChannel} used by this Server.</p>
*
* @return the {@link SelectableChannel} for this server.
*/
protected abstract SelectableChannel getSelectableChannel();
/**
* <p>Close this servers Socket. Once closed you must construct a new Server to open it again.</p>
*/
public abstract void close();
/**
* <p>This is used by the {@link SocketExecuter} to know how to handle this Server
* when its added to it. Currently only UDP or TCP.</p>
*
* @return returns the type of protocol this socket uses.
*/
public abstract WireProtocol getServerType();
protected boolean setClosed() {
return this.closed.compareAndSet(false, true);
}
/**
* <p>Get the current ServerCloser callback assigned to this Server.</p>
*
* @return the currently set Closer.
*/
protected void callClosers() {
this.closer.call().onClose(this);
}
/**
* Tells the Server to start accepting connections.
*/
public void start() {
sei.startListening(this);
}
/**
* Tells the Server to stop accepting connections. The Listen port is still open and you
* can call {@link #start()} to start listening again. Use {@link #close()} to close the Listen port.
*/
public void stop() {
sei.stopListening(this);
}
/**
* <p>Gets the Current {@link SocketExecuter} this Server is assigned to.</p>
*
* @return the current {@link SocketExecuter} for this Server.
*/
public SocketExecuter getSocketExecuter() {
return sei;
}
/**
* <p>Adds a {@link ServerCloseListener} for this Server.</p>
*
* @param listener The {@link ServerCloseListener} to set for this Server.
*/
public void addCloseListener(final ServerCloseListener listener) {
this.closer.addListener(listener);
}
/**
* <p>Gets the current {@link ClientAcceptor} Callback for this Server.</p>
*
* @return the currently set {@link ClientAcceptor}.
*/
public ClientAcceptor getClientAcceptor() {
return this.clientAcceptor;
}
/**
* <p>Set the {@link ClientAcceptor} for this Server. This should be set before the Server is added to the {@link SocketExecuter}.
* If its not you could miss pending client connections.</p>
*
* @param acceptor Sets the {@link ClientAcceptor} callback for this server.
*/
public void setClientAcceptor(final ClientAcceptor acceptor) {
clientAcceptor = acceptor;
}
/**
* Tells you if this sever objects Listen port is still open or not.
*
* @return true if the socket is closed, false if the socket is open.
*/
public boolean isClosed() {
return closed.get();
}
/**
* Used to notify the when a new {@link Client} has been created for a {@link Server}
*
* <p>NOTE: This will/can be called from many threads at once.</p>
*
*/
public interface ClientAcceptor {
/**
* This is called when a new Client is added by this {@link Server}.
*
* @param client The new {@link Client} object created.
*/
public void accept(Client client);
}
/**
* Used to notify when a {@link Server} connection is closed.
*
* <p>This is called once a Close is detected on the Servers Socket. Since it can happen on any thread as well
* you might get new clients for this server shortly after it closes.</p>
*
*
*/
public interface ServerCloseListener {
/**
* Once a close is detected for this {@link Server} this is called..
*
* @param server The {@link Server} that has been closed.
*/
public void onClose(Server server);
}
}