@Override public void start() throws SmppChannelException { if (isDestroyed()) { throw new SmppChannelException("Unable to start: server is destroyed"); } try { ChannelFuture f = this.serverBootstrap.bind(new InetSocketAddress(configuration.getPort())); // wait until the connection is made successfully boolean timeout = !f.await(configuration.getBindTimeout()); if (timeout || !f.isSuccess()) throw new SmppChannelException( "Can't bind to port " + configuration.getPort() + " after " + configuration.getBindTimeout() + " milliseconds"); logger.info("{} started on SMPP port [{}]", configuration.getName(), configuration.getPort()); serverChannel = f.channel(); } catch (ChannelException e) { throw new SmppChannelException(e.getMessage(), e); } catch (InterruptedException e) { throw new SmppChannelException(e.getMessage(), e); } }
private void registerMBean() { if (configuration == null) { return; } if (configuration.isJmxEnabled()) { // register the this queue manager as an mbean try { ObjectName name = new ObjectName(configuration.getJmxDomain() + ":name=" + configuration.getName()); ManagementFactory.getPlatformMBeanServer().registerMBean(this, name); } catch (Exception e) { // log the error, but don't throw an exception for this datasource logger.error("Unable to register DefaultSmppServerMXBean [{}]", configuration.getName(), e); } } }
@Override public void stop() { if (this.channels.size() > 0) { logger.info( "{} currently has [{}] open child channel(s) that will be closed as part of stop()", configuration.getName(), this.channels.size()); } // close all channels still open within this session "bootstrap" this.channels.close().awaitUninterruptibly(); // clean up all external resources if (this.serverChannel != null) { this.serverChannel.close().awaitUninterruptibly(); this.serverChannel = null; } logger.info("{} stopped on SMPP port [{}]", configuration.getName(), configuration.getPort()); }
/** * Creates a new default SmppServer. * * @param configuration The server configuration to create this server with * @param serverHandler The handler implementation for handling bind requests and * creating/destroying sessions. * @param monitorExecutor The scheduled executor that all sessions will share to monitor * themselves and expire requests. If null monitoring will be disabled. * @param bossGroup Specify the EventLoopGroup to accept new connections and handle accepted * connections. The {@link EventLoopGroup} is used to handle all the events and IO for {@link * SocketChannel}. * @param workerGroup The {@link EventLoopGroup} is used to handle all the events and IO for * {@link Channel}. */ public DefaultSmppServer( final SmppServerConfiguration configuration, SmppServerHandler serverHandler, ScheduledExecutorService monitorExecutor, EventLoopGroup bossGroup, EventLoopGroup workerGroup) { this.configuration = configuration; // the same group we'll put every server channel this.channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); this.serverHandler = serverHandler; // tie the server bootstrap to this server socket channel factory this.serverBootstrap = new ServerBootstrap(); // a factory for creating channels (connections) if (configuration.isNonBlockingSocketsEnabled()) { this.serverBootstrap.channel(NioServerSocketChannel.class); } else { this.serverBootstrap.channel(OioServerSocketChannel.class); } this.bossGroup = bossGroup; this.workerGroup = workerGroup; this.serverBootstrap.group(this.bossGroup, this.workerGroup); // set options for the server socket that are useful this.serverBootstrap.option(ChannelOption.SO_REUSEADDR, configuration.isReuseAddress()); // we use the same default pipeline for all new channels - no need for a factory this.serverConnector = new SmppServerConnector(channels, this); this.serverBootstrap.childHandler( new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline() .addLast(SmppChannelConstants.PIPELINE_SERVER_CONNECTOR_NAME, serverConnector); } }); // a shared timer used to make sure new channels are bound within X milliseconds this.bindTimer = new Timer(configuration.getName() + "-BindTimer0", true); // NOTE: this would permit us to customize the "transcoding" context for a server if needed this.transcoder = new DefaultPduTranscoder(new DefaultPduTranscoderContext()); this.sessionIdSequence = new AtomicLong(0); this.monitorExecutor = monitorExecutor; this.counters = new DefaultSmppServerCounters(); if (configuration.isJmxEnabled()) { registerMBean(); } }
protected void destroySession(Long sessionId, DefaultSmppSession session) { // session destroyed, now pass it upstream counters.incrementSessionDestroyedAndGet(); decrementSessionSizeCounters(session); serverHandler.sessionDestroyed(sessionId, session); // unregister this session as an mbean if (configuration.isJmxEnabled()) { session.unregisterMBean( configuration.getJmxDomain() + ":type=" + configuration.getName() + "Sessions,name=" + sessionId); } }
@Override public void destroy() { this.bindTimer.cancel(); stop(); // Shut down all event loops to terminate all threads. bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); try { // Wait until all threads are terminated. bossGroup.terminationFuture().sync(); workerGroup.terminationFuture().sync(); } catch (InterruptedException e) { // is ok } this.serverBootstrap = null; unregisterMBean(); logger.info("{} destroyed on SMPP port [{}]", configuration.getName(), configuration.getPort()); }
protected void createSession( Long sessionId, Channel channel, SmppSessionConfiguration config, BaseBindResp preparedBindResponse) throws SmppProcessingException { // NOTE: exactly one PDU (bind request) was read from the channel, we // now need to delegate permitting this bind request by calling a method // further upstream. Only after the server-side is completely ready to // start processing requests from this session, do we want to actually // return the bind response and start reading further requests -- we'll // initially block reading from the channel first -- this will be turned // back on via the "serverReady()" method call on the session object // make sure the channel is not being read/processed (until we flag we're ready later on) channel.config().setAutoRead(false); // auto negotiate the interface version in use based on the requested interface version byte interfaceVersion = this.autoNegotiateInterfaceVersion(config.getInterfaceVersion()); // create a new server session associated with this server DefaultSmppSession session = new DefaultSmppSession( SmppSession.Type.SERVER, config, channel, this, sessionId, preparedBindResponse, interfaceVersion, monitorExecutor); // replace name of thread used for renaming SmppSessionThreadRenamer threadRenamer = (SmppSessionThreadRenamer) channel.pipeline().get(SmppChannelConstants.PIPELINE_SESSION_THREAD_RENAMER_NAME); threadRenamer.setThreadName(config.getName()); // add a logging handler after the thread renamer SmppSessionLogger loggingHandler = new SmppSessionLogger( DefaultSmppSession.class.getCanonicalName(), config.getLoggingOptions()); channel .pipeline() .addAfter( SmppChannelConstants.PIPELINE_SESSION_THREAD_RENAMER_NAME, SmppChannelConstants.PIPELINE_SESSION_LOGGER_NAME, loggingHandler); // decoder in pipeline is ok (keep it) // create a new wrapper around a session to pass the pdu up the chain channel.pipeline().remove(SmppChannelConstants.PIPELINE_SESSION_WRAPPER_NAME); channel .pipeline() .addLast( SmppChannelConstants.PIPELINE_SESSION_WRAPPER_NAME, new SmppSessionWrapper(session)); // check if the # of channels exceeds maxConnections if (this.channels.size() > this.configuration.getMaxConnectionSize()) { logger.warn( "The current connection size [{}] exceeds the configured max connection size [{}]", this.channels.size(), this.configuration.getMaxConnectionSize()); } // session created, now pass it upstream counters.incrementSessionCreatedAndGet(); incrementSessionSizeCounters(session); this.serverHandler.sessionCreated(sessionId, session, preparedBindResponse); // register this session as an mbean if (configuration.isJmxEnabled()) { session.registerMBean( configuration.getJmxDomain() + ":type=" + configuration.getName() + "Sessions,name=" + sessionId); } }