/* * Copyright 2013 Red Hat, Inc. * * Red Hat 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 org.xnio.netty.transport; import io.netty.channel.AbstractServerChannel; import io.netty.channel.ChannelException; import io.netty.channel.EventLoop; import io.netty.channel.socket.ServerSocketChannel; import org.xnio.ChannelListener; import org.xnio.Option; import org.xnio.StreamConnection; import org.xnio.channels.AcceptingChannel; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; /** * {@link ServerSocketChannel} base class for our XNIO transport * * @author <a href="mailto:[email protected]">Norman Maurer</a> */ abstract class AbstractXnioServerSocketChannel extends AbstractServerChannel implements ServerSocketChannel { private final XnioServerSocketChannelConfigImpl config = new XnioServerSocketChannelConfigImpl(this); private static final ThreadLocal<StreamConnection[]> connections = new ThreadLocal<StreamConnection[]>(); private static StreamConnection[] connectionsArray(int size) { StreamConnection[] array = connections.get(); if (array == null || array.length < size) { array = new StreamConnection[size]; connections.set(array); } return array; } @Override protected boolean isCompatible(EventLoop loop) { return loop instanceof XnioEventLoop; } @Override public boolean isActive() { return isOpen(); } @Override public InetSocketAddress localAddress() { return (InetSocketAddress) super.localAddress(); } @Override public InetSocketAddress remoteAddress() { return (InetSocketAddress) super.remoteAddress(); } @Override public XnioServerSocketChannelConfigImpl config() { return config; } <T> T getOption(Option<T> option) { try { return getOption0(option); } catch (IOException e) { throw new ChannelException(e); } } <T> void setOption(Option<T> option, T value) { try { setOption0(option, value); } catch (IOException e) { throw new ChannelException(e); } } @Override protected SocketAddress localAddress0() { AcceptingChannel channel = xnioChannel(); if (channel == null) { return null; } return channel.getLocalAddress(); } @Override protected void doClose() throws Exception { AcceptingChannel channel = xnioChannel(); if (channel == null) { return; } channel.suspendAccepts(); channel.close(); } @Override protected void doBeginRead() throws Exception { AcceptingChannel channel = xnioChannel(); if (channel == null) { return; } channel.resumeAccepts(); } @Override public boolean isOpen() { AcceptingChannel channel = xnioChannel(); return channel == null || channel.isOpen(); } /** * Return the underyling {@link AcceptingChannel} */ protected abstract AcceptingChannel xnioChannel(); /** * Set the given {@link Option} to the given value. */ protected abstract <T> void setOption0(Option<T> option, T value) throws IOException; /** * Return the value for the given {@link Option}. */ protected abstract <T> T getOption0(Option<T> option) throws IOException; /** * {@link ChannelListener} implementation which takes care of accept connections and fire them through the * {@link io.netty.channel.ChannelPipeline}. */ final class AcceptListener implements ChannelListener<AcceptingChannel<StreamConnection>> { @Override public void handleEvent(final AcceptingChannel<StreamConnection> channel) { if (!config.isAutoRead()) { channel.suspendAccepts(); } EventLoop loop = eventLoop(); if (loop.inEventLoop()) { try { int messagesToRead = config().getMaxMessagesPerRead(); for (int i = 0; i < messagesToRead; i++) { StreamConnection conn = channel.accept(); if (conn == null) { break; } pipeline().fireChannelRead(new WrappingXnioSocketChannel(AbstractXnioServerSocketChannel.this, conn)); } } catch (Throwable cause) { pipeline().fireExceptionCaught(cause); } pipeline().fireChannelReadComplete(); } else { Throwable cause; int messagesToRead = config().getMaxMessagesPerRead(); final StreamConnection[] array = connectionsArray(messagesToRead); try { for (int i = 0; i < messagesToRead; i++) { StreamConnection conn = channel.accept(); array[i] = conn; if (conn == null) { break; } } cause = null; } catch (Throwable e) { cause = e; } final Throwable acceptError = cause; eventLoop().execute( new Runnable() { @Override public void run() { try { for (int i = 0; i < array.length; i++) { StreamConnection conn = array[i]; if (conn == null) { break; } pipeline().fireChannelRead(new WrappingXnioSocketChannel(AbstractXnioServerSocketChannel.this, conn)); } } catch (Throwable cause) { pipeline().fireExceptionCaught(cause); } if (acceptError != null) { pipeline().fireExceptionCaught(acceptError); } pipeline().fireChannelReadComplete(); } }); } } } }