/** * Copyright 2012 Ronen Hamias, Anton Kharenko * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ package io.scalecube.socketio; import io.netty.bootstrap.ServerBootstrap; import io.netty.handler.ssl.ClientAuth; import io.netty.handler.ssl.JdkSslContext; import io.netty.handler.ssl.SslContext; import io.netty.util.HashedWheelTimer; import io.scalecube.socketio.pipeline.SocketIOChannelInitializer; import io.scalecube.socketio.session.SocketIOHeartbeatScheduler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.SSLContext; /** * A Socket.IO server launcher class. * * @author Anton Kharenko */ public final class SocketIOServer { private final Logger log = LoggerFactory.getLogger(getClass()); private enum State { STARTED, STOPPED } private ServerConfiguration configuration; private SocketIOListener listener; private PipelineModifier pipelineModifier; private HashedWheelTimer timer; private volatile State state = State.STOPPED; private ServerBootstrapFactory serverBootstrapFactory; private ServerBootstrap bootstrap; private SocketIOServer(ServerConfiguration configuration) { this.configuration = configuration; } /** * Creates instance of Socket.IO server with default settings. */ public static SocketIOServer newInstance() { return new SocketIOServer(ServerConfiguration.DEFAULT); } /** * Creates instance of Socket.IO server with the given port. */ public static SocketIOServer newInstance(int port) { return new SocketIOServer(ServerConfiguration.builder().port(port).build()); } /** * Creates instance of Socket.IO server with the given secure port. */ public static SocketIOServer newInstance(int port, SSLContext sslContext) { SslContext nettySslContext = new JdkSslContext(sslContext, false, ClientAuth.NONE); return newInstance(port, nettySslContext); } /** * Creates instance of Socket.IO server with the given secure port. */ public static SocketIOServer newInstance(int port, SslContext sslContext) { return new SocketIOServer(ServerConfiguration.builder() .port(port) .sslContext(sslContext) .build()); } /** * Creates instance of Socket.IO server with the given configuration. */ public static SocketIOServer newInstance(ServerConfiguration config) { return new SocketIOServer(config); } /** * Starts Socket.IO server with current configuration settings. * * @throws IllegalStateException * if server already started */ public synchronized void start() { if (isStarted()) { throw new IllegalStateException("Failed to start Socket.IO server: server already started"); } log.info("Socket.IO server starting"); // Configure heartbeat scheduler timer = new HashedWheelTimer(); timer.start(); SocketIOHeartbeatScheduler.setHashedWheelTimer(timer); SocketIOHeartbeatScheduler.setHeartbeatInterval(configuration.getHeartbeatInterval()); SocketIOHeartbeatScheduler.setHeartbeatTimeout(configuration.getHeartbeatTimeout()); // Configure and bind server ServerBootstrapFactory bootstrapFactory = serverBootstrapFactory != null ? serverBootstrapFactory : new DefaultServerBootstrapFactory(configuration); bootstrap = bootstrapFactory.createServerBootstrap(); bootstrap.childHandler(new SocketIOChannelInitializer(configuration, listener, pipelineModifier)); bootstrap.bind(configuration.getPort()).syncUninterruptibly(); state = State.STARTED; log.info("Socket.IO server started: {}", configuration); } /** * Stops Socket.IO server. * * @throws IllegalStateException * if server already stopped */ public synchronized void stop() { if (isStopped()) { throw new IllegalStateException("Failed to stop Socket.IO server: server already stopped"); } log.info("Socket.IO server stopping"); timer.stop(); bootstrap.config().group().shutdownGracefully().syncUninterruptibly(); state = State.STOPPED; log.info("Socket.IO server stopped"); } /** * Restarts Socket.IO server. If server already started it stops server; * otherwise it just starts server. */ public synchronized void restart() { if (isStarted()) { stop(); } start(); } /** * Returns if server is in started state or not. */ public boolean isStarted() { return state == State.STARTED; } /** * Returns if server is in stopped state or not. */ public boolean isStopped() { return state == State.STOPPED; } /** * Socket.IO events listener. */ public SocketIOListener getListener() { return listener; } /** * Sets Socket.IO events listener. If server already started new listener will be applied only after * server restart. */ public void setListener(SocketIOListener listener) { this.listener = listener; } /** * Returns pipeline modifier */ public PipelineModifier getPipelineModifier() { return pipelineModifier; } /** * Sets pipeline modifier. Pipeline modifier could be used for adding handlers to channel pipeline. */ public void setPipelineModifier(PipelineModifier pipelineModifier) { this.pipelineModifier = pipelineModifier; } /** * Returns server configuration settings. */ public ServerConfiguration getConfiguration() { return configuration; } /** * Sets server configuration settings. If server already started new settings will be applied only after * server restart. */ public void setConfiguration(ServerConfiguration configuration) { this.configuration = configuration; } /** * Returns ServerBootstrap factory. */ public ServerBootstrapFactory getServerBootstrapFactory() { return serverBootstrapFactory; } /** * Sets ServerBootstrap factory. If server already started new bootstrap factory will be applied only after * server restart. */ public void setServerBootstrapFactory(ServerBootstrapFactory serverBootstrapFactory) { this.serverBootstrapFactory = serverBootstrapFactory; } }