Implement the local transport
- Replace the old local example with localecho example - Channel's outbound buffer is guaranteed to be created on construction time.
This commit is contained in:
parent
392623749e
commit
a53ecbf5f1
|
@ -31,16 +31,15 @@ import java.util.Queue;
|
||||||
class EmbeddedChannel extends AbstractChannel {
|
class EmbeddedChannel extends AbstractChannel {
|
||||||
|
|
||||||
private final ChannelConfig config = new DefaultChannelConfig();
|
private final ChannelConfig config = new DefaultChannelConfig();
|
||||||
private final ChannelBufferHolder<?> firstOut;
|
|
||||||
private final SocketAddress localAddress = new EmbeddedSocketAddress();
|
private final SocketAddress localAddress = new EmbeddedSocketAddress();
|
||||||
private final SocketAddress remoteAddress = new EmbeddedSocketAddress();
|
private final SocketAddress remoteAddress = new EmbeddedSocketAddress();
|
||||||
private final Queue<Object> productQueue;
|
private final Queue<Object> productQueue;
|
||||||
private int state; // 0 = OPEN, 1 = ACTIVE, 2 = CLOSED
|
private int state; // 0 = OPEN, 1 = ACTIVE, 2 = CLOSED
|
||||||
|
|
||||||
EmbeddedChannel(Queue<Object> productQueue) {
|
EmbeddedChannel(Queue<Object> productQueue) {
|
||||||
super(null, null);
|
super(null, null, ChannelBufferHolders.catchAllBuffer(
|
||||||
|
productQueue, ChannelBuffers.dynamicBuffer()));
|
||||||
this.productQueue = productQueue;
|
this.productQueue = productQueue;
|
||||||
firstOut = ChannelBufferHolders.catchAllBuffer(productQueue, ChannelBuffers.dynamicBuffer());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -63,12 +62,6 @@ class EmbeddedChannel extends AbstractChannel {
|
||||||
return loop instanceof EmbeddedEventLoop;
|
return loop instanceof EmbeddedEventLoop;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected ChannelBufferHolder<Object> firstOut() {
|
|
||||||
return (ChannelBufferHolder<Object>) firstOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SocketAddress localAddress0() {
|
protected SocketAddress localAddress0() {
|
||||||
return isActive()? localAddress : null;
|
return isActive()? localAddress : null;
|
||||||
|
|
|
@ -1,109 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2011 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.example.local;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
import io.netty.bootstrap.ClientBootstrap;
|
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelPipelineFactory;
|
|
||||||
import io.netty.channel.Channels;
|
|
||||||
import io.netty.channel.local.DefaultLocalClientChannelFactory;
|
|
||||||
import io.netty.channel.local.DefaultLocalServerChannelFactory;
|
|
||||||
import io.netty.channel.local.LocalAddress;
|
|
||||||
import io.netty.example.echo.EchoServerHandler;
|
|
||||||
import io.netty.handler.codec.string.StringDecoder;
|
|
||||||
import io.netty.handler.codec.string.StringEncoder;
|
|
||||||
import io.netty.handler.logging.LoggingHandler;
|
|
||||||
import io.netty.logging.InternalLogLevel;
|
|
||||||
|
|
||||||
public class LocalExample {
|
|
||||||
|
|
||||||
private final String port;
|
|
||||||
|
|
||||||
public LocalExample(String port) {
|
|
||||||
this.port = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() throws IOException {
|
|
||||||
// Address to bind on / connect to.
|
|
||||||
LocalAddress socketAddress = new LocalAddress(port);
|
|
||||||
|
|
||||||
// Configure the server.
|
|
||||||
ServerBootstrap sb = new ServerBootstrap(
|
|
||||||
new DefaultLocalServerChannelFactory());
|
|
||||||
|
|
||||||
// Set up the default server-side event pipeline.
|
|
||||||
EchoServerHandler handler = new EchoServerHandler();
|
|
||||||
sb.pipeline().addLast("handler", handler);
|
|
||||||
|
|
||||||
// Start up the server.
|
|
||||||
sb.bind(socketAddress);
|
|
||||||
|
|
||||||
// Configure the client.
|
|
||||||
ClientBootstrap cb = new ClientBootstrap(
|
|
||||||
new DefaultLocalClientChannelFactory());
|
|
||||||
|
|
||||||
// Set up the client-side pipeline factory.
|
|
||||||
cb.setPipelineFactory(new ChannelPipelineFactory() {
|
|
||||||
public ChannelPipeline getPipeline() throws Exception {
|
|
||||||
return Channels.pipeline(
|
|
||||||
new StringDecoder(),
|
|
||||||
new StringEncoder(),
|
|
||||||
new LoggingHandler(InternalLogLevel.INFO));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Make the connection attempt to the server.
|
|
||||||
ChannelFuture channelFuture = cb.connect(socketAddress);
|
|
||||||
channelFuture.awaitUninterruptibly();
|
|
||||||
|
|
||||||
// Read commands from the stdin.
|
|
||||||
System.out.println("Enter text (quit to end)");
|
|
||||||
ChannelFuture lastWriteFuture = null;
|
|
||||||
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
|
|
||||||
for (; ;) {
|
|
||||||
String line = in.readLine();
|
|
||||||
if (line == null || "quit".equalsIgnoreCase(line)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sends the received line to the server.
|
|
||||||
lastWriteFuture = channelFuture.channel().write(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until all messages are flushed before closing the channel.
|
|
||||||
if (lastWriteFuture != null) {
|
|
||||||
lastWriteFuture.awaitUninterruptibly();
|
|
||||||
}
|
|
||||||
channelFuture.channel().close();
|
|
||||||
|
|
||||||
// Wait until the connection is closed or the connection attempt fails.
|
|
||||||
channelFuture.channel().getCloseFuture().awaitUninterruptibly();
|
|
||||||
|
|
||||||
// Release all resources used by the local transport.
|
|
||||||
cb.releaseExternalResources();
|
|
||||||
sb.releaseExternalResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
|
||||||
new LocalExample("1").run();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,105 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2011 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.example.local;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import io.netty.bootstrap.ClientBootstrap;
|
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelPipelineFactory;
|
|
||||||
import io.netty.channel.Channels;
|
|
||||||
import io.netty.channel.local.DefaultLocalClientChannelFactory;
|
|
||||||
import io.netty.channel.local.DefaultLocalServerChannelFactory;
|
|
||||||
import io.netty.channel.local.LocalAddress;
|
|
||||||
import io.netty.handler.codec.string.StringDecoder;
|
|
||||||
import io.netty.handler.codec.string.StringEncoder;
|
|
||||||
import io.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;
|
|
||||||
import io.netty.handler.logging.LoggingHandler;
|
|
||||||
import io.netty.logging.InternalLogLevel;
|
|
||||||
|
|
||||||
public class LocalExampleMultithreaded {
|
|
||||||
|
|
||||||
private final String port;
|
|
||||||
|
|
||||||
public LocalExampleMultithreaded(String port) {
|
|
||||||
this.port = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
LocalAddress socketAddress = new LocalAddress(port);
|
|
||||||
|
|
||||||
OrderedMemoryAwareThreadPoolExecutor eventExecutor =
|
|
||||||
new OrderedMemoryAwareThreadPoolExecutor(
|
|
||||||
5, 1000000, 10000000, 100,
|
|
||||||
TimeUnit.MILLISECONDS);
|
|
||||||
|
|
||||||
ServerBootstrap sb = new ServerBootstrap(
|
|
||||||
new DefaultLocalServerChannelFactory());
|
|
||||||
|
|
||||||
sb.setPipelineFactory(new LocalServerPipelineFactory(eventExecutor));
|
|
||||||
sb.bind(socketAddress);
|
|
||||||
|
|
||||||
ClientBootstrap cb = new ClientBootstrap(
|
|
||||||
new DefaultLocalClientChannelFactory());
|
|
||||||
|
|
||||||
cb.setPipelineFactory(new ChannelPipelineFactory() {
|
|
||||||
public ChannelPipeline getPipeline() throws Exception {
|
|
||||||
return Channels.pipeline(
|
|
||||||
new StringDecoder(),
|
|
||||||
new StringEncoder(),
|
|
||||||
new LoggingHandler(InternalLogLevel.INFO));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Read commands from array
|
|
||||||
String[] commands = { "First", "Second", "Third", "quit" };
|
|
||||||
for (int j = 0; j < 5 ; j++) {
|
|
||||||
System.err.println("Start " + j);
|
|
||||||
ChannelFuture channelFuture = cb.connect(socketAddress);
|
|
||||||
channelFuture.awaitUninterruptibly();
|
|
||||||
if (! channelFuture.isSuccess()) {
|
|
||||||
System.err.println("CANNOT CONNECT");
|
|
||||||
channelFuture.cause().printStackTrace();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ChannelFuture lastWriteFuture = null;
|
|
||||||
for (String line: commands) {
|
|
||||||
// Sends the received line to the server.
|
|
||||||
lastWriteFuture = channelFuture.channel().write(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until all messages are flushed before closing the channel.
|
|
||||||
if (lastWriteFuture != null) {
|
|
||||||
lastWriteFuture.awaitUninterruptibly();
|
|
||||||
}
|
|
||||||
channelFuture.channel().close();
|
|
||||||
// Wait until the connection is closed or the connection attempt fails.
|
|
||||||
channelFuture.channel().getCloseFuture().awaitUninterruptibly();
|
|
||||||
System.err.println("End " + j);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release all resources
|
|
||||||
cb.releaseExternalResources();
|
|
||||||
sb.releaseExternalResources();
|
|
||||||
eventExecutor.shutdownNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
|
||||||
new LocalExampleMultithreaded("1").run();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2011 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.example.local;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelDownstreamHandler;
|
|
||||||
import io.netty.channel.ChannelEvent;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelPipelineFactory;
|
|
||||||
import io.netty.channel.ChannelUpstreamHandler;
|
|
||||||
import io.netty.channel.Channels;
|
|
||||||
import io.netty.channel.MessageEvent;
|
|
||||||
import io.netty.handler.codec.string.StringDecoder;
|
|
||||||
import io.netty.handler.codec.string.StringEncoder;
|
|
||||||
import io.netty.handler.execution.ExecutionHandler;
|
|
||||||
|
|
||||||
public class LocalServerPipelineFactory implements ChannelPipelineFactory {
|
|
||||||
|
|
||||||
private final ExecutionHandler executionHandler;
|
|
||||||
|
|
||||||
public LocalServerPipelineFactory(Executor eventExecutor) {
|
|
||||||
executionHandler = new ExecutionHandler(eventExecutor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChannelPipeline getPipeline() throws Exception {
|
|
||||||
final ChannelPipeline pipeline = Channels.pipeline();
|
|
||||||
pipeline.addLast("decoder", new StringDecoder());
|
|
||||||
pipeline.addLast("encoder", new StringEncoder());
|
|
||||||
pipeline.addLast("executor", executionHandler);
|
|
||||||
pipeline.addLast("handler", new EchoCloseServerHandler());
|
|
||||||
return pipeline;
|
|
||||||
}
|
|
||||||
|
|
||||||
static class EchoCloseServerHandler implements ChannelUpstreamHandler, ChannelDownstreamHandler {
|
|
||||||
@Override
|
|
||||||
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
|
|
||||||
throws Exception {
|
|
||||||
if (e instanceof MessageEvent) {
|
|
||||||
final MessageEvent evt = (MessageEvent) e;
|
|
||||||
String msg = (String) evt.getMessage();
|
|
||||||
if (msg.equalsIgnoreCase("quit")) {
|
|
||||||
Channels.close(e.getChannel());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.sendUpstream(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e) {
|
|
||||||
if (e instanceof MessageEvent) {
|
|
||||||
final MessageEvent evt = (MessageEvent) e;
|
|
||||||
String msg = (String) evt.getMessage();
|
|
||||||
if (msg.equalsIgnoreCase("quit")) {
|
|
||||||
Channels.close(e.getChannel());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
System.err.println("SERVER:" + msg);
|
|
||||||
// Write back
|
|
||||||
Channels.write(e.getChannel(), msg);
|
|
||||||
}
|
|
||||||
ctx.sendDownstream(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
114
example/src/main/java/io/netty/example/localecho/LocalEcho.java
Normal file
114
example/src/main/java/io/netty/example/localecho/LocalEcho.java
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2011 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.example.localecho;
|
||||||
|
|
||||||
|
import io.netty.bootstrap.Bootstrap;
|
||||||
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.local.LocalAddress;
|
||||||
|
import io.netty.channel.local.LocalChannel;
|
||||||
|
import io.netty.channel.local.LocalServerChannel;
|
||||||
|
import io.netty.channel.socket.nio.NioEventLoop;
|
||||||
|
import io.netty.handler.logging.LogLevel;
|
||||||
|
import io.netty.handler.logging.LoggingHandler;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
|
public class LocalEcho {
|
||||||
|
|
||||||
|
private final String port;
|
||||||
|
|
||||||
|
public LocalEcho(String port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() throws Exception {
|
||||||
|
// Address to bind on / connect to.
|
||||||
|
final LocalAddress addr = new LocalAddress(port);
|
||||||
|
|
||||||
|
Bootstrap cb = new Bootstrap();
|
||||||
|
ServerBootstrap sb = new ServerBootstrap();
|
||||||
|
try {
|
||||||
|
// Note that we can use any event loop so that you can ensure certain local channels
|
||||||
|
// are handled by the same event loop thread which drives a certain socket channel.
|
||||||
|
sb.eventLoop(new NioEventLoop(), new NioEventLoop())
|
||||||
|
.channel(new LocalServerChannel())
|
||||||
|
.localAddress(addr)
|
||||||
|
.initializer(new ChannelInitializer<LocalServerChannel>() {
|
||||||
|
@Override
|
||||||
|
public void initChannel(LocalServerChannel ch) throws Exception {
|
||||||
|
ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.childInitializer(new ChannelInitializer<LocalChannel>() {
|
||||||
|
@Override
|
||||||
|
public void initChannel(LocalChannel ch) throws Exception {
|
||||||
|
ch.pipeline().addLast(
|
||||||
|
new LoggingHandler(LogLevel.INFO),
|
||||||
|
new LocalEchoServerHandler());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cb.eventLoop(new NioEventLoop())
|
||||||
|
.channel(new LocalChannel())
|
||||||
|
.remoteAddress(addr)
|
||||||
|
.initializer(new ChannelInitializer<LocalChannel>() {
|
||||||
|
@Override
|
||||||
|
public void initChannel(LocalChannel ch) throws Exception {
|
||||||
|
ch.pipeline().addLast(
|
||||||
|
new LoggingHandler(LogLevel.INFO),
|
||||||
|
new LocalEchoClientHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Channel sch = sb.bind().sync().channel();
|
||||||
|
Channel ch = cb.connect().sync().channel();
|
||||||
|
|
||||||
|
// Read commands from the stdin.
|
||||||
|
System.out.println("Enter text (quit to end)");
|
||||||
|
ChannelFuture lastWriteFuture = null;
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
|
||||||
|
for (; ;) {
|
||||||
|
String line = in.readLine();
|
||||||
|
if (line == null || "quit".equalsIgnoreCase(line)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sends the received line to the server.
|
||||||
|
lastWriteFuture = ch.write(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until all messages are flushed before closing the channel.
|
||||||
|
if (lastWriteFuture != null) {
|
||||||
|
lastWriteFuture.awaitUninterruptibly();
|
||||||
|
}
|
||||||
|
|
||||||
|
ch.close().sync();
|
||||||
|
sch.close().sync();
|
||||||
|
} finally {
|
||||||
|
sb.shutdown();
|
||||||
|
cb.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
new LocalEcho("1").run();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package io.netty.example.localecho;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelInboundHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
|
|
||||||
|
public class LocalEchoClientHandler extends ChannelInboundMessageHandlerAdapter<String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void messageReceived(ChannelInboundHandlerContext<String> ctx, String msg) {
|
||||||
|
// Print as received
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelInboundHandlerContext<String> ctx, Throwable cause) {
|
||||||
|
cause.printStackTrace();
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package io.netty.example.localecho;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelInboundHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||||
|
|
||||||
|
public class LocalEchoServerHandler extends ChannelInboundMessageHandlerAdapter<String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void messageReceived(ChannelInboundHandlerContext<String> ctx, String msg) {
|
||||||
|
// Write back as received
|
||||||
|
ctx.write(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(ChannelInboundHandlerContext<String> ctx, Throwable cause) {
|
||||||
|
cause.printStackTrace();
|
||||||
|
ctx.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -80,7 +80,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||||
private volatile EventLoop eventLoop;
|
private volatile EventLoop eventLoop;
|
||||||
private volatile boolean registered;
|
private volatile boolean registered;
|
||||||
|
|
||||||
private final ChannelBufferHolder<Object> outbound;
|
private final ChannelBufferHolder<Object> directOutbound;
|
||||||
private ClosedChannelException closedChannelException;
|
private ClosedChannelException closedChannelException;
|
||||||
private final Deque<FlushCheckpoint> flushCheckpoints = new ArrayDeque<FlushCheckpoint>();
|
private final Deque<FlushCheckpoint> flushCheckpoints = new ArrayDeque<FlushCheckpoint>();
|
||||||
private long writeCounter;
|
private long writeCounter;
|
||||||
|
@ -102,7 +102,11 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||||
* the parent of this channel. {@code null} if there's no parent.
|
* the parent of this channel. {@code null} if there's no parent.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected AbstractChannel(Channel parent, Integer id) {
|
protected AbstractChannel(Channel parent, Integer id, ChannelBufferHolder<?> outboundBuffer) {
|
||||||
|
if (outboundBuffer == null) {
|
||||||
|
throw new NullPointerException("outboundBuffer");
|
||||||
|
}
|
||||||
|
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
id = allocateId(this);
|
id = allocateId(this);
|
||||||
} else {
|
} else {
|
||||||
|
@ -117,7 +121,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
unsafe = newUnsafe();
|
unsafe = newUnsafe();
|
||||||
outbound = (ChannelBufferHolder<Object>) newOutboundBuffer();
|
directOutbound = (ChannelBufferHolder<Object>) outboundBuffer;
|
||||||
|
|
||||||
closeFuture().addListener(new ChannelFutureListener() {
|
closeFuture().addListener(new ChannelFutureListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -370,27 +374,27 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||||
private final Runnable flushLaterTask = new FlushLater();
|
private final Runnable flushLaterTask = new FlushLater();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelBufferHolder<Object> directOutbound() {
|
public final ChannelBufferHolder<Object> directOutbound() {
|
||||||
return outbound;
|
return directOutbound;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture voidFuture() {
|
public final ChannelFuture voidFuture() {
|
||||||
return voidFuture;
|
return voidFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SocketAddress localAddress() {
|
public final SocketAddress localAddress() {
|
||||||
return localAddress0();
|
return localAddress0();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SocketAddress remoteAddress() {
|
public final SocketAddress remoteAddress() {
|
||||||
return remoteAddress0();
|
return remoteAddress0();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void register(EventLoop eventLoop, ChannelFuture future) {
|
public final void register(EventLoop eventLoop, ChannelFuture future) {
|
||||||
if (eventLoop == null) {
|
if (eventLoop == null) {
|
||||||
throw new NullPointerException("eventLoop");
|
throw new NullPointerException("eventLoop");
|
||||||
}
|
}
|
||||||
|
@ -432,7 +436,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bind(final SocketAddress localAddress, final ChannelFuture future) {
|
public final void bind(final SocketAddress localAddress, final ChannelFuture future) {
|
||||||
if (eventLoop().inEventLoop()) {
|
if (eventLoop().inEventLoop()) {
|
||||||
if (!ensureOpen(future)) {
|
if (!ensureOpen(future)) {
|
||||||
return;
|
return;
|
||||||
|
@ -461,7 +465,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnect(final ChannelFuture future) {
|
public final void disconnect(final ChannelFuture future) {
|
||||||
if (eventLoop().inEventLoop()) {
|
if (eventLoop().inEventLoop()) {
|
||||||
try {
|
try {
|
||||||
boolean wasActive = isActive();
|
boolean wasActive = isActive();
|
||||||
|
@ -485,7 +489,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close(final ChannelFuture future) {
|
public final void close(final ChannelFuture future) {
|
||||||
if (eventLoop().inEventLoop()) {
|
if (eventLoop().inEventLoop()) {
|
||||||
if (closeFuture.setClosed()) {
|
if (closeFuture.setClosed()) {
|
||||||
boolean wasActive = isActive();
|
boolean wasActive = isActive();
|
||||||
|
@ -522,16 +526,28 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deregister(final ChannelFuture future) {
|
public final void deregister(final ChannelFuture future) {
|
||||||
if (eventLoop().inEventLoop()) {
|
if (eventLoop().inEventLoop()) {
|
||||||
|
if (!registered) {
|
||||||
|
future.setSuccess();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
doDeregister();
|
doDeregister();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
logger.warn("Unexpected exception occurred while deregistering a channel.", t);
|
logger.warn("Unexpected exception occurred while deregistering a channel.", t);
|
||||||
} finally {
|
} finally {
|
||||||
registered = false;
|
if (registered) {
|
||||||
future.setSuccess();
|
registered = false;
|
||||||
pipeline().fireChannelUnregistered();
|
future.setSuccess();
|
||||||
|
pipeline().fireChannelUnregistered();
|
||||||
|
} else {
|
||||||
|
// Some transports like local and AIO does not allow the deregistration of
|
||||||
|
// an open channel. Their doDeregister() calls close(). Consequently,
|
||||||
|
// close() calls deregister() again - no need to fire channelUnregistered.
|
||||||
|
future.setSuccess();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eventLoop().execute(new Runnable() {
|
eventLoop().execute(new Runnable() {
|
||||||
|
@ -594,7 +610,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flushNow() {
|
public final void flushNow() {
|
||||||
if (inFlushNow) {
|
if (inFlushNow) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -630,7 +646,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean ensureOpen(ChannelFuture future) {
|
protected final boolean ensureOpen(ChannelFuture future) {
|
||||||
if (isOpen()) {
|
if (isOpen()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -641,7 +657,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void closeIfClosed() {
|
protected final void closeIfClosed() {
|
||||||
if (isOpen()) {
|
if (isOpen()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -659,8 +675,6 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||||
|
|
||||||
protected abstract boolean isCompatible(EventLoop loop);
|
protected abstract boolean isCompatible(EventLoop loop);
|
||||||
|
|
||||||
protected abstract ChannelBufferHolder<?> newOutboundBuffer();
|
|
||||||
|
|
||||||
protected abstract SocketAddress localAddress0();
|
protected abstract SocketAddress localAddress0();
|
||||||
protected abstract SocketAddress remoteAddress0();
|
protected abstract SocketAddress remoteAddress0();
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ public abstract class AbstractServerChannel extends AbstractChannel implements S
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
*/
|
*/
|
||||||
protected AbstractServerChannel(Integer id) {
|
protected AbstractServerChannel(Integer id) {
|
||||||
super(null, id);
|
super(null, id, ChannelBufferHolders.discardBuffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -47,8 +47,8 @@ public abstract class AbstractServerChannel extends AbstractChannel implements S
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ChannelBufferHolder<Object> newOutboundBuffer() {
|
protected Unsafe newUnsafe() {
|
||||||
return ChannelBufferHolders.discardBuffer();
|
return new DefaultServerUnsafe();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -60,4 +60,53 @@ public abstract class AbstractServerChannel extends AbstractChannel implements S
|
||||||
protected void doDisconnect() throws Exception {
|
protected void doDisconnect() throws Exception {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFlush(ChannelBufferHolder<Object> buf) throws Exception {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isFlushPending() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class DefaultServerUnsafe extends AbstractUnsafe {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush(final ChannelFuture future) {
|
||||||
|
if (eventLoop().inEventLoop()) {
|
||||||
|
reject(future);
|
||||||
|
} else {
|
||||||
|
eventLoop().execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
flush(future);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connect(
|
||||||
|
final SocketAddress remoteAddress, final SocketAddress localAddress,
|
||||||
|
final ChannelFuture future) {
|
||||||
|
if (eventLoop().inEventLoop()) {
|
||||||
|
reject(future);
|
||||||
|
} else {
|
||||||
|
eventLoop().execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
connect(remoteAddress, localAddress, future);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reject(ChannelFuture future) {
|
||||||
|
Exception cause = new UnsupportedOperationException();
|
||||||
|
future.setFailure(cause);
|
||||||
|
pipeline().fireExceptionCaught(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,12 @@ public final class ChannelBufferHolder<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelBufferHolder(Queue<E> msgBuf, ChannelBuffer byteBuf) {
|
ChannelBufferHolder(Queue<E> msgBuf, ChannelBuffer byteBuf) {
|
||||||
|
if (msgBuf == null) {
|
||||||
|
throw new NullPointerException("msgBuf");
|
||||||
|
}
|
||||||
|
if (byteBuf == null) {
|
||||||
|
throw new NullPointerException("byteBuf");
|
||||||
|
}
|
||||||
ctx = null;
|
ctx = null;
|
||||||
bypassDirection = 0;
|
bypassDirection = 0;
|
||||||
this.msgBuf = msgBuf;
|
this.msgBuf = msgBuf;
|
||||||
|
|
|
@ -1,218 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2011 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.local;
|
|
||||||
|
|
||||||
import static io.netty.channel.Channels.*;
|
|
||||||
|
|
||||||
import java.nio.channels.ClosedChannelException;
|
|
||||||
import java.nio.channels.NotYetConnectedException;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import io.netty.channel.AbstractChannel;
|
|
||||||
import io.netty.channel.ChannelConfig;
|
|
||||||
import io.netty.channel.ChannelException;
|
|
||||||
import io.netty.channel.ChannelFactory;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelSink;
|
|
||||||
import io.netty.channel.DefaultChannelConfig;
|
|
||||||
import io.netty.channel.MessageEvent;
|
|
||||||
import io.netty.util.internal.QueueFactory;
|
|
||||||
import io.netty.util.internal.ThreadLocalBoolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
final class DefaultLocalChannel extends AbstractChannel implements LocalChannel {
|
|
||||||
|
|
||||||
// TODO Move the state management up to AbstractChannel to remove duplication.
|
|
||||||
private static final int ST_OPEN = 0;
|
|
||||||
private static final int ST_BOUND = 1;
|
|
||||||
private static final int ST_CONNECTED = 2;
|
|
||||||
private static final int ST_CLOSED = -1;
|
|
||||||
final AtomicInteger state = new AtomicInteger(ST_OPEN);
|
|
||||||
|
|
||||||
private final ChannelConfig config;
|
|
||||||
private final ThreadLocalBoolean delivering = new ThreadLocalBoolean();
|
|
||||||
|
|
||||||
final Queue<MessageEvent> writeBuffer = QueueFactory.createQueue(MessageEvent.class);
|
|
||||||
|
|
||||||
volatile DefaultLocalChannel pairedChannel;
|
|
||||||
volatile LocalAddress localAddress;
|
|
||||||
volatile LocalAddress remoteAddress;
|
|
||||||
|
|
||||||
static DefaultLocalChannel create(LocalServerChannel parent,
|
|
||||||
ChannelFactory factory, ChannelPipeline pipeline, ChannelSink sink,
|
|
||||||
DefaultLocalChannel pairedChannel) {
|
|
||||||
DefaultLocalChannel instance = new DefaultLocalChannel(parent, factory, pipeline, sink,
|
|
||||||
pairedChannel);
|
|
||||||
fireChannelOpen(instance);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DefaultLocalChannel(LocalServerChannel parent, ChannelFactory factory, ChannelPipeline pipeline, ChannelSink sink, DefaultLocalChannel pairedChannel) {
|
|
||||||
super(parent, factory, pipeline, sink);
|
|
||||||
this.pairedChannel = pairedChannel;
|
|
||||||
config = new DefaultChannelConfig();
|
|
||||||
|
|
||||||
// TODO Move the state variable to AbstractChannel so that we don't need
|
|
||||||
// to add many listeners.
|
|
||||||
getCloseFuture().addListener(new ChannelFutureListener() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(ChannelFuture future) throws Exception {
|
|
||||||
state.set(ST_CLOSED);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChannelConfig getConfig() {
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isOpen() {
|
|
||||||
return state.get() >= ST_OPEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBound() {
|
|
||||||
return state.get() >= ST_BOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isConnected() {
|
|
||||||
return state.get() == ST_CONNECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBound() throws ClosedChannelException {
|
|
||||||
if (!state.compareAndSet(ST_OPEN, ST_BOUND)) {
|
|
||||||
switch (state.get()) {
|
|
||||||
case ST_CLOSED:
|
|
||||||
throw new ClosedChannelException();
|
|
||||||
default:
|
|
||||||
throw new ChannelException("already bound");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setConnected() {
|
|
||||||
if (state.get() != ST_CLOSED) {
|
|
||||||
state.set(ST_CONNECTED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean setClosed() {
|
|
||||||
return super.setClosed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LocalAddress getLocalAddress() {
|
|
||||||
return localAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LocalAddress getRemoteAddress() {
|
|
||||||
return remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
void closeNow(ChannelFuture future) {
|
|
||||||
LocalAddress localAddress = this.localAddress;
|
|
||||||
try {
|
|
||||||
// Close the self.
|
|
||||||
if (!setClosed()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultLocalChannel pairedChannel = this.pairedChannel;
|
|
||||||
if (pairedChannel != null) {
|
|
||||||
this.pairedChannel = null;
|
|
||||||
fireChannelDisconnected(this);
|
|
||||||
fireChannelUnbound(this);
|
|
||||||
}
|
|
||||||
fireChannelClosed(this);
|
|
||||||
|
|
||||||
// Close the peer.
|
|
||||||
if (pairedChannel == null || !pairedChannel.setClosed()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultLocalChannel me = pairedChannel.pairedChannel;
|
|
||||||
if (me != null) {
|
|
||||||
pairedChannel.pairedChannel = null;
|
|
||||||
fireChannelDisconnected(pairedChannel);
|
|
||||||
fireChannelUnbound(pairedChannel);
|
|
||||||
}
|
|
||||||
fireChannelClosed(pairedChannel);
|
|
||||||
} finally {
|
|
||||||
future.setSuccess();
|
|
||||||
if (localAddress != null && getParent() == null) {
|
|
||||||
LocalChannelRegistry.unregister(localAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void flushWriteBuffer() {
|
|
||||||
DefaultLocalChannel pairedChannel = this.pairedChannel;
|
|
||||||
if (pairedChannel != null) {
|
|
||||||
if (pairedChannel.isConnected()) {
|
|
||||||
// Channel is open and connected and channelConnected event has
|
|
||||||
// been fired.
|
|
||||||
if (!delivering.get()) {
|
|
||||||
delivering.set(true);
|
|
||||||
try {
|
|
||||||
for (;;) {
|
|
||||||
MessageEvent e = writeBuffer.poll();
|
|
||||||
if (e == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.getFuture().setSuccess();
|
|
||||||
fireMessageReceived(pairedChannel, e.getMessage());
|
|
||||||
fireWriteComplete(this, 1);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
delivering.set(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Channel is open and connected but channelConnected event has
|
|
||||||
// not been fired yet.
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Channel is closed or not connected yet - notify as failures.
|
|
||||||
Exception cause;
|
|
||||||
if (isOpen()) {
|
|
||||||
cause = new NotYetConnectedException();
|
|
||||||
} else {
|
|
||||||
cause = new ClosedChannelException();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
MessageEvent e = writeBuffer.poll();
|
|
||||||
if (e == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.getFuture().setFailure(cause);
|
|
||||||
fireExceptionCaught(this, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2011 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.local;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelSink;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The default {@link LocalClientChannelFactory} implementation.
|
|
||||||
* @apiviz.landmark
|
|
||||||
*/
|
|
||||||
public class DefaultLocalClientChannelFactory implements LocalClientChannelFactory {
|
|
||||||
|
|
||||||
private final ChannelSink sink;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance.
|
|
||||||
*/
|
|
||||||
public DefaultLocalClientChannelFactory() {
|
|
||||||
sink = new LocalClientChannelSink();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LocalChannel newChannel(ChannelPipeline pipeline) {
|
|
||||||
return DefaultLocalChannel.create(null, this, pipeline, sink, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does nothing because this implementation does not require any external
|
|
||||||
* resources.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void releaseExternalResources() {
|
|
||||||
// No external resources.
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2011 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.local;
|
|
||||||
|
|
||||||
import static io.netty.channel.Channels.*;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import io.netty.channel.AbstractServerChannel;
|
|
||||||
import io.netty.channel.ChannelConfig;
|
|
||||||
import io.netty.channel.ChannelFactory;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelSink;
|
|
||||||
import io.netty.channel.DefaultServerChannelConfig;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
final class DefaultLocalServerChannel extends AbstractServerChannel implements
|
|
||||||
LocalServerChannel {
|
|
||||||
|
|
||||||
final ChannelConfig channelConfig;
|
|
||||||
|
|
||||||
final AtomicBoolean bound = new AtomicBoolean();
|
|
||||||
|
|
||||||
volatile LocalAddress localAddress;
|
|
||||||
|
|
||||||
static DefaultLocalServerChannel create(ChannelFactory factory,
|
|
||||||
ChannelPipeline pipeline, ChannelSink sink) {
|
|
||||||
DefaultLocalServerChannel instance =
|
|
||||||
new DefaultLocalServerChannel(factory, pipeline, sink);
|
|
||||||
fireChannelOpen(instance);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DefaultLocalServerChannel(ChannelFactory factory,
|
|
||||||
ChannelPipeline pipeline, ChannelSink sink) {
|
|
||||||
super(factory, pipeline, sink);
|
|
||||||
channelConfig = new DefaultServerChannelConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChannelConfig getConfig() {
|
|
||||||
return channelConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBound() {
|
|
||||||
return isOpen() && bound.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LocalAddress getLocalAddress() {
|
|
||||||
return isBound() ? localAddress : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LocalAddress getRemoteAddress() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean setClosed() {
|
|
||||||
return super.setClosed();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2011 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.local;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelSink;
|
|
||||||
import io.netty.channel.group.DefaultChannelGroup;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The default {@link LocalServerChannelFactory} implementation.
|
|
||||||
* @apiviz.landmark
|
|
||||||
*/
|
|
||||||
public class DefaultLocalServerChannelFactory implements LocalServerChannelFactory {
|
|
||||||
|
|
||||||
private final DefaultChannelGroup group = new DefaultChannelGroup();
|
|
||||||
|
|
||||||
private final ChannelSink sink = new LocalServerChannelSink();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LocalServerChannel newChannel(ChannelPipeline pipeline) {
|
|
||||||
LocalServerChannel channel = DefaultLocalServerChannel.create(this, pipeline, sink);
|
|
||||||
group.add(channel);
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release all the previous created channels. This takes care of calling {@link LocalChannelRegistry#unregister(LocalAddress)}
|
|
||||||
* for each if them.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void releaseExternalResources() {
|
|
||||||
group.close().awaitUninterruptibly();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,34 +15,37 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.channel.local;
|
package io.netty.channel.local;
|
||||||
|
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An endpoint in the local transport. Each endpoint is identified by a unique
|
* An endpoint in the local transport. Each endpoint is identified by a unique
|
||||||
* case-insensitive string, except for the pre-defined value called
|
* case-insensitive string.
|
||||||
* {@code "ephemeral"}.
|
|
||||||
*
|
*
|
||||||
* <h3>Ephemeral Address</h3>
|
|
||||||
*
|
|
||||||
* An ephemeral address is an anonymous address which is assigned temporarily
|
|
||||||
* and is released as soon as the connection is closed. All ephemeral addresses
|
|
||||||
* have the same ID, {@code "ephemeral"}, but they are not equal to each other.
|
|
||||||
* @apiviz.landmark
|
* @apiviz.landmark
|
||||||
*/
|
*/
|
||||||
public final class LocalAddress extends SocketAddress implements Comparable<LocalAddress> {
|
public final class LocalAddress extends SocketAddress implements Comparable<LocalAddress> {
|
||||||
|
|
||||||
private static final long serialVersionUID = -3601961747680808645L;
|
private static final long serialVersionUID = 4644331421130916435L;
|
||||||
|
|
||||||
public static final String EPHEMERAL = "ephemeral";
|
public static final LocalAddress ANY = new LocalAddress("ANY");
|
||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
private final boolean ephemeral;
|
private final String strVal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance with the specified ID.
|
* Creates a new ephemeral port based on the ID of the specified channel.
|
||||||
|
* Note that we prepend an upper-case character so that it never conflicts with
|
||||||
|
* the addresses created by a user, which are always lower-cased on construction time.
|
||||||
*/
|
*/
|
||||||
public LocalAddress(int id) {
|
LocalAddress(Channel channel) {
|
||||||
this(String.valueOf(id));
|
StringBuilder buf = new StringBuilder(16);
|
||||||
|
buf.append("local:E");
|
||||||
|
buf.append(Long.toHexString(channel.id().intValue() & 0xFFFFFFFFL | 0x100000000L));
|
||||||
|
buf.setCharAt(7, ':');
|
||||||
|
id = buf.substring(6);
|
||||||
|
strVal = buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,30 +60,19 @@ public final class LocalAddress extends SocketAddress implements Comparable<Loca
|
||||||
throw new IllegalArgumentException("empty id");
|
throw new IllegalArgumentException("empty id");
|
||||||
}
|
}
|
||||||
this.id = id;
|
this.id = id;
|
||||||
ephemeral = id.equals("ephemeral");
|
strVal = "local:" + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ID of this address.
|
* Returns the ID of this address.
|
||||||
*/
|
*/
|
||||||
public String getId() {
|
public String id() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns {@code true} if and only if this address is ephemeral.
|
|
||||||
*/
|
|
||||||
public boolean isEphemeral() {
|
|
||||||
return ephemeral;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
if (ephemeral) {
|
return id.hashCode();
|
||||||
return System.identityHashCode(this);
|
|
||||||
} else {
|
|
||||||
return id.hashCode();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -89,50 +81,16 @@ public final class LocalAddress extends SocketAddress implements Comparable<Loca
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ephemeral) {
|
return id.equals(((LocalAddress) o).id);
|
||||||
return this == o;
|
|
||||||
} else {
|
|
||||||
return getId().equals(((LocalAddress) o).getId());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: This comparison is broken! Assign distinct port numbers for
|
|
||||||
// ephemeral ports, just like O/S does for port number 0. It will
|
|
||||||
// break backward compatibility though.
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(LocalAddress o) {
|
public int compareTo(LocalAddress o) {
|
||||||
if (ephemeral) {
|
return id.compareTo(o.id);
|
||||||
if (o.ephemeral) {
|
|
||||||
if (this == o) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int a = System.identityHashCode(this);
|
|
||||||
int b = System.identityHashCode(o);
|
|
||||||
if (a < b) {
|
|
||||||
return -1;
|
|
||||||
} else if (a > b) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
"Two different ephemeral addresses have " +
|
|
||||||
"same identityHashCode.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (o.ephemeral) {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return getId().compareTo(o.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "local:" + getId();
|
return strVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,14 +15,252 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.channel.local;
|
package io.netty.channel.local;
|
||||||
|
|
||||||
|
import io.netty.channel.AbstractChannel;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelBufferHolder;
|
||||||
|
import io.netty.channel.ChannelBufferHolders;
|
||||||
|
import io.netty.channel.ChannelConfig;
|
||||||
|
import io.netty.channel.ChannelException;
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
import io.netty.channel.DefaultChannelConfig;
|
||||||
|
import io.netty.channel.EventLoop;
|
||||||
|
import io.netty.channel.SingleThreadEventLoop;
|
||||||
|
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.nio.channels.AlreadyConnectedException;
|
||||||
|
import java.nio.channels.ClosedChannelException;
|
||||||
|
import java.nio.channels.ConnectionPendingException;
|
||||||
|
import java.nio.channels.NotYetConnectedException;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link Channel} for the local transport.
|
* A {@link Channel} for the local transport.
|
||||||
*/
|
*/
|
||||||
public interface LocalChannel extends Channel {
|
public class LocalChannel extends AbstractChannel {
|
||||||
|
|
||||||
|
private final ChannelConfig config = new DefaultChannelConfig();
|
||||||
|
|
||||||
|
private volatile int state; // 0 - open, 1 - bound, 2 - connected, 3 - closed
|
||||||
|
private volatile LocalChannel peer;
|
||||||
|
private volatile LocalAddress localAddress;
|
||||||
|
private volatile LocalAddress remoteAddress;
|
||||||
|
private volatile ChannelFuture connectFuture;
|
||||||
|
|
||||||
|
public LocalChannel() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalChannel(Integer id) {
|
||||||
|
super(null, id, ChannelBufferHolders.messageBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalChannel(LocalServerChannel parent, LocalChannel peer) {
|
||||||
|
super(parent, null, ChannelBufferHolders.messageBuffer());
|
||||||
|
this.peer = peer;
|
||||||
|
localAddress = parent.localAddress();
|
||||||
|
remoteAddress = peer.localAddress();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
LocalAddress getLocalAddress();
|
public ChannelConfig config() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
LocalAddress getRemoteAddress();
|
public LocalServerChannel parent() {
|
||||||
|
return (LocalServerChannel) super.parent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocalAddress localAddress() {
|
||||||
|
return (LocalAddress) super.localAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocalAddress remoteAddress() {
|
||||||
|
return (LocalAddress) super.remoteAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOpen() {
|
||||||
|
return state < 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive() {
|
||||||
|
return state == 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Unsafe newUnsafe() {
|
||||||
|
return new LocalUnsafe();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isCompatible(EventLoop loop) {
|
||||||
|
return loop instanceof SingleThreadEventLoop;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SocketAddress localAddress0() {
|
||||||
|
return localAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SocketAddress remoteAddress0() {
|
||||||
|
return remoteAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doRegister() throws Exception {
|
||||||
|
if (peer != null) {
|
||||||
|
state = 2;
|
||||||
|
|
||||||
|
peer.remoteAddress = parent().localAddress();
|
||||||
|
peer.state = 2;
|
||||||
|
peer.eventLoop().execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
peer.connectFuture.setSuccess();
|
||||||
|
peer.pipeline().fireChannelActive();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||||
|
this.localAddress =
|
||||||
|
LocalChannelRegistry.register(this, this.localAddress,
|
||||||
|
localAddress);
|
||||||
|
state = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDisconnect() throws Exception {
|
||||||
|
doClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doClose() throws Exception {
|
||||||
|
if (state > 2) {
|
||||||
|
// Closed already
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalChannelRegistry.unregister(localAddress);
|
||||||
|
localAddress = null;
|
||||||
|
state = 3;
|
||||||
|
if (peer.isActive()) {
|
||||||
|
peer.unsafe().close(peer.unsafe().voidFuture());
|
||||||
|
peer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDeregister() throws Exception {
|
||||||
|
if (isOpen()) {
|
||||||
|
unsafe().close(unsafe().voidFuture());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFlush(ChannelBufferHolder<Object> buf) throws Exception {
|
||||||
|
if (state < 2) {
|
||||||
|
throw new NotYetConnectedException();
|
||||||
|
}
|
||||||
|
if (state > 2) {
|
||||||
|
throw new ClosedChannelException();
|
||||||
|
}
|
||||||
|
|
||||||
|
final LocalChannel peer = this.peer;
|
||||||
|
assert peer != null;
|
||||||
|
|
||||||
|
Queue<Object> in = buf.messageBuffer();
|
||||||
|
Queue<Object> out = peer.pipeline().inbound().messageBuffer();
|
||||||
|
for (;;) {
|
||||||
|
Object msg = in.poll();
|
||||||
|
if (msg == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out.add(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
peer.eventLoop().execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
peer.pipeline().fireInboundBufferUpdated();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isFlushPending() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LocalUnsafe extends AbstractUnsafe {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connect(final SocketAddress remoteAddress,
|
||||||
|
SocketAddress localAddress, final ChannelFuture future) {
|
||||||
|
if (eventLoop().inEventLoop()) {
|
||||||
|
if (!ensureOpen(future)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == 2) {
|
||||||
|
Exception cause = new AlreadyConnectedException();
|
||||||
|
future.setFailure(cause);
|
||||||
|
pipeline().fireExceptionCaught(cause);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connectFuture != null) {
|
||||||
|
throw new ConnectionPendingException();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectFuture = future;
|
||||||
|
|
||||||
|
if (state != 1) {
|
||||||
|
// Not bound yet and no localAddress specified - get one.
|
||||||
|
if (localAddress == null) {
|
||||||
|
localAddress = new LocalAddress(LocalChannel.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localAddress != null) {
|
||||||
|
try {
|
||||||
|
doBind(localAddress);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
future.setFailure(t);
|
||||||
|
pipeline().fireExceptionCaught(t);
|
||||||
|
close(voidFuture());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Channel boundChannel = LocalChannelRegistry.get(remoteAddress);
|
||||||
|
if (!(boundChannel instanceof LocalServerChannel)) {
|
||||||
|
Exception cause =
|
||||||
|
new ChannelException("connection refused");
|
||||||
|
future.setFailure(cause);
|
||||||
|
pipeline().fireExceptionCaught(cause);
|
||||||
|
close(voidFuture());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalServerChannel serverChannel = (LocalServerChannel) boundChannel;
|
||||||
|
peer = serverChannel.serve(LocalChannel.this);
|
||||||
|
} else {
|
||||||
|
final SocketAddress localAddress0 = localAddress;
|
||||||
|
eventLoop().execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
connect(remoteAddress, localAddress0, future);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,46 +1,45 @@
|
||||||
/*
|
|
||||||
* Copyright 2011 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.local;
|
package io.netty.channel.local;
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelException;
|
||||||
|
|
||||||
|
import java.net.SocketAddress;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
final class LocalChannelRegistry {
|
final class LocalChannelRegistry {
|
||||||
|
|
||||||
private static final ConcurrentMap<LocalAddress, Channel> map =
|
private static final ConcurrentMap<LocalAddress, Channel> boundChannels =
|
||||||
new ConcurrentHashMap<LocalAddress, Channel>();
|
new ConcurrentHashMap<LocalAddress, Channel>();
|
||||||
|
|
||||||
static boolean isRegistered(LocalAddress address) {
|
static LocalAddress register(
|
||||||
return map.containsKey(address);
|
Channel channel, LocalAddress oldLocalAddress, SocketAddress localAddress) {
|
||||||
|
if (oldLocalAddress != null) {
|
||||||
|
throw new ChannelException("already bound");
|
||||||
|
}
|
||||||
|
if (!(localAddress instanceof LocalAddress)) {
|
||||||
|
throw new ChannelException(
|
||||||
|
"unsupported address type: " + localAddress.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalAddress addr = (LocalAddress) localAddress;
|
||||||
|
if (LocalAddress.ANY.equals(addr)) {
|
||||||
|
addr = new LocalAddress(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
Channel boundChannel = boundChannels.putIfAbsent(addr, channel);
|
||||||
|
if (boundChannel != null) {
|
||||||
|
throw new ChannelException("address already in use by: " + boundChannel);
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Channel getChannel(LocalAddress address) {
|
static Channel get(SocketAddress localAddress) {
|
||||||
return map.get(address);
|
return boundChannels.get(localAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean register(LocalAddress address, Channel channel) {
|
static void unregister(LocalAddress localAddress) {
|
||||||
return map.putIfAbsent(address, channel) == null;
|
boundChannels.remove(localAddress);
|
||||||
}
|
|
||||||
|
|
||||||
static boolean unregister(LocalAddress address) {
|
|
||||||
return map.remove(address) != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocalChannelRegistry() {
|
private LocalChannelRegistry() {
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2011 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.local;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelFactory;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link ChannelFactory} that creates a client-side {@link LocalChannel}.
|
|
||||||
*/
|
|
||||||
public interface LocalClientChannelFactory extends ChannelFactory {
|
|
||||||
@Override
|
|
||||||
LocalChannel newChannel(ChannelPipeline pipeline);
|
|
||||||
}
|
|
|
@ -1,150 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2011 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.local;
|
|
||||||
|
|
||||||
import static io.netty.channel.Channels.*;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.ConnectException;
|
|
||||||
|
|
||||||
import io.netty.channel.AbstractChannelSink;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelEvent;
|
|
||||||
import io.netty.channel.ChannelException;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelState;
|
|
||||||
import io.netty.channel.ChannelStateEvent;
|
|
||||||
import io.netty.channel.MessageEvent;
|
|
||||||
import io.netty.logging.InternalLogger;
|
|
||||||
import io.netty.logging.InternalLoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
final class LocalClientChannelSink extends AbstractChannelSink {
|
|
||||||
|
|
||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(LocalClientChannelSink.class);
|
|
||||||
|
|
||||||
LocalClientChannelSink() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void eventSunk(ChannelPipeline pipeline, ChannelEvent e) throws Exception {
|
|
||||||
if (e instanceof ChannelStateEvent) {
|
|
||||||
ChannelStateEvent event = (ChannelStateEvent) e;
|
|
||||||
|
|
||||||
DefaultLocalChannel channel =
|
|
||||||
(DefaultLocalChannel) event.channel();
|
|
||||||
ChannelFuture future = event.getFuture();
|
|
||||||
ChannelState state = event.getState();
|
|
||||||
Object value = event.getValue();
|
|
||||||
switch (state) {
|
|
||||||
case OPEN:
|
|
||||||
if (Boolean.FALSE.equals(value)) {
|
|
||||||
channel.closeNow(future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BOUND:
|
|
||||||
if (value != null) {
|
|
||||||
bind(channel, future, (LocalAddress) value);
|
|
||||||
} else {
|
|
||||||
channel.closeNow(future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CONNECTED:
|
|
||||||
if (value != null) {
|
|
||||||
connect(channel, future, (LocalAddress) value);
|
|
||||||
} else {
|
|
||||||
channel.closeNow(future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case INTEREST_OPS:
|
|
||||||
// Unsupported - discard silently.
|
|
||||||
future.setSuccess();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (e instanceof MessageEvent) {
|
|
||||||
MessageEvent event = (MessageEvent) e;
|
|
||||||
DefaultLocalChannel channel = (DefaultLocalChannel) event.channel();
|
|
||||||
boolean offered = channel.writeBuffer.offer(event);
|
|
||||||
assert offered;
|
|
||||||
channel.flushWriteBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bind(DefaultLocalChannel channel, ChannelFuture future, LocalAddress localAddress) {
|
|
||||||
try {
|
|
||||||
if (!LocalChannelRegistry.register(localAddress, channel)) {
|
|
||||||
throw new ChannelException("address already in use: " + localAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.setBound();
|
|
||||||
channel.localAddress = localAddress;
|
|
||||||
future.setSuccess();
|
|
||||||
fireChannelBound(channel, localAddress);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
LocalChannelRegistry.unregister(localAddress);
|
|
||||||
future.setFailure(t);
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void connect(DefaultLocalChannel channel, ChannelFuture future, LocalAddress remoteAddress) {
|
|
||||||
Channel remoteChannel = LocalChannelRegistry.getChannel(remoteAddress);
|
|
||||||
if (!(remoteChannel instanceof DefaultLocalServerChannel)) {
|
|
||||||
future.setFailure(new ConnectException("connection refused"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultLocalServerChannel serverChannel = (DefaultLocalServerChannel) remoteChannel;
|
|
||||||
ChannelPipeline pipeline;
|
|
||||||
try {
|
|
||||||
pipeline = serverChannel.getConfig().getPipelineFactory().getPipeline();
|
|
||||||
} catch (Exception e) {
|
|
||||||
future.setFailure(e);
|
|
||||||
fireExceptionCaught(channel, e);
|
|
||||||
if (logger.isWarnEnabled()) {
|
|
||||||
logger.warn(
|
|
||||||
"Failed to initialize an accepted socket.", e);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
future.setSuccess();
|
|
||||||
DefaultLocalChannel acceptedChannel = DefaultLocalChannel.create(serverChannel, serverChannel.getFactory(), pipeline, this, channel);
|
|
||||||
channel.pairedChannel = acceptedChannel;
|
|
||||||
|
|
||||||
bind(channel, succeededFuture(channel), new LocalAddress(LocalAddress.EPHEMERAL));
|
|
||||||
channel.remoteAddress = serverChannel.getLocalAddress();
|
|
||||||
channel.setConnected();
|
|
||||||
fireChannelConnected(channel, serverChannel.getLocalAddress());
|
|
||||||
|
|
||||||
acceptedChannel.localAddress = serverChannel.getLocalAddress();
|
|
||||||
try {
|
|
||||||
acceptedChannel.setBound();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new Error(e);
|
|
||||||
}
|
|
||||||
fireChannelBound(acceptedChannel, channel.getRemoteAddress());
|
|
||||||
acceptedChannel.remoteAddress = channel.getLocalAddress();
|
|
||||||
acceptedChannel.setConnected();
|
|
||||||
fireChannelConnected(acceptedChannel, channel.getLocalAddress());
|
|
||||||
|
|
||||||
// Flush something that was written in channelBound / channelConnected
|
|
||||||
channel.flushWriteBuffer();
|
|
||||||
acceptedChannel.flushWriteBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,14 +15,113 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.channel.local;
|
package io.netty.channel.local;
|
||||||
|
|
||||||
|
import io.netty.channel.AbstractServerChannel;
|
||||||
|
import io.netty.channel.ChannelConfig;
|
||||||
|
import io.netty.channel.DefaultChannelConfig;
|
||||||
|
import io.netty.channel.EventLoop;
|
||||||
import io.netty.channel.ServerChannel;
|
import io.netty.channel.ServerChannel;
|
||||||
|
import io.netty.channel.SingleThreadEventLoop;
|
||||||
|
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link ServerChannel} for the local transport.
|
* A {@link ServerChannel} for the local transport.
|
||||||
*/
|
*/
|
||||||
public interface LocalServerChannel extends ServerChannel {
|
public class LocalServerChannel extends AbstractServerChannel {
|
||||||
|
|
||||||
|
private final ChannelConfig config = new DefaultChannelConfig();
|
||||||
|
|
||||||
|
private volatile int state; // 0 - open, 1 - active, 2 - closed
|
||||||
|
private volatile LocalAddress localAddress;
|
||||||
|
|
||||||
|
public LocalServerChannel() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalServerChannel(Integer id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
LocalAddress getLocalAddress();
|
public ChannelConfig config() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
LocalAddress getRemoteAddress();
|
public LocalAddress localAddress() {
|
||||||
|
return (LocalAddress) super.localAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocalAddress remoteAddress() {
|
||||||
|
return (LocalAddress) super.remoteAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOpen() {
|
||||||
|
return state < 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive() {
|
||||||
|
return state == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isCompatible(EventLoop loop) {
|
||||||
|
return loop instanceof SingleThreadEventLoop;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SocketAddress localAddress0() {
|
||||||
|
return localAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doRegister() throws Exception {
|
||||||
|
// NOOP
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||||
|
this.localAddress = LocalChannelRegistry.register(this, this.localAddress, localAddress);
|
||||||
|
state = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doClose() throws Exception {
|
||||||
|
if (state > 1) {
|
||||||
|
// Closed already.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalChannelRegistry.unregister(localAddress);
|
||||||
|
localAddress = null;
|
||||||
|
state = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDeregister() throws Exception {
|
||||||
|
// NOOP
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalChannel serve(final LocalChannel peer) {
|
||||||
|
LocalChannel child = new LocalChannel(this, peer);
|
||||||
|
serve0(child);
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void serve0(final LocalChannel child) {
|
||||||
|
if (eventLoop().inEventLoop()) {
|
||||||
|
pipeline().inbound().messageBuffer().add(child);
|
||||||
|
pipeline().fireInboundBufferUpdated();
|
||||||
|
} else {
|
||||||
|
eventLoop().execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
serve0(child);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2011 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.local;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ServerChannelFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link ServerChannelFactory} that creates a {@link LocalServerChannel}.
|
|
||||||
*/
|
|
||||||
public interface LocalServerChannelFactory extends ServerChannelFactory {
|
|
||||||
@Override
|
|
||||||
LocalServerChannel newChannel(ChannelPipeline pipeline);
|
|
||||||
}
|
|
|
@ -1,144 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2011 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.local;
|
|
||||||
|
|
||||||
import static io.netty.channel.Channels.*;
|
|
||||||
|
|
||||||
import io.netty.channel.AbstractChannelSink;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelEvent;
|
|
||||||
import io.netty.channel.ChannelException;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelState;
|
|
||||||
import io.netty.channel.ChannelStateEvent;
|
|
||||||
import io.netty.channel.MessageEvent;
|
|
||||||
|
|
||||||
final class LocalServerChannelSink extends AbstractChannelSink {
|
|
||||||
|
|
||||||
LocalServerChannelSink() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void eventSunk(ChannelPipeline pipeline, ChannelEvent e) throws Exception {
|
|
||||||
Channel channel = e.getChannel();
|
|
||||||
if (channel instanceof DefaultLocalServerChannel) {
|
|
||||||
handleServerChannel(e);
|
|
||||||
} else if (channel instanceof DefaultLocalChannel) {
|
|
||||||
handleAcceptedChannel(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleServerChannel(ChannelEvent e) {
|
|
||||||
if (!(e instanceof ChannelStateEvent)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ChannelStateEvent event = (ChannelStateEvent) e;
|
|
||||||
DefaultLocalServerChannel channel =
|
|
||||||
(DefaultLocalServerChannel) event.channel();
|
|
||||||
ChannelFuture future = event.getFuture();
|
|
||||||
ChannelState state = event.getState();
|
|
||||||
Object value = event.getValue();
|
|
||||||
switch (state) {
|
|
||||||
case OPEN:
|
|
||||||
if (Boolean.FALSE.equals(value)) {
|
|
||||||
close(channel, future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BOUND:
|
|
||||||
if (value != null) {
|
|
||||||
bind(channel, future, (LocalAddress) value);
|
|
||||||
} else {
|
|
||||||
close(channel, future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleAcceptedChannel(ChannelEvent e) {
|
|
||||||
if (e instanceof ChannelStateEvent) {
|
|
||||||
ChannelStateEvent event = (ChannelStateEvent) e;
|
|
||||||
DefaultLocalChannel channel = (DefaultLocalChannel) event.channel();
|
|
||||||
ChannelFuture future = event.getFuture();
|
|
||||||
ChannelState state = event.getState();
|
|
||||||
Object value = event.getValue();
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case OPEN:
|
|
||||||
if (Boolean.FALSE.equals(value)) {
|
|
||||||
channel.closeNow(future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BOUND:
|
|
||||||
case CONNECTED:
|
|
||||||
if (value == null) {
|
|
||||||
channel.closeNow(future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case INTEREST_OPS:
|
|
||||||
// Unsupported - discard silently.
|
|
||||||
future.setSuccess();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (e instanceof MessageEvent) {
|
|
||||||
MessageEvent event = (MessageEvent) e;
|
|
||||||
DefaultLocalChannel channel = (DefaultLocalChannel) event.channel();
|
|
||||||
boolean offered = channel.writeBuffer.offer(event);
|
|
||||||
assert offered;
|
|
||||||
channel.flushWriteBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bind(DefaultLocalServerChannel channel, ChannelFuture future, LocalAddress localAddress) {
|
|
||||||
try {
|
|
||||||
if (!LocalChannelRegistry.register(localAddress, channel)) {
|
|
||||||
throw new ChannelException("address already in use: " + localAddress);
|
|
||||||
}
|
|
||||||
if (!channel.bound.compareAndSet(false, true)) {
|
|
||||||
throw new ChannelException("already bound");
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.localAddress = localAddress;
|
|
||||||
future.setSuccess();
|
|
||||||
fireChannelBound(channel, localAddress);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
LocalChannelRegistry.unregister(localAddress);
|
|
||||||
future.setFailure(t);
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void close(DefaultLocalServerChannel channel, ChannelFuture future) {
|
|
||||||
try {
|
|
||||||
if (channel.setClosed()) {
|
|
||||||
future.setSuccess();
|
|
||||||
LocalAddress localAddress = channel.localAddress;
|
|
||||||
if (channel.bound.compareAndSet(true, false)) {
|
|
||||||
channel.localAddress = null;
|
|
||||||
LocalChannelRegistry.unregister(localAddress);
|
|
||||||
fireChannelUnbound(channel);
|
|
||||||
}
|
|
||||||
fireChannelClosed(channel);
|
|
||||||
} else {
|
|
||||||
future.setSuccess();
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
future.setFailure(t);
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,6 +17,7 @@ package io.netty.channel.socket.nio;
|
||||||
|
|
||||||
import io.netty.channel.AbstractChannel;
|
import io.netty.channel.AbstractChannel;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelBufferHolder;
|
||||||
import io.netty.channel.ChannelException;
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.EventLoop;
|
import io.netty.channel.EventLoop;
|
||||||
|
@ -49,8 +50,10 @@ public abstract class AbstractNioChannel extends AbstractChannel {
|
||||||
private ScheduledFuture<?> connectTimeoutFuture;
|
private ScheduledFuture<?> connectTimeoutFuture;
|
||||||
private ConnectException connectTimeoutException;
|
private ConnectException connectTimeoutException;
|
||||||
|
|
||||||
protected AbstractNioChannel(Channel parent, Integer id, SelectableChannel ch, int defaultInterestOps) {
|
protected AbstractNioChannel(
|
||||||
super(parent, id);
|
Channel parent, Integer id, ChannelBufferHolder<?> outboundBuffer,
|
||||||
|
SelectableChannel ch, int defaultInterestOps) {
|
||||||
|
super(parent, id, outboundBuffer);
|
||||||
this.ch = ch;
|
this.ch = ch;
|
||||||
this.defaultInterestOps = defaultInterestOps;
|
this.defaultInterestOps = defaultInterestOps;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -2,7 +2,6 @@ package io.netty.channel.socket.nio;
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelBufferHolder;
|
import io.netty.channel.ChannelBufferHolder;
|
||||||
import io.netty.channel.ChannelBufferHolders;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -12,13 +11,9 @@ import java.util.Queue;
|
||||||
abstract class AbstractNioMessageChannel extends AbstractNioChannel {
|
abstract class AbstractNioMessageChannel extends AbstractNioChannel {
|
||||||
|
|
||||||
protected AbstractNioMessageChannel(
|
protected AbstractNioMessageChannel(
|
||||||
Channel parent, Integer id, SelectableChannel ch, int defaultInterestOps) {
|
Channel parent, Integer id, ChannelBufferHolder<?> outboundBuffer,
|
||||||
super(parent, id, ch, defaultInterestOps);
|
SelectableChannel ch, int defaultInterestOps) {
|
||||||
}
|
super(parent, id, outboundBuffer, ch, defaultInterestOps);
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ChannelBufferHolder<?> newOutboundBuffer() {
|
|
||||||
return ChannelBufferHolders.messageBuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -14,12 +14,7 @@ abstract class AbstractNioStreamChannel extends AbstractNioChannel {
|
||||||
|
|
||||||
protected AbstractNioStreamChannel(
|
protected AbstractNioStreamChannel(
|
||||||
Channel parent, Integer id, SelectableChannel ch) {
|
Channel parent, Integer id, SelectableChannel ch) {
|
||||||
super(parent, id, ch, SelectionKey.OP_READ);
|
super(parent, id, ChannelBufferHolders.byteBuffer(), ch, SelectionKey.OP_READ);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ChannelBufferHolder<?> newOutboundBuffer() {
|
|
||||||
return ChannelBufferHolders.byteBuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
package io.netty.channel.socket.nio;
|
package io.netty.channel.socket.nio;
|
||||||
|
|
||||||
import io.netty.buffer.ChannelBuffers;
|
import io.netty.buffer.ChannelBuffers;
|
||||||
import io.netty.channel.ChannelBufferHolder;
|
|
||||||
import io.netty.channel.ChannelBufferHolders;
|
import io.netty.channel.ChannelBufferHolders;
|
||||||
import io.netty.channel.ChannelException;
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
|
@ -67,7 +66,7 @@ public final class NioDatagramChannel extends AbstractNioMessageChannel implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
public NioDatagramChannel(Integer id, DatagramChannel socket) {
|
public NioDatagramChannel(Integer id, DatagramChannel socket) {
|
||||||
super(null, id, socket, SelectionKey.OP_READ);
|
super(null, id, ChannelBufferHolders.messageBuffer(), socket, SelectionKey.OP_READ);
|
||||||
config = new NioDatagramChannelConfig(socket);
|
config = new NioDatagramChannelConfig(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,11 +86,6 @@ public final class NioDatagramChannel extends AbstractNioMessageChannel implemen
|
||||||
return (DatagramChannel) super.javaChannel();
|
return (DatagramChannel) super.javaChannel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ChannelBufferHolder<?> newOutboundBuffer() {
|
|
||||||
return ChannelBufferHolders.messageBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SocketAddress localAddress0() {
|
protected SocketAddress localAddress0() {
|
||||||
return javaChannel().socket().getLocalSocketAddress();
|
return javaChannel().socket().getLocalSocketAddress();
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.channel.socket.nio;
|
package io.netty.channel.socket.nio;
|
||||||
|
|
||||||
import io.netty.channel.ChannelBufferHolder;
|
|
||||||
import io.netty.channel.ChannelBufferHolders;
|
import io.netty.channel.ChannelBufferHolders;
|
||||||
import io.netty.channel.ChannelException;
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.channel.socket.DefaultServerSocketChannelConfig;
|
import io.netty.channel.socket.DefaultServerSocketChannelConfig;
|
||||||
|
@ -43,7 +42,7 @@ public class NioServerSocketChannel extends AbstractNioMessageChannel
|
||||||
private final ServerSocketChannelConfig config;
|
private final ServerSocketChannelConfig config;
|
||||||
|
|
||||||
public NioServerSocketChannel() {
|
public NioServerSocketChannel() {
|
||||||
super(null, null, newSocket(), SelectionKey.OP_ACCEPT);
|
super(null, null, ChannelBufferHolders.discardBuffer(), newSocket(), SelectionKey.OP_ACCEPT);
|
||||||
config = new DefaultServerSocketChannelConfig(javaChannel().socket());
|
config = new DefaultServerSocketChannelConfig(javaChannel().socket());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,11 +105,6 @@ public class NioServerSocketChannel extends AbstractNioMessageChannel
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ChannelBufferHolder<?> newOutboundBuffer() {
|
|
||||||
return ChannelBufferHolders.discardBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SocketAddress remoteAddress0() {
|
protected SocketAddress remoteAddress0() {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -17,8 +17,6 @@ package io.netty.channel.socket.nio;
|
||||||
|
|
||||||
import io.netty.buffer.ChannelBuffer;
|
import io.netty.buffer.ChannelBuffer;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelBufferHolder;
|
|
||||||
import io.netty.channel.ChannelBufferHolders;
|
|
||||||
import io.netty.channel.ChannelException;
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.channel.socket.DefaultSocketChannelConfig;
|
import io.netty.channel.socket.DefaultSocketChannelConfig;
|
||||||
import io.netty.channel.socket.SocketChannelConfig;
|
import io.netty.channel.socket.SocketChannelConfig;
|
||||||
|
@ -89,11 +87,6 @@ public class NioSocketChannel extends AbstractNioStreamChannel implements io.net
|
||||||
return ch.isOpen() && ch.isConnected();
|
return ch.isOpen() && ch.isConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ChannelBufferHolder<?> newOutboundBuffer() {
|
|
||||||
return ChannelBufferHolders.byteBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SocketAddress localAddress0() {
|
protected SocketAddress localAddress0() {
|
||||||
return javaChannel().socket().getLocalSocketAddress();
|
return javaChannel().socket().getLocalSocketAddress();
|
||||||
|
|
|
@ -2,6 +2,7 @@ package io.netty.channel.socket.oio;
|
||||||
|
|
||||||
import io.netty.channel.AbstractChannel;
|
import io.netty.channel.AbstractChannel;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelBufferHolder;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.EventLoop;
|
import io.netty.channel.EventLoop;
|
||||||
|
|
||||||
|
@ -10,8 +11,8 @@ import java.net.SocketAddress;
|
||||||
|
|
||||||
abstract class AbstractOioChannel extends AbstractChannel {
|
abstract class AbstractOioChannel extends AbstractChannel {
|
||||||
|
|
||||||
protected AbstractOioChannel(Channel parent, Integer id) {
|
protected AbstractOioChannel(Channel parent, Integer id, ChannelBufferHolder<?> outboundBuffer) {
|
||||||
super(parent, id);
|
super(parent, id, outboundBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -2,7 +2,6 @@ package io.netty.channel.socket.oio;
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelBufferHolder;
|
import io.netty.channel.ChannelBufferHolder;
|
||||||
import io.netty.channel.ChannelBufferHolders;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -10,13 +9,9 @@ import java.util.Queue;
|
||||||
|
|
||||||
abstract class AbstractOioMessageChannel extends AbstractOioChannel {
|
abstract class AbstractOioMessageChannel extends AbstractOioChannel {
|
||||||
|
|
||||||
protected AbstractOioMessageChannel(Channel parent, Integer id) {
|
protected AbstractOioMessageChannel(
|
||||||
super(parent, id);
|
Channel parent, Integer id, ChannelBufferHolder<?> outboundBuffer) {
|
||||||
}
|
super(parent, id, outboundBuffer);
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ChannelBufferHolder<?> newOutboundBuffer() {
|
|
||||||
return ChannelBufferHolders.messageBuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -11,12 +11,7 @@ import java.io.IOException;
|
||||||
abstract class AbstractOioStreamChannel extends AbstractOioChannel {
|
abstract class AbstractOioStreamChannel extends AbstractOioChannel {
|
||||||
|
|
||||||
protected AbstractOioStreamChannel(Channel parent, Integer id) {
|
protected AbstractOioStreamChannel(Channel parent, Integer id) {
|
||||||
super(parent, id);
|
super(parent, id, ChannelBufferHolders.byteBuffer());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ChannelBufferHolder<?> newOutboundBuffer() {
|
|
||||||
return ChannelBufferHolders.byteBuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,7 +17,6 @@ package io.netty.channel.socket.oio;
|
||||||
|
|
||||||
import io.netty.buffer.ChannelBuffer;
|
import io.netty.buffer.ChannelBuffer;
|
||||||
import io.netty.buffer.ChannelBuffers;
|
import io.netty.buffer.ChannelBuffers;
|
||||||
import io.netty.channel.ChannelBufferHolder;
|
|
||||||
import io.netty.channel.ChannelBufferHolders;
|
import io.netty.channel.ChannelBufferHolders;
|
||||||
import io.netty.channel.ChannelException;
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
|
@ -66,7 +65,7 @@ public class OioDatagramChannel extends AbstractOioMessageChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
public OioDatagramChannel(Integer id, MulticastSocket socket) {
|
public OioDatagramChannel(Integer id, MulticastSocket socket) {
|
||||||
super(null, id);
|
super(null, id, ChannelBufferHolders.messageBuffer());
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
|
@ -101,11 +100,6 @@ public class OioDatagramChannel extends AbstractOioMessageChannel
|
||||||
return isOpen() && socket.isBound();
|
return isOpen() && socket.isBound();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ChannelBufferHolder<?> newOutboundBuffer() {
|
|
||||||
return ChannelBufferHolders.messageBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SocketAddress localAddress0() {
|
protected SocketAddress localAddress0() {
|
||||||
return socket.getLocalSocketAddress();
|
return socket.getLocalSocketAddress();
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package io.netty.channel.socket.oio;
|
package io.netty.channel.socket.oio;
|
||||||
|
|
||||||
import io.netty.channel.ChannelBufferHolder;
|
|
||||||
import io.netty.channel.ChannelBufferHolders;
|
import io.netty.channel.ChannelBufferHolders;
|
||||||
import io.netty.channel.ChannelException;
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.channel.socket.DefaultServerSocketChannelConfig;
|
import io.netty.channel.socket.DefaultServerSocketChannelConfig;
|
||||||
|
@ -61,7 +60,7 @@ public class OioServerSocketChannel extends AbstractOioMessageChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
public OioServerSocketChannel(Integer id, ServerSocket socket) {
|
public OioServerSocketChannel(Integer id, ServerSocket socket) {
|
||||||
super(null, id);
|
super(null, id, ChannelBufferHolders.discardBuffer());
|
||||||
if (socket == null) {
|
if (socket == null) {
|
||||||
throw new NullPointerException("socket");
|
throw new NullPointerException("socket");
|
||||||
}
|
}
|
||||||
|
@ -160,11 +159,6 @@ public class OioServerSocketChannel extends AbstractOioMessageChannel
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ChannelBufferHolder<?> newOutboundBuffer() {
|
|
||||||
return ChannelBufferHolders.discardBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SocketAddress remoteAddress0() {
|
protected SocketAddress remoteAddress0() {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -17,8 +17,6 @@ package io.netty.channel.socket.oio;
|
||||||
|
|
||||||
import io.netty.buffer.ChannelBuffer;
|
import io.netty.buffer.ChannelBuffer;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelBufferHolder;
|
|
||||||
import io.netty.channel.ChannelBufferHolders;
|
|
||||||
import io.netty.channel.ChannelException;
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.channel.socket.DefaultSocketChannelConfig;
|
import io.netty.channel.socket.DefaultSocketChannelConfig;
|
||||||
import io.netty.channel.socket.SocketChannel;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
|
@ -94,11 +92,6 @@ public class OioSocketChannel extends AbstractOioStreamChannel
|
||||||
return !socket.isClosed() && socket.isConnected();
|
return !socket.isClosed() && socket.isConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ChannelBufferHolder<?> newOutboundBuffer() {
|
|
||||||
return ChannelBufferHolders.byteBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SocketAddress localAddress0() {
|
protected SocketAddress localAddress0() {
|
||||||
return socket.getLocalSocketAddress();
|
return socket.getLocalSocketAddress();
|
||||||
|
|
|
@ -38,29 +38,6 @@ public class CompleteChannelFutureTest {
|
||||||
new CompleteChannelFutureImpl(null);
|
new CompleteChannelFutureImpl(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void shouldNotifyImmediatelyOnAdd() throws Exception {
|
|
||||||
ChannelFutureListener l = createStrictMock(ChannelFutureListener.class);
|
|
||||||
l.operationComplete(future);
|
|
||||||
replay(l);
|
|
||||||
|
|
||||||
future.addListener(l);
|
|
||||||
verify(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void shouldNotRethrowListenerException() {
|
|
||||||
ChannelFutureListener l = new ChannelFutureListener() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(ChannelFuture future)
|
|
||||||
throws Exception {
|
|
||||||
throw new ExpectedError();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
future.addListener(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldNotDoAnythingOnRemove() throws Exception {
|
public void shouldNotDoAnythingOnRemove() throws Exception {
|
||||||
ChannelFutureListener l = createStrictMock(ChannelFutureListener.class);
|
ChannelFutureListener l = createStrictMock(ChannelFutureListener.class);
|
||||||
|
@ -110,11 +87,4 @@ public class CompleteChannelFutureTest {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ExpectedError extends Error {
|
|
||||||
private static final long serialVersionUID = 7059276744882005047L;
|
|
||||||
|
|
||||||
ExpectedError() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,32 +16,49 @@
|
||||||
package io.netty.channel;
|
package io.netty.channel;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
import io.netty.channel.local.LocalChannel;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class DefaultChannelPipelineTest {
|
public class DefaultChannelPipelineTest {
|
||||||
@Test
|
@Test
|
||||||
public void testReplaceChannelHandler() {
|
public void testReplaceChannelHandler() {
|
||||||
DefaultChannelPipeline pipeline = new DefaultChannelPipeline();
|
DefaultChannelPipeline pipeline = new DefaultChannelPipeline(new LocalChannel());
|
||||||
|
|
||||||
SimpleChannelHandler handler1 = new SimpleChannelHandler();
|
ChannelHandler handler1 = newHandler();
|
||||||
pipeline.addLast("handler1", handler1);
|
pipeline.addLast("handler1", handler1);
|
||||||
pipeline.addLast("handler2", handler1);
|
pipeline.addLast("handler2", handler1);
|
||||||
pipeline.addLast("handler3", handler1);
|
pipeline.addLast("handler3", handler1);
|
||||||
assertTrue(pipeline.get("handler1") == handler1);
|
assertTrue(pipeline.get("handler1") == handler1);
|
||||||
assertTrue(pipeline.get("handler2") == handler1);
|
assertTrue(pipeline.get("handler2") == handler1);
|
||||||
assertTrue(pipeline.get("handler3") == handler1);
|
assertTrue(pipeline.get("handler3") == handler1);
|
||||||
|
|
||||||
SimpleChannelHandler newHandler1 = new SimpleChannelHandler();
|
ChannelHandler newHandler1 = newHandler();
|
||||||
pipeline.replace("handler1", "handler1", newHandler1);
|
pipeline.replace("handler1", "handler1", newHandler1);
|
||||||
assertTrue(pipeline.get("handler1") == newHandler1);
|
assertTrue(pipeline.get("handler1") == newHandler1);
|
||||||
|
|
||||||
SimpleChannelHandler newHandler3 = new SimpleChannelHandler();
|
ChannelHandler newHandler3 = newHandler();
|
||||||
pipeline.replace("handler3", "handler3", newHandler3);
|
pipeline.replace("handler3", "handler3", newHandler3);
|
||||||
assertTrue(pipeline.get("handler3") == newHandler3);
|
assertTrue(pipeline.get("handler3") == newHandler3);
|
||||||
|
|
||||||
SimpleChannelHandler newHandler2 = new SimpleChannelHandler();
|
ChannelHandler newHandler2 = newHandler();
|
||||||
pipeline.replace("handler2", "handler2", newHandler2);
|
pipeline.replace("handler2", "handler2", newHandler2);
|
||||||
assertTrue(pipeline.get("handler2") == newHandler2);
|
assertTrue(pipeline.get("handler2") == newHandler2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ChannelHandler newHandler() {
|
||||||
|
return new ChannelHandlerAdapter<Byte, Byte>() {
|
||||||
|
@Override
|
||||||
|
public ChannelBufferHolder<Byte> newInboundBuffer(
|
||||||
|
ChannelInboundHandlerContext<Byte> ctx) throws Exception {
|
||||||
|
return ChannelBufferHolders.byteBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelBufferHolder<Byte> newOutboundBuffer(
|
||||||
|
ChannelOutboundHandlerContext<Byte> ctx) throws Exception {
|
||||||
|
return ChannelBufferHolders.byteBuffer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user