Jian Li
Committed by Gerrit Code Review

Add LispMessageEncoder and LispMessageDecoder with unit tests

Change-Id: If73a41687a9c2400de23bbde6179a63ac7f75d15
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//protocols/lisp/api:onos-protocols-lisp-api'
'//protocols/lisp/api:onos-protocols-lisp-api',
'//protocols/lisp/msg:onos-protocols-lisp-msg',
'//lib:netty-buffer',
'//lib:netty-codec',
'//lib:netty-transport'
]
TEST_DEPS = [
......
......@@ -41,6 +41,11 @@
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-lisp-msg</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-api</artifactId>
<classifier>tests</classifier>
<scope>test</scope>
......
......@@ -15,10 +15,26 @@
*/
package org.onosproject.lisp.ctl;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.onosproject.lisp.msg.protocols.LispMessage;
import org.onosproject.lisp.msg.protocols.LispMessageReader;
import org.onosproject.lisp.msg.protocols.LispMessageReaderFactory;
import java.util.List;
/**
* Remove me.
* Decode a LISP message from a ByteBuffer, for use in a netty pipeline.
*/
@Deprecated
public abstract class PlaceHolder {
public class LispMessageDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf,
List<Object> list) throws Exception {
LispMessageReader reader = LispMessageReaderFactory.getReader(byteBuf);
LispMessage message = (LispMessage) reader.readFrom(byteBuf);
list.add(message);
}
}
......
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.lisp.ctl;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import org.onosproject.lisp.msg.protocols.LispMessage;
import java.util.List;
/**
* Encode a LISP message for output into a ByteBuffer,
* for use in a netty pipeline.
*/
public class LispMessageEncoder extends MessageToByteEncoder {
@Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
if (!(msg instanceof List)) {
((LispMessage) msg).writeTo(out);
return;
}
List<LispMessage> msgList = (List<LispMessage>) msg;
for (LispMessage message : msgList) {
if (message != null) {
message.writeTo(out);
}
}
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.lisp.ctl;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.ChannelProgressivePromise;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.EventLoop;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import java.net.SocketAddress;
/**
* Adapter for testing against a netty channel.
*/
public class ChannelAdapter implements Channel {
@Override
public EventLoop eventLoop() {
return null;
}
@Override
public Channel parent() {
return null;
}
@Override
public ChannelConfig config() {
return null;
}
@Override
public boolean isOpen() {
return false;
}
@Override
public boolean isRegistered() {
return false;
}
@Override
public boolean isActive() {
return false;
}
@Override
public ChannelMetadata metadata() {
return null;
}
@Override
public SocketAddress localAddress() {
return null;
}
@Override
public SocketAddress remoteAddress() {
return null;
}
@Override
public ChannelFuture closeFuture() {
return null;
}
@Override
public boolean isWritable() {
return false;
}
@Override
public Unsafe unsafe() {
return null;
}
@Override
public ChannelPipeline pipeline() {
return null;
}
@Override
public ByteBufAllocator alloc() {
return null;
}
@Override
public ChannelPromise newPromise() {
return null;
}
@Override
public ChannelProgressivePromise newProgressivePromise() {
return null;
}
@Override
public ChannelFuture newSucceededFuture() {
return null;
}
@Override
public ChannelFuture newFailedFuture(Throwable cause) {
return null;
}
@Override
public ChannelPromise voidPromise() {
return null;
}
@Override
public ChannelFuture bind(SocketAddress localAddress) {
return null;
}
@Override
public ChannelFuture connect(SocketAddress remoteAddress) {
return null;
}
@Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
return null;
}
@Override
public ChannelFuture disconnect() {
return null;
}
@Override
public ChannelFuture close() {
return null;
}
@Override
public ChannelFuture deregister() {
return null;
}
@Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return null;
}
@Override
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
return null;
}
@Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
return null;
}
@Override
public ChannelFuture disconnect(ChannelPromise promise) {
return null;
}
@Override
public ChannelFuture close(ChannelPromise promise) {
return null;
}
@Override
public ChannelFuture deregister(ChannelPromise promise) {
return null;
}
@Override
public Channel read() {
return null;
}
@Override
public ChannelFuture write(Object msg) {
return null;
}
@Override
public ChannelFuture write(Object msg, ChannelPromise promise) {
return null;
}
@Override
public Channel flush() {
return null;
}
@Override
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
return null;
}
@Override
public ChannelFuture writeAndFlush(Object msg) {
return null;
}
@Override
public <T> Attribute<T> attr(AttributeKey<T> attributeKey) {
return null;
}
@Override
public int compareTo(Channel o) {
return 0;
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.lisp.ctl;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelProgressivePromise;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.EventExecutor;
import java.net.SocketAddress;
/**
* Adapter for testing against a netty channel handler context.
*/
public class ChannelHandlerContextAdapter implements ChannelHandlerContext {
@Override
public Channel channel() {
return null;
}
@Override
public EventExecutor executor() {
return null;
}
@Override
public String name() {
return null;
}
@Override
public ChannelHandler handler() {
return null;
}
@Override
public boolean isRemoved() {
return false;
}
@Override
public ChannelHandlerContext fireChannelRegistered() {
return null;
}
@Override
public ChannelHandlerContext fireChannelUnregistered() {
return null;
}
@Override
public ChannelHandlerContext fireChannelActive() {
return null;
}
@Override
public ChannelHandlerContext fireChannelInactive() {
return null;
}
@Override
public ChannelHandlerContext fireExceptionCaught(Throwable cause) {
return null;
}
@Override
public ChannelHandlerContext fireUserEventTriggered(Object evt) {
return null;
}
@Override
public ChannelHandlerContext fireChannelRead(Object msg) {
return null;
}
@Override
public ChannelHandlerContext fireChannelReadComplete() {
return null;
}
@Override
public ChannelHandlerContext fireChannelWritabilityChanged() {
return null;
}
@Override
public ChannelFuture bind(SocketAddress localAddress) {
return null;
}
@Override
public ChannelFuture connect(SocketAddress remoteAddress) {
return null;
}
@Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
return null;
}
@Override
public ChannelFuture disconnect() {
return null;
}
@Override
public ChannelFuture close() {
return null;
}
@Override
public ChannelFuture deregister() {
return null;
}
@Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return null;
}
@Override
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
return null;
}
@Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
return null;
}
@Override
public ChannelFuture disconnect(ChannelPromise promise) {
return null;
}
@Override
public ChannelFuture close(ChannelPromise promise) {
return null;
}
@Override
public ChannelFuture deregister(ChannelPromise promise) {
return null;
}
@Override
public ChannelHandlerContext read() {
return null;
}
@Override
public ChannelFuture write(Object msg) {
return null;
}
@Override
public ChannelFuture write(Object msg, ChannelPromise promise) {
return null;
}
@Override
public ChannelHandlerContext flush() {
return null;
}
@Override
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
return null;
}
@Override
public ChannelFuture writeAndFlush(Object msg) {
return null;
}
@Override
public ChannelPipeline pipeline() {
return null;
}
@Override
public ByteBufAllocator alloc() {
return null;
}
@Override
public ChannelPromise newPromise() {
return null;
}
@Override
public ChannelProgressivePromise newProgressivePromise() {
return null;
}
@Override
public ChannelFuture newSucceededFuture() {
return null;
}
@Override
public ChannelFuture newFailedFuture(Throwable cause) {
return null;
}
@Override
public ChannelPromise voidPromise() {
return null;
}
@Override
public <T> Attribute<T> attr(AttributeKey<T> attributeKey) {
return null;
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.lisp.ctl;
import com.google.common.collect.ImmutableList;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.junit.Test;
import org.onosproject.lisp.msg.protocols.LispType;
import java.nio.charset.StandardCharsets;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
/**
* Tests for LISP message encoder.
*/
public class LIspMessageEncoderTest {
static class MockLispMessage extends LispMessageAdapter {
LispType type;
public MockLispMessage(LispType type) {
super(type);
this.type = type;
}
@Override
public void writeTo(ByteBuf buffer) {
String message = "LISP message [" + type.toString() + "] ";
buffer.writeBytes(message.getBytes(StandardCharsets.UTF_8));
}
}
@Test
public void testEncodeOneEntry() throws Exception {
LispMessageEncoder encoder = new LispMessageEncoder();
MockLispMessage message = new MockLispMessage(LispType.LISP_MAP_REQUEST);
ByteBuf buff = Unpooled.buffer();
encoder.encode(null, message, buff);
assertThat(buff, notNullValue());
String expected = "LISP message [LISP_MAP_REQUEST] ";
String returned = new String(buff.array(), StandardCharsets.UTF_8).substring(0, expected.length());
assertThat(returned, is(expected));
}
@Test
public void testEncode() throws Exception {
LispMessageEncoder encoder = new LispMessageEncoder();
MockLispMessage request = new MockLispMessage(LispType.LISP_MAP_REQUEST);
MockLispMessage reply = new MockLispMessage(LispType.LISP_MAP_REPLY);
MockLispMessage register = new MockLispMessage(LispType.LISP_MAP_REGISTER);
MockLispMessage notify = new MockLispMessage(LispType.LISP_MAP_NOTIFY);
ByteBuf buff = Unpooled.buffer();
List<MockLispMessage> messages = ImmutableList.of(request, reply, register, notify);
encoder.encode(null, messages, buff);
assertThat(buff, notNullValue());
StringBuilder expBuilder = new StringBuilder();
expBuilder.append("LISP message [LISP_MAP_REQUEST] ");
expBuilder.append("LISP message [LISP_MAP_REPLY] ");
expBuilder.append("LISP message [LISP_MAP_REGISTER] ");
expBuilder.append("LISP message [LISP_MAP_NOTIFY] ");
String expected = expBuilder.toString();
String returned = new String(buff.array(), StandardCharsets.UTF_8).substring(0, expected.length());
assertThat(returned, is(expected));
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.lisp.ctl;
import io.netty.buffer.ByteBuf;
import org.onosproject.lisp.msg.protocols.LispMessage;
import org.onosproject.lisp.msg.protocols.LispType;
/**
* Adapter for testing against a LISP message.
*/
public class LispMessageAdapter implements LispMessage {
LispType type;
private LispMessageAdapter() {}
public LispMessageAdapter(LispType type) {
this.type = type;
}
@Override
public LispType getType() {
return type;
}
@Override
public void writeTo(ByteBuf byteBuf) {
}
@Override
public Builder createBuilder() {
return null;
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.lisp.ctl;
import com.google.common.collect.Lists;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.junit.Test;
import org.onosproject.lisp.msg.protocols.LispMapNotify;
import org.onosproject.lisp.msg.protocols.LispMapRegister;
import org.onosproject.lisp.msg.protocols.LispMapReply;
import org.onosproject.lisp.msg.protocols.LispMapRequest;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
/**
* Tests for LISP message decoder.
*/
public class LispMessageDecoderTest {
private static final int TYPE_SHIFT_BIT = 4;
private static final byte MAP_REQUEST = 1;
private static final byte MAP_REPLY = 2;
private static final byte MAP_REGISTER = 3;
private static final byte MAP_NOTIFY = 4;
private ByteBuf getLispMapRequestBuffer() {
ByteBuf buffer = Unpooled.buffer();
// specify message type
buffer.writeByte(MAP_REQUEST << TYPE_SHIFT_BIT);
// fill up message payload
// second byte denotes the number of RLOCs
byte[] messageData = {0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
byte[] eidData = {0x0, 0x1, 0x0, 0x0, 0x0, 0x0};
byte[] rlocData = {0x0, 0x1, 0x0, 0x0, 0x0, 0x0};
buffer.writeBytes(messageData);
buffer.writeBytes(eidData);
buffer.writeBytes(rlocData);
return buffer;
}
private ByteBuf getLispMapReplyBuffer() {
ByteBuf buffer = Unpooled.buffer();
// specify message type
buffer.writeByte(MAP_REPLY << TYPE_SHIFT_BIT);
// fill up message payload
byte[] messageData = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
buffer.writeBytes(messageData);
return buffer;
}
private ByteBuf getLispMapRegisterBuffer() {
ByteBuf buffer = Unpooled.buffer();
// specify message type
buffer.writeByte(MAP_REGISTER << TYPE_SHIFT_BIT);
// fill up message payload
byte[] messageData = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
byte[] keyId = {0x0, 0x1};
// assume that we have auth data which has 2 bytes size
byte[] authDataLength = {0x0, 0x2};
byte[] authData = {0x0, 0x0};
buffer.writeBytes(messageData);
buffer.writeBytes(keyId);
buffer.writeBytes(authDataLength);
buffer.writeBytes(authData);
return buffer;
}
private ByteBuf getLispMapNotifyBuffer() {
ByteBuf buffer = Unpooled.buffer();
// specify message type
buffer.writeByte(MAP_NOTIFY << TYPE_SHIFT_BIT);
// fill up message payload
byte[] messageData = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
byte[] keyId = {0x0, 0x1};
// assume that we have auth data which has 2 bytes size
byte[] authDataLength = {0x0, 0x2};
byte[] authData = {0x0, 0x0};
buffer.writeBytes(messageData);
buffer.writeBytes(keyId);
buffer.writeBytes(authDataLength);
buffer.writeBytes(authData);
return buffer;
}
@Test(expected = IllegalArgumentException.class)
public void testDecodeNoChannel() throws Exception {
LispMessageDecoder decoder = new LispMessageDecoder();
List<Object> list = Lists.newArrayList();
decoder.decode(new ChannelHandlerContextAdapter(), Unpooled.buffer(), list);
}
@Test
public void testDecode() throws Exception {
LispMessageDecoder decoder = new LispMessageDecoder();
ByteBuf requestBuff = getLispMapRequestBuffer();
ByteBuf replyBuff = getLispMapReplyBuffer();
ByteBuf registerBuff = getLispMapRegisterBuffer();
ByteBuf notifyBuff = getLispMapNotifyBuffer();
List<Object> list = Lists.newArrayList();
decoder.decode(new ChannelHandlerContextAdapter(), requestBuff, list);
decoder.decode(new ChannelHandlerContextAdapter(), replyBuff, list);
decoder.decode(new ChannelHandlerContextAdapter(), registerBuff, list);
decoder.decode(new ChannelHandlerContextAdapter(), notifyBuff, list);
assertThat(list.size(), is(4));
assertThat(list.get(0), is(instanceOf(LispMapRequest.class)));
assertThat(list.get(1), is(instanceOf(LispMapReply.class)));
assertThat(list.get(2), is(instanceOf(LispMapRegister.class)));
assertThat(list.get(3), is(instanceOf(LispMapNotify.class)));
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.lisp.msg.protocols;
import io.netty.buffer.ByteBuf;
import static org.onosproject.lisp.msg.protocols.DefaultLispMapReply.ReplyReader;
import static org.onosproject.lisp.msg.protocols.DefaultLispMapNotify.NotifyReader;
import static org.onosproject.lisp.msg.protocols.DefaultLispMapRegister.RegisterReader;
import static org.onosproject.lisp.msg.protocols.DefaultLispMapRequest.RequestReader;
/**
* A factory class which helps to instantiate LISP reader class.
*/
public final class LispMessageReaderFactory {
private static final int TYPE_SHIFT_BIT = 4;
private LispMessageReaderFactory() {}
/**
* Obtains corresponding LISP message reader.
*
* @param buffer netty byte buffer
* @return LISP message reader
*/
public static LispMessageReader getReader(ByteBuf buffer) {
LispMessageReader reader;
int type = buffer.getByte(0) >> TYPE_SHIFT_BIT;
switch (type) {
case 1:
reader = new RequestReader();
break;
case 2:
reader = new ReplyReader();
break;
case 3:
reader = new RegisterReader();
break;
case 4:
reader = new NotifyReader();
break;
default:
throw new IllegalArgumentException("Unknown LISP message type: " + type);
}
return reader;
}
}