/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you 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.netty.channel.socket.nio;
import io.netty.channel.
ChannelException;
import io.netty.channel.
ChannelMetadata;
import io.netty.channel.
ChannelOption;
import io.netty.channel.
ChannelOutboundBuffer;
import io.netty.util.internal.
SocketUtils;
import io.netty.channel.nio.
AbstractNioMessageChannel;
import io.netty.channel.socket.
DefaultServerSocketChannelConfig;
import io.netty.channel.socket.
ServerSocketChannelConfig;
import io.netty.util.internal.
PlatformDependent;
import io.netty.util.internal.logging.
InternalLogger;
import io.netty.util.internal.logging.
InternalLoggerFactory;
import java.io.
IOException;
import java.net.
InetSocketAddress;
import java.net.
ServerSocket;
import java.net.
SocketAddress;
import java.nio.channels.
SelectionKey;
import java.nio.channels.
ServerSocketChannel;
import java.nio.channels.
SocketChannel;
import java.nio.channels.spi.
SelectorProvider;
import java.util.
List;
import java.util.
Map;
/**
* A {@link io.netty.channel.socket.ServerSocketChannel} implementation which uses
* NIO selector based implementation to accept new connections.
*/
public class
NioServerSocketChannel extends
AbstractNioMessageChannel
implements io.netty.channel.socket.
ServerSocketChannel {
private static final
ChannelMetadata METADATA = new
ChannelMetadata(false, 16);
private static final
SelectorProvider DEFAULT_SELECTOR_PROVIDER =
SelectorProvider.
provider();
private static final
InternalLogger logger =
InternalLoggerFactory.
getInstance(
NioServerSocketChannel.class);
private static
ServerSocketChannel newSocket(
SelectorProvider provider) {
try {
/**
* Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
* {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise.
*
* See <a href="https://github.com/netty/netty/issues/2308">#2308</a>.
*/
return
provider.
openServerSocketChannel();
} catch (
IOException e) {
throw new
ChannelException(
"Failed to open a server socket.",
e);
}
}
private final
ServerSocketChannelConfig config;
/**
* Create a new instance
*/
public
NioServerSocketChannel() {
this(
newSocket(
DEFAULT_SELECTOR_PROVIDER));
}
/**
* Create a new instance using the given {@link SelectorProvider}.
*/
public
NioServerSocketChannel(
SelectorProvider provider) {
this(
newSocket(
provider));
}
/**
* Create a new instance using the given {@link ServerSocketChannel}.
*/
public
NioServerSocketChannel(
ServerSocketChannel channel) {
super(null,
channel,
SelectionKey.
OP_ACCEPT);
config = new
NioServerSocketChannelConfig(this,
javaChannel().
socket());
}
@
Override
public
InetSocketAddress localAddress() {
return (
InetSocketAddress) super.localAddress();
}
@
Override
public
ChannelMetadata metadata() {
return
METADATA;
}
@
Override
public
ServerSocketChannelConfig config() {
return
config;
}
@
Override
public boolean
isActive() {
return
javaChannel().
socket().
isBound();
}
@
Override
public
InetSocketAddress remoteAddress() {
return null;
}
@
Override
protected
ServerSocketChannel javaChannel() {
return (
ServerSocketChannel) super.javaChannel();
}
@
Override
protected
SocketAddress localAddress0() {
return
SocketUtils.
localSocketAddress(
javaChannel().
socket());
}
@
Override
protected void
doBind(
SocketAddress localAddress) throws
Exception {
if (
PlatformDependent.
javaVersion() >= 7) {
javaChannel().
bind(
localAddress,
config.
getBacklog());
} else {
javaChannel().
socket().
bind(
localAddress,
config.
getBacklog());
}
}
@
Override
protected void
doClose() throws
Exception {
javaChannel().
close();
}
@
Override
protected int
doReadMessages(
List<
Object>
buf) throws
Exception {
SocketChannel ch =
SocketUtils.
accept(
javaChannel());
try {
if (
ch != null) {
buf.
add(new
NioSocketChannel(this,
ch));
return 1;
}
} catch (
Throwable t) {
logger.
warn("Failed to create a new channel from an accepted socket.",
t);
try {
ch.
close();
} catch (
Throwable t2) {
logger.
warn("Failed to close a socket.",
t2);
}
}
return 0;
}
// Unnecessary stuff
@
Override
protected boolean
doConnect(
SocketAddress remoteAddress,
SocketAddress localAddress) throws
Exception {
throw new
UnsupportedOperationException();
}
@
Override
protected void
doFinishConnect() throws
Exception {
throw new
UnsupportedOperationException();
}
@
Override
protected
SocketAddress remoteAddress0() {
return null;
}
@
Override
protected void
doDisconnect() throws
Exception {
throw new
UnsupportedOperationException();
}
@
Override
protected boolean
doWriteMessage(
Object msg,
ChannelOutboundBuffer in) throws
Exception {
throw new
UnsupportedOperationException();
}
@
Override
protected final
Object filterOutboundMessage(
Object msg) throws
Exception {
throw new
UnsupportedOperationException();
}
private final class
NioServerSocketChannelConfig extends
DefaultServerSocketChannelConfig {
private
NioServerSocketChannelConfig(
NioServerSocketChannel channel,
ServerSocket javaSocket) {
super(
channel,
javaSocket);
}
@
Override
protected void
autoReadCleared() {
clearReadPending();
}
@
Override
public <T> boolean
setOption(
ChannelOption<T>
option, T
value) {
if (
PlatformDependent.
javaVersion() >= 7 &&
option instanceof
NioChannelOption) {
return
NioChannelOption.
setOption(
jdkChannel(), (
NioChannelOption<T>)
option,
value);
}
return super.setOption(
option,
value);
}
@
Override
public <T> T
getOption(
ChannelOption<T>
option) {
if (
PlatformDependent.
javaVersion() >= 7 &&
option instanceof
NioChannelOption) {
return
NioChannelOption.
getOption(
jdkChannel(), (
NioChannelOption<T>)
option);
}
return super.getOption(
option);
}
@
SuppressWarnings("unchecked")
@
Override
public
Map<
ChannelOption<?>,
Object>
getOptions() {
if (
PlatformDependent.
javaVersion() >= 7) {
return
getOptions(super.getOptions(),
NioChannelOption.
getOptions(
jdkChannel()));
}
return super.getOptions();
}
private
ServerSocketChannel jdkChannel() {
return ((
NioServerSocketChannel)
channel).
javaChannel();
}
}
// Override just to to be able to call directly via unit tests.
@
Override
protected boolean
closeOnReadError(
Throwable cause) {
return super.closeOnReadError(
cause);
}
}