netty5/transport/src/test/java/io/netty/channel/ChannelInitializerTest.java
Norman Maurer c4dbbe39c9
Add executor() to ChannelOutboundInvoker and let it replace eventLoop() (#11617)
Motivation:

We should just add `executor()` to the `ChannelOutboundInvoker` interface and override this method in `Channel` to return `EventLoop`.

Modifications:

- Add `executor()` method to `ChannelOutboundInvoker`
- Let `Channel` override this method and return `EventLoop`.
- Adjust all usages of `eventLoop()`
- Add some default implementations

Result:

API cleanup
2021-08-25 18:31:24 +02:00

267 lines
9.8 KiB
Java

/*
* Copyright 2016 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:
*
* https://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;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalHandler;
import io.netty.channel.local.LocalServerChannel;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import java.nio.channels.ClosedChannelException;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ChannelInitializerTest {
private static final int TIMEOUT_MILLIS = 1000;
private static final LocalAddress SERVER_ADDRESS = new LocalAddress("addr");
private EventLoopGroup group;
private ServerBootstrap server;
private Bootstrap client;
private InspectableHandler testHandler;
@BeforeEach
public void setUp() {
group = new MultithreadEventLoopGroup(1, LocalHandler.newFactory());
server = new ServerBootstrap()
.group(group)
.channel(LocalServerChannel.class)
.localAddress(SERVER_ADDRESS);
client = new Bootstrap()
.group(group)
.channel(LocalChannel.class)
.handler(new ChannelHandler() { });
testHandler = new InspectableHandler();
}
@AfterEach
public void tearDown() {
group.shutdownGracefully(0, TIMEOUT_MILLIS, TimeUnit.MILLISECONDS).syncUninterruptibly();
}
@Test
public void testInitChannelThrowsRegisterFirst() {
testInitChannelThrows(true);
}
@Test
public void testInitChannelThrowsRegisterAfter() {
testInitChannelThrows(false);
}
private void testInitChannelThrows(boolean registerFirst) {
final Exception exception = new Exception();
final AtomicReference<Throwable> causeRef = new AtomicReference<>();
ChannelPipeline pipeline = new LocalChannel(group.next()).pipeline();
if (registerFirst) {
pipeline.channel().register().syncUninterruptibly();
}
pipeline.addFirst(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
throw exception;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
causeRef.set(cause);
super.exceptionCaught(ctx, cause);
}
});
if (!registerFirst) {
assertTrue(pipeline.channel().register().awaitUninterruptibly().cause() instanceof ClosedChannelException);
}
pipeline.channel().close().syncUninterruptibly();
pipeline.channel().closeFuture().syncUninterruptibly();
assertSame(exception, causeRef.get());
}
@Test
public void testChannelInitializerInInitializerCorrectOrdering() throws Exception {
final ChannelHandler handler1 = new ChannelHandler() { };
final ChannelHandler handler2 = new ChannelHandler() { };
final ChannelHandler handler3 = new ChannelHandler() { };
final ChannelHandler handler4 = new ChannelHandler() { };
client.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(handler1);
ch.pipeline().addLast(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(handler2);
ch.pipeline().addLast(handler3);
}
});
ch.pipeline().addLast(handler4);
}
}).localAddress(LocalAddress.ANY);
Channel channel = client.bind().get();
try {
// Execute some task on the EventLoop and wait until its done to be sure all handlers are added to the
// pipeline.
channel.executor().submit(() -> {
// NOOP
}).syncUninterruptibly();
Iterator<Map.Entry<String, ChannelHandler>> handlers = channel.pipeline().iterator();
assertSame(handler1, handlers.next().getValue());
assertSame(handler2, handlers.next().getValue());
assertSame(handler3, handlers.next().getValue());
assertSame(handler4, handlers.next().getValue());
assertFalse(handlers.hasNext());
} finally {
channel.close().syncUninterruptibly();
}
}
@Test
public void testChannelInitializerReentrance() throws Exception {
final AtomicInteger registeredCalled = new AtomicInteger(0);
final ChannelHandler handler1 = new ChannelHandler() {
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
registeredCalled.incrementAndGet();
}
};
final AtomicInteger initChannelCalled = new AtomicInteger(0);
client.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
initChannelCalled.incrementAndGet();
ch.pipeline().addLast(handler1);
ch.pipeline().fireChannelRegistered();
}
}).localAddress(LocalAddress.ANY);
Channel channel = client.bind().get();
try {
// Execute some task on the EventLoop and wait until its done to be sure all handlers are added to the
// pipeline.
channel.executor().submit(() -> {
// NOOP
}).syncUninterruptibly();
assertEquals(1, initChannelCalled.get());
assertEquals(2, registeredCalled.get());
} finally {
channel.close().syncUninterruptibly();
}
}
@Test
@Timeout(value = TIMEOUT_MILLIS, unit = TimeUnit.MILLISECONDS)
public void firstHandlerInPipelineShouldReceiveChannelRegisteredEvent() throws Exception {
testChannelRegisteredEventPropagation(new ChannelInitializer<LocalChannel>() {
@Override
public void initChannel(LocalChannel channel) {
channel.pipeline().addFirst(testHandler);
}
});
}
@Test
@Timeout(value = TIMEOUT_MILLIS, unit = TimeUnit.MILLISECONDS)
public void lastHandlerInPipelineShouldReceiveChannelRegisteredEvent() throws Exception {
testChannelRegisteredEventPropagation(new ChannelInitializer<LocalChannel>() {
@Override
public void initChannel(LocalChannel channel) {
channel.pipeline().addLast(testHandler);
}
});
}
@Test
public void testAddFirstChannelInitializer() {
testAddChannelInitializer(true);
}
@Test
public void testAddLastChannelInitializer() {
testAddChannelInitializer(false);
}
private static void testAddChannelInitializer(final boolean first) {
final AtomicBoolean called = new AtomicBoolean();
EmbeddedChannel channel = new EmbeddedChannel(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelHandler handler = new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
called.set(true);
}
};
if (first) {
ch.pipeline().addFirst(handler);
} else {
ch.pipeline().addLast(handler);
}
}
});
channel.finish();
assertTrue(called.get());
}
private void testChannelRegisteredEventPropagation(ChannelInitializer<LocalChannel> init) throws Exception {
Channel clientChannel = null, serverChannel = null;
try {
server.childHandler(init);
serverChannel = server.bind().get();
clientChannel = client.connect(SERVER_ADDRESS).get();
assertEquals(1, testHandler.channelRegisteredCount.get());
} finally {
closeChannel(clientChannel);
closeChannel(serverChannel);
}
}
private static void closeChannel(Channel c) {
if (c != null) {
c.close().syncUninterruptibly();
}
}
private static final class InspectableHandler implements ChannelHandler {
final AtomicInteger channelRegisteredCount = new AtomicInteger(0);
@Override
public void channelRegistered(ChannelHandlerContext ctx) {
channelRegisteredCount.incrementAndGet();
ctx.fireChannelRegistered();
}
}
}