/**
*
*/
package vnet.sms.gateway.server.framework;
import static org.apache.commons.lang.Validate.notEmpty;
import static org.apache.commons.lang.Validate.notNull;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ServerChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import vnet.sms.gateway.server.framework.internal.channel.GatewayServerChannelPipelineFactory;
import vnet.sms.gateway.server.framework.spi.GatewayServerDescription;
/**
* @author obergner
*
*/
class GatewayServer<ID extends Serializable, TP> {
private final Logger log = LoggerFactory
.getLogger(getClass());
private final GatewayServerDescription description;
private final String instanceId;
private final SocketAddress localAddress;
private final ServerChannelFactory channelFactory;
private final GatewayServerChannelPipelineFactory<ID, TP> channelPipelineFactory;
private final InstanceState stopped = new Stopped();
private final InstanceState starting = new Starting();
private final InstanceState running = new Running();
private final InstanceState stopping = new Stopping();
private final AtomicReference<InstanceState> currentState = new AtomicReference<InstanceState>(
this.stopped);
GatewayServer(
final GatewayServerDescription description,
final String instanceId,
final int listenPort,
final GatewayServerChannelPipelineFactory<ID, TP> channelPipelineFactory,
final Executor bossExecutor, final Executor workerExecutor) {
this(description, instanceId, "127.0.0.1", listenPort,
channelPipelineFactory, bossExecutor, workerExecutor);
}
GatewayServer(
final GatewayServerDescription description,
final String instanceId,
final String host,
final int listenPort,
final GatewayServerChannelPipelineFactory<ID, TP> channelPipelineFactory,
final Executor bossExecutor, final Executor workerExecutor) {
this(
description,
instanceId,
new InetSocketAddress(host, listenPort),
new NioServerSocketChannelFactory(bossExecutor, workerExecutor),
channelPipelineFactory);
}
GatewayServer(
final GatewayServerDescription description,
final String instanceId,
final SocketAddress localAddress,
final ServerChannelFactory channelFactory,
final GatewayServerChannelPipelineFactory<ID, TP> channelPipelineFactory) {
notNull(description, "Argument 'description' must not be null");
notEmpty(instanceId,
"Argument 'instanceId' may be neither null nor empty");
notNull(channelPipelineFactory,
"Argument 'channelPipelineFactory' must not be null");
notNull(localAddress, "Argument 'localAddress' must not be null");
notNull(channelFactory, "Argument 'channelFactory' must not be null");
this.description = description;
this.instanceId = instanceId;
this.localAddress = localAddress;
this.channelFactory = channelFactory;
this.channelPipelineFactory = channelPipelineFactory;
}
void start() throws Exception {
this.currentState.get().start();
}
void stop() throws Exception {
this.currentState.get().stop();
}
ServerStatus getCurrentStatus() {
return this.currentState.get().getStatus();
}
GatewayServerDescription getDescription() {
return this.description;
}
String getInstanceId() {
return this.instanceId;
}
SocketAddress getLocalAddress() {
return this.localAddress;
}
@Override
public String toString() {
return "GatewayServer@" + hashCode() + "[name: "
+ this.description.getName() + "|version: "
+ this.description.getVersion() + "|instanceId: "
+ this.instanceId + "|localAddress: " + this.localAddress + "]";
}
public abstract class InstanceState {
private final ServerStatus status;
InstanceState(final ServerStatus name) {
this.status = name;
}
public final ServerStatus getStatus() {
return this.status;
}
abstract void start() throws Exception;
abstract void stop() throws Exception;
@Override
public abstract String toString();
}
private final class Stopped extends InstanceState {
Stopped() {
super(ServerStatus.STOPPED);
}
@Override
void start() throws Exception {
GatewayServer.this.log.info("Starting {} ...", GatewayServer.this);
final long start = System.currentTimeMillis();
if (!GatewayServer.this.currentState.compareAndSet(
GatewayServer.this.stopped, GatewayServer.this.starting)) {
GatewayServer.this.log
.warn("{} is not in state STOPPED - ignoring attempt to start",
GatewayServer.this);
return;
}
final ServerBootstrap bootstrap = new ServerBootstrap(
GatewayServer.this.channelFactory);
bootstrap
.setPipelineFactory(GatewayServer.this.channelPipelineFactory);
bootstrap.bind(GatewayServer.this.localAddress);
if (!GatewayServer.this.currentState.compareAndSet(
GatewayServer.this.starting, GatewayServer.this.running)) {
throw new IllegalStateException(
String.format(
"Expected %s to be in state STARTING - in fact, it is in state %s",
GatewayServer.this,
GatewayServer.this.currentState.get()));
}
final long end = System.currentTimeMillis();
GatewayServer.this.log.info(
"{} started in [{}] ms - listening on {}", new Object[] {
GatewayServer.this, end - start,
GatewayServer.this.localAddress });
}
@Override
void stop() throws Exception {
GatewayServer.this.log
.warn("Ignoring request to stop {} - this GatewayServer has not been started yet",
GatewayServer.this);
}
@Override
public String toString() {
return "Stopped@" + hashCode() + "[status: " + getStatus() + "]";
}
}
private final class Starting extends InstanceState {
Starting() {
super(ServerStatus.STARTING);
}
@Override
void start() throws Exception {
GatewayServer.this.log
.warn("Ignoring request to start {} - this GatewayServer is already starting",
GatewayServer.this);
}
@Override
void stop() throws Exception {
GatewayServer.this.log
.warn("Ignoring request to stop {} - this GatewayServer is currently starting",
GatewayServer.this);
}
@Override
public String toString() {
return "Starting@" + hashCode() + "[status: " + getStatus() + "]";
}
}
private final class Running extends InstanceState {
Running() {
super(ServerStatus.RUNNING);
}
@Override
void start() throws Exception {
GatewayServer.this.log
.warn("Ignoring request to start {} - this GatewayServer is already running",
GatewayServer.this);
}
@Override
void stop() throws Exception {
GatewayServer.this.log.info("Stopping {} ...", GatewayServer.this);
final long start = System.currentTimeMillis();
if (!GatewayServer.this.currentState.compareAndSet(
GatewayServer.this.running, GatewayServer.this.stopping)) {
GatewayServer.this.log
.warn("{} is not in state RUNNING - ignoring attempt to stop",
GatewayServer.this);
return;
}
final int numberOfChannels = GatewayServer.this.channelPipelineFactory
.getAllConnectedChannels().size();
GatewayServer.this.log.info("Closing [{}] open channels ...",
numberOfChannels);
GatewayServer.this.channelPipelineFactory.getAllConnectedChannels()
.close().awaitUninterruptibly();
GatewayServer.this.log.info("Closed [{}] open channels",
numberOfChannels);
GatewayServer.this.channelFactory.releaseExternalResources();
if (!GatewayServer.this.currentState.compareAndSet(
GatewayServer.this.stopping, GatewayServer.this.stopped)) {
throw new IllegalStateException(
String.format(
"Expected %s to be in state STOPPING - in fact, it is in state %s",
GatewayServer.this,
GatewayServer.this.currentState.get()));
}
final long end = System.currentTimeMillis();
GatewayServer.this.log.info("{} stopped in [{}] ms",
GatewayServer.this, end - start);
}
@Override
public String toString() {
return "Running@" + hashCode() + "[status: " + getStatus() + "]";
}
}
private final class Stopping extends InstanceState {
Stopping() {
super(ServerStatus.STOPPING);
}
@Override
void start() throws Exception {
GatewayServer.this.log
.warn("Ignoring request to start {} - this GatewayServer is currently stopping",
GatewayServer.this);
}
@Override
void stop() throws Exception {
GatewayServer.this.log
.warn("Ignoring request to stop {} - this GatewayServer is already stopping",
GatewayServer.this);
}
@Override
public String toString() {
return "Stopping@" + hashCode() + "[status: " + getStatus() + "]";
}
}
}