/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.fabric.netty.server; import com.liferay.portal.fabric.agent.FabricAgentRegistry; import com.liferay.portal.fabric.netty.codec.serialization.AnnotatedObjectDecoder; import com.liferay.portal.fabric.netty.codec.serialization.AnnotatedObjectEncoder; import com.liferay.portal.fabric.netty.fileserver.FileHelperUtil; import com.liferay.portal.fabric.netty.fileserver.handlers.FileRequestChannelHandler; import com.liferay.portal.fabric.netty.handlers.NettyFabricAgentRegistrationChannelHandler; import com.liferay.portal.fabric.netty.rpc.handlers.NettyRPCChannelHandler; import com.liferay.portal.fabric.netty.util.NettyUtil; import com.liferay.portal.fabric.server.FabricServer; import com.liferay.portal.kernel.concurrent.DefaultNoticeableFuture; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.util.NamedThreadFactory; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoop; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.util.ThreadDeathWatcher; import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.EventExecutorGroup; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; import java.nio.file.Files; import java.util.concurrent.TimeUnit; /** * @author Shuyang Zhou */ public class NettyFabricServer implements FabricServer { public NettyFabricServer( FabricAgentRegistry fabricAgentRegistry, NettyFabricServerConfig nettyFabricServerConfig) { _fabricAgentRegistry = fabricAgentRegistry; _nettyFabricServerConfig = nettyFabricServerConfig; } @Override public synchronized void start() throws Exception { if (_serverChannel != null) { throw new IllegalStateException( "Netty fabric server was already started"); } if (_log.isInfoEnabled()) { _log.info( "Starting Netty fabric server using " + _nettyFabricServerConfig); } Files.createDirectories( _nettyFabricServerConfig.getRepositoryParentPath()); ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.handler(new LoggingHandler(LogLevel.INFO)); serverBootstrap.childHandler(new ChildChannelInitializer()); EventLoopGroup bossEventLoopGroup = new NioEventLoopGroup( _nettyFabricServerConfig.getBossGroupThreadCount(), new NamedThreadFactory( "Netty Fabric Server/Boss Event Loop Group", Thread.NORM_PRIORITY, null)); EventLoopGroup workerEventLoopGroup = new NioEventLoopGroup( _nettyFabricServerConfig.getWorkerGroupThreadCount(), new NamedThreadFactory( "Netty Fabric Server/Worker Event Loop Group", Thread.NORM_PRIORITY, null)); NettyUtil.bindShutdown( bossEventLoopGroup, workerEventLoopGroup, _nettyFabricServerConfig.getShutdownQuietPeriod(), _nettyFabricServerConfig.getShutdownTimeout()); serverBootstrap.group(bossEventLoopGroup, workerEventLoopGroup); ChannelFuture channelFuture = serverBootstrap.bind( _nettyFabricServerConfig.getNettyFabricServerHost(), _nettyFabricServerConfig.getNettyFabricServerPort()); _serverChannel = channelFuture.channel(); channelFuture.addListener(new PostBindChannelFutureListener()); channelFuture.sync(); } @Override public synchronized java.util.concurrent.Future<?> stop() throws InterruptedException { if (_serverChannel == null) { throw new IllegalStateException( "Netty fabric server is not started"); } EventLoop eventLoop = _serverChannel.eventLoop(); EventLoopGroup bossEventLoopGroup = eventLoop.parent(); DefaultNoticeableFuture<?> defaultNoticeableFuture = new DefaultNoticeableFuture<>(); try { ChannelFuture channelFuture = _serverChannel.close(); channelFuture.sync(); } finally { Future<?> future = bossEventLoopGroup.shutdownGracefully( _nettyFabricServerConfig.getShutdownQuietPeriod(), _nettyFabricServerConfig.getShutdownTimeout(), TimeUnit.MILLISECONDS); future.addListener( new PostShutdownChannelListener(defaultNoticeableFuture)); } return defaultNoticeableFuture; } protected EventExecutorGroup createEventExecutorGroup( int threadCount, String threadPoolName) { EventExecutorGroup eventExecutorGroup = new DefaultEventExecutorGroup( threadCount, new NamedThreadFactory(threadPoolName, Thread.NORM_PRIORITY, null)); EventLoop eventLoop = _serverChannel.eventLoop(); NettyUtil.bindShutdown( eventLoop.parent(), eventExecutorGroup, _nettyFabricServerConfig.getShutdownQuietPeriod(), _nettyFabricServerConfig.getShutdownTimeout()); return eventExecutorGroup; } protected class ChildChannelInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) { ChannelPipeline channelPipeline = socketChannel.pipeline(); channelPipeline.addLast( AnnotatedObjectEncoder.NAME, AnnotatedObjectEncoder.INSTANCE); channelPipeline.addLast( AnnotatedObjectDecoder.NAME, new AnnotatedObjectDecoder()); channelPipeline.addLast( createEventExecutorGroup( _nettyFabricServerConfig.getRPCGroupThreadCount(), "Netty Fabric Server/RPC Event Executor Group"), NettyRPCChannelHandler.NAME, NettyRPCChannelHandler.INSTANCE); EventExecutorGroup fileServerEventExecutorGroup = createEventExecutorGroup( _nettyFabricServerConfig.getFileServerGroupThreadCount(), "Netty Fabric Server/File Server Event Executor Group"); channelPipeline.addLast( fileServerEventExecutorGroup, FileRequestChannelHandler.NAME, new FileRequestChannelHandler( _nettyFabricServerConfig. getFileServerFolderCompressionLevel())); channelPipeline.addLast( createEventExecutorGroup( _nettyFabricServerConfig.getRegistrationGroupThreadCount(), "Netty Fabric Server/Registration Event Executor Group"), new NettyFabricAgentRegistrationChannelHandler( _fabricAgentRegistry, _nettyFabricServerConfig.getRepositoryParentPath(), fileServerEventExecutorGroup, _nettyFabricServerConfig.getRepositoryGetFileTimeout(), _nettyFabricServerConfig.getRPCRelayTimeout(), _nettyFabricServerConfig.getWorkerStartupTimeout())); } } protected class PostBindChannelFutureListener implements ChannelFutureListener { @Override public void operationComplete(ChannelFuture channelFuture) throws InterruptedException { Channel channel = channelFuture.channel(); if (channelFuture.isSuccess()) { if (_log.isInfoEnabled()) { _log.info( "Started Netty fabric server on " + channel.localAddress()); } return; } String serverAddress = _nettyFabricServerConfig.getNettyFabricServerHost() + ":" + _nettyFabricServerConfig.getNettyFabricServerPort(); if (channelFuture.isCancelled()) { _log.error( "Cancelled starting Netty fabric server on " + serverAddress); } else { _log.error( "Unable to start Netty fabric server on " + serverAddress, channelFuture.cause()); } stop(); } } protected class PostShutdownChannelListener implements FutureListener<Object> { @Override public void operationComplete(Future<Object> future) throws InterruptedException { FileHelperUtil.delete( _nettyFabricServerConfig.getRepositoryParentPath()); _serverChannel = null; if (!ThreadDeathWatcher.awaitInactivity( _nettyFabricServerConfig.getShutdownTimeout(), TimeUnit.MILLISECONDS)) { _log.error("Unable to stop thread death watcher"); } runnable.run(); } protected PostShutdownChannelListener(Runnable runnable) { this.runnable = runnable; } protected final Runnable runnable; } private static final Log _log = LogFactoryUtil.getLog( NettyFabricServer.class); private final FabricAgentRegistry _fabricAgentRegistry; private final NettyFabricServerConfig _nettyFabricServerConfig; private volatile Channel _serverChannel; }