/** * Copyright 2014 Ricardo Padilha * * Licensed 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 net.dsys.snio.impl.channel; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import javax.annotation.Nonnegative; import javax.annotation.Nonnull; import net.dsys.commons.api.future.CallbackFuture; import net.dsys.commons.api.lang.Factory; import net.dsys.commons.impl.future.SettableCallbackFuture; import net.dsys.snio.api.buffer.MessageBufferProvider; import net.dsys.snio.api.channel.AcceptListener; import net.dsys.snio.api.channel.CloseListener; import net.dsys.snio.api.codec.MessageCodec; import net.dsys.snio.api.limit.RateLimiter; import net.dsys.snio.api.pool.KeyAcceptor; import net.dsys.snio.api.pool.SelectorExecutor; import net.dsys.snio.api.pool.SelectorPool; import net.dsys.snio.api.pool.SelectorThread; /** * @author Ricardo Padilha */ final class TCPAcceptor implements KeyAcceptor<ByteBuffer> { @Nonnull private final SelectorPool pool; @Nonnull private final Factory<MessageCodec> codecs; @Nonnull private final Factory<RateLimiter> limiters; @Nonnull private final Factory<MessageBufferProvider<ByteBuffer>> providers; @Nonnull private final int sendSize; @Nonnull private final int receiveSize; @Nonnull private final SettableCallbackFuture<Void> bindFuture; @Nonnull private final SettableCallbackFuture<Void> closeFuture; @Nonnull private AcceptListener<ByteBuffer> accept; @Nonnull private CloseListener<ByteBuffer> close; private SelectionKey acceptKey; TCPAcceptor(@Nonnull final SelectorPool pool, @Nonnull final Factory<MessageCodec> codecs, @Nonnull final Factory<RateLimiter> limiters, @Nonnull final Factory<MessageBufferProvider<ByteBuffer>> providers, @Nonnegative final int sendSize, @Nonnegative final int receiveSize) { if (pool == null) { throw new NullPointerException("pool == null"); } if (codecs == null) { throw new IllegalArgumentException("codecs == null"); } if (limiters == null) { throw new IllegalArgumentException("limiters == null"); } if (providers == null) { throw new IllegalArgumentException("providers == null"); } if (sendSize < 1) { throw new IllegalArgumentException("sendSize < 1"); } if (receiveSize < 1) { throw new IllegalArgumentException("receiveSize < 1"); } this.pool = pool; this.codecs = codecs; this.limiters = limiters; this.providers = providers; this.sendSize = sendSize; this.receiveSize = receiveSize; this.bindFuture = new SettableCallbackFuture<>(); this.closeFuture = new SettableCallbackFuture<>(); this.accept = MessageChannels.dummyAcceptListener(); this.close = MessageChannels.dummyCloseListener(); } /** * {@inheritDoc} */ @Override public void onAccept(final AcceptListener<ByteBuffer> listener) { if (listener == null) { throw new NullPointerException("listener == null"); } this.accept = listener; } /** * {@inheritDoc} */ @Override public void onClose(final CloseListener<ByteBuffer> listener) { if (listener == null) { throw new NullPointerException("listener == null"); } this.close = listener; } /** * {@inheritDoc} */ @Override public CallbackFuture<Void> getBindFuture() { return bindFuture; } /** * {@inheritDoc} */ @Override public void registered(final SelectorThread thread, final SelectionKey key) { if (thread == null) { throw new NullPointerException("thread == null"); } if (key == null) { throw new NullPointerException("key == null"); } this.acceptKey = key; bindFuture.success(null); } /** * {@inheritDoc} */ @Override public void accept(final SelectionKey key) throws IOException { final ServerSocketChannel server = (ServerSocketChannel) key.channel(); final SocketChannel client = server.accept(); client.configureBlocking(false); final MessageCodec codec = codecs.newInstance(); final RateLimiter limiter = limiters.newInstance(); final MessageBufferProvider<ByteBuffer> provider = providers.newInstance(); final TCPProcessor processor = new TCPProcessor(codec, limiter, provider, sendSize, receiveSize); final TCPChannel<ByteBuffer> channel = new TCPChannel<>(pool.next(), processor, client, close); channel.open(); channel.register(); final Future<Void> future = channel.getConnectFuture(); try { future.get(); } catch (final InterruptedException | ExecutionException e) { channel.close(); throw new IOException(e); } accept.connectionAccepted(client.getRemoteAddress(), channel); } /** * {@inheritDoc} */ @Override public void close(final SelectorExecutor executor, final Callable<Void> closeTask) { executor.cancelBind(acceptKey, closeFuture, closeTask); } /** * {@inheritDoc} */ @Override public CallbackFuture<Void> getCloseFuture() { return closeFuture; } }