Improve flexibility of EmbeddedChannel ID
Motivation: EmbeddedChannelId#hashCode() and equals() do not behave correctly if an instance is serialized and then deserialized. Additionally, EmbeddedChannel does not allow use of any other type of ChannelId, and EmbeddedChannelId is (mostly) a singleton instance. This creates a problem for unit tests that construct multiple EmbeddedChannels and expect each channel to have a unique ID. Modifications: EmbeddedChannelId is modified so equals() will return true and hashCode() will return the same value for any instance of the class. EmbeddedChannel is modified to allow a ChannelId to be specified when the channel is constructed. Tests added for both changes. Result: EmbeddedChannelId now behaves correctly when deserialized, and EmbeddedChannels can now have unique IDs.
This commit is contained in:
parent
30dc1c1fa4
commit
b58036aeea
@ -21,6 +21,7 @@ import io.netty.channel.ChannelConfig;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelId;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelMetadata;
|
||||
@ -64,19 +65,39 @@ public class EmbeddedChannel extends AbstractChannel {
|
||||
private State state;
|
||||
|
||||
/**
|
||||
* Create a new instance with an empty pipeline.
|
||||
* Create a new instance with an {@link EmbeddedChannelId} and an empty pipeline.
|
||||
*/
|
||||
public EmbeddedChannel() {
|
||||
this(EMPTY_HANDLERS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with the specified ID and an empty pipeline.
|
||||
*
|
||||
* @param channelId the {@link ChannelId} that will be used to identify this channel
|
||||
*/
|
||||
public EmbeddedChannel(ChannelId channelId) {
|
||||
this(channelId, EMPTY_HANDLERS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with the pipeline initialized with the specified handlers.
|
||||
*
|
||||
* @param handlers the @link ChannelHandler}s which will be add in the {@link ChannelPipeline}
|
||||
* @param handlers the {@link ChannelHandler}s which will be add in the {@link ChannelPipeline}
|
||||
*/
|
||||
public EmbeddedChannel(final ChannelHandler... handlers) {
|
||||
super(null, EmbeddedChannelId.INSTANCE);
|
||||
this(EmbeddedChannelId.INSTANCE, handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with the channel ID set to the given ID and the pipeline
|
||||
* initialized with the specified handlers.
|
||||
*
|
||||
* @param channelId the {@link ChannelId} that will be used to identify this channel
|
||||
* @param handlers the {@link ChannelHandler}s which will be add in the {@link ChannelPipeline}
|
||||
*/
|
||||
public EmbeddedChannel(ChannelId channelId, final ChannelHandler... handlers) {
|
||||
super(null, channelId);
|
||||
|
||||
if (handlers == null) {
|
||||
throw new NullPointerException("handlers");
|
||||
|
@ -41,7 +41,7 @@ final class EmbeddedChannelId implements ChannelId {
|
||||
|
||||
@Override
|
||||
public int compareTo(ChannelId o) {
|
||||
if (o == INSTANCE) {
|
||||
if (o instanceof EmbeddedChannelId) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -50,12 +50,12 @@ final class EmbeddedChannelId implements ChannelId {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return super.equals(obj);
|
||||
return obj instanceof EmbeddedChannelId;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2015 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.embedded;
|
||||
|
||||
import io.netty.channel.ChannelId;
|
||||
|
||||
public class CustomChannelId implements ChannelId {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int id;
|
||||
|
||||
public CustomChannelId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ChannelId o) {
|
||||
if (o instanceof CustomChannelId) {
|
||||
return id - ((CustomChannelId) o).id;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof CustomChannelId) {
|
||||
return id == ((CustomChannelId) obj).id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CustomChannelId " + id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asShortText() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asLongText() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2015 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.embedded;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import io.netty.buffer.ByteBufInputStream;
|
||||
import io.netty.buffer.ByteBufOutputStream;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelId;
|
||||
|
||||
public class EmbeddedChannelIdTest {
|
||||
|
||||
@Test
|
||||
public void testSerialization() throws IOException, ClassNotFoundException {
|
||||
// test that a deserialized instance works the same as a normal instance (issue #2869)
|
||||
ChannelId normalInstance = EmbeddedChannelId.INSTANCE;
|
||||
|
||||
ByteBufOutputStream buffer = new ByteBufOutputStream(Unpooled.buffer());
|
||||
ObjectOutputStream outStream = new ObjectOutputStream(buffer);
|
||||
outStream.writeObject(normalInstance);
|
||||
outStream.close();
|
||||
|
||||
ObjectInputStream inStream = new ObjectInputStream(new ByteBufInputStream(buffer.buffer()));
|
||||
ChannelId deserializedInstance = (ChannelId) inStream.readObject();
|
||||
inStream.close();
|
||||
|
||||
Assert.assertEquals(normalInstance, deserializedInstance);
|
||||
Assert.assertEquals(normalInstance.hashCode(), deserializedInstance.hashCode());
|
||||
Assert.assertEquals(0, normalInstance.compareTo(deserializedInstance));
|
||||
}
|
||||
|
||||
}
|
@ -20,6 +20,7 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerAdapter;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelId;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
@ -136,6 +137,13 @@ public class EmbeddedChannelTest {
|
||||
Assert.assertNull(channel.readOutbound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructWithChannelId() {
|
||||
ChannelId channelId = new CustomChannelId(1);
|
||||
EmbeddedChannel channel = new EmbeddedChannel(channelId);
|
||||
Assert.assertSame(channelId, channel.id());
|
||||
}
|
||||
|
||||
// See https://github.com/netty/netty/issues/4316.
|
||||
@Test(timeout = 2000)
|
||||
public void testFireChannelInactiveAndUnregisteredOnClose() throws InterruptedException {
|
||||
|
Loading…
Reference in New Issue
Block a user