/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.transport;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
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.ssl.SslHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.concurrent.Future;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLEngine;
import org.teiid.common.buffer.StorageManager;
import org.teiid.core.TeiidRuntimeException;
import org.teiid.core.util.NamedThreadFactory;
import org.teiid.core.util.PropertiesUtils;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.logging.MessageLevel;
import org.teiid.net.socket.ObjectChannel;
import org.teiid.runtime.RuntimePlugin;
import org.teiid.transport.ChannelListener.ChannelListenerFactory;
/**
* Server-side class to listen for new connection requests and create a SocketClientConnection for each connection request.
*/
public class SocketListener implements ChannelListenerFactory {
private static final int DEFAULT_MAX_MESSAGE_SIZE = 1 << 21;
protected SSLAwareChannelHandler channelHandler;
private Channel serverChannel;
private boolean isClientEncryptionEnabled;
private ClientServiceRegistryImpl csr;
private ServerBootstrap bootstrap;
private int maxMessageSize = PropertiesUtils.getIntProperty(System.getProperties(), "org.teiid.maxMessageSize", DEFAULT_MAX_MESSAGE_SIZE); //$NON-NLS-1$
private long maxLobSize = PropertiesUtils.getLongProperty(System.getProperties(), "org.teiid.maxStreamingLobSize", ObjectDecoder.MAX_LOB_SIZE); //$NON-NLS-1$
public SocketListener(InetSocketAddress address, SocketConfiguration config, ClientServiceRegistryImpl csr, StorageManager storageManager) {
this(address, config.getInputBufferSize(), config.getOutputBufferSize(), config.getMaxSocketThreads(), config.getSSLConfiguration(), csr, storageManager);
LogManager.logDetail(LogConstants.CTX_TRANSPORT, RuntimePlugin.Util.getString("SocketTransport.1", new Object[] {address.getHostName(), String.valueOf(config.getPortNumber())})); //$NON-NLS-1$
}
/**
*
* @param port
* @param inputBufferSize
* @param outputBufferSize
* @param engine null if SSL is disabled
* @param bindaddress
* @param server
*/
public SocketListener(final InetSocketAddress address, final int inputBufferSize,
final int outputBufferSize, int maxWorkers,
final SSLConfiguration config, final ClientServiceRegistryImpl csr,
final StorageManager storageManager) {
if (config != null) {
this.isClientEncryptionEnabled = config.isClientEncryptionEnabled();
}
this.csr = csr;
NamedThreadFactory nettyPool = new NamedThreadFactory("NIO"); //$NON-NLS-1$
if (LogManager.isMessageToBeRecorded(LogConstants.CTX_TRANSPORT, MessageLevel.DETAIL)) {
LogManager.logDetail(LogConstants.CTX_TRANSPORT, "server = " + address.getAddress() + "binding to port:" + address.getPort()); //$NON-NLS-1$ //$NON-NLS-2$
}
if (maxWorkers == 0) {
maxWorkers = Math.max(4, 2*Runtime.getRuntime().availableProcessors());
}
EventLoopGroup workers = new NioEventLoopGroup(maxWorkers, nettyPool);
bootstrap = new ServerBootstrap();
bootstrap.group(workers).channel(NioServerSocketChannel.class);
this.channelHandler = createChannelHandler();
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
configureChannelPipeline(pipeline, config, storageManager);
}
});
if (inputBufferSize != 0) {
bootstrap.childOption(ChannelOption.SO_RCVBUF, new Integer(inputBufferSize));
}
if (outputBufferSize != 0) {
bootstrap.childOption(ChannelOption.SO_SNDBUF, new Integer(outputBufferSize));
}
bootstrap.childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE);
bootstrap.childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE);
ChannelFuture future = bootstrap.bind(address);
future.syncUninterruptibly();
this.serverChannel = future.channel();
}
protected void configureChannelPipeline(ChannelPipeline pipeline,
SSLConfiguration config, StorageManager storageManager) throws Exception {
if (config != null) {
SSLEngine engine = config.getServerSSLEngine();
if (engine != null) {
pipeline.addLast("ssl", new SslHandler(engine)); //$NON-NLS-1$
}
}
pipeline.addLast("decoder", new ObjectDecoder(maxMessageSize, //$NON-NLS-1$
maxLobSize,
Thread.currentThread().getContextClassLoader(),
storageManager));
pipeline.addLast("chunker", new ChunkedWriteHandler()); //$NON-NLS-1$
pipeline.addLast("encoder", new ObjectEncoder()); //$NON-NLS-1$
pipeline.addLast("handler", this.channelHandler); //$NON-NLS-1$
}
public int getPort() {
return ((InetSocketAddress)this.serverChannel.localAddress()).getPort();
}
/**
* Stops the {@link SocketListener}
* @return a Future if the transport was started successfully
* that can notify of successfully killing all clients
*/
public Future<?> stop() {
ChannelFuture future = this.serverChannel.closeFuture();
Future<?> shutdown = null;
if (this.bootstrap != null) {
shutdown = bootstrap.group().shutdownGracefully(0, 0, TimeUnit.SECONDS);
bootstrap = null;
}
try {
future.await();
} catch (InterruptedException e) {
throw new TeiidRuntimeException(e);
}
return shutdown;
}
public SocketListenerStats getStats() {
SocketListenerStats stats = new SocketListenerStats();
stats.objectsRead = this.channelHandler.getObjectsRead();
stats.objectsWritten = this.channelHandler.getObjectsWritten();
stats.sockets = this.channelHandler.getConnectedChannels();
stats.maxSockets = this.channelHandler.getMaxConnectedChannels();
return stats;
}
protected SSLAwareChannelHandler createChannelHandler() {
return new SSLAwareChannelHandler(this);
}
public ChannelListener createChannelListener(ObjectChannel channel) {
return new SocketClientInstance(channel, csr, this.isClientEncryptionEnabled);
}
SSLAwareChannelHandler getChannelHandler() {
return channelHandler;
}
public int getMaxMessageSize() {
return maxMessageSize;
}
public void setMaxMessageSize(int maxMessageSize) {
this.maxMessageSize = maxMessageSize;
}
public void setMaxLobSize(long maxLobSize) {
this.maxLobSize = maxLobSize;
}
}