From: Robert Varga Date: Thu, 16 Jan 2014 10:58:29 +0000 (+0100) Subject: Move protocol framework from BGPCEP project X-Git-Tag: jenkins-controller-bulk-release-prepare-only-2-1~56^2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=391180fcde6a7a2336182e346e24a1ff9f754062 Move protocol framework from BGPCEP project This moves the current parts of BGPCEP protocol framework into the controller project, eliminating the circular dependency forced by netconf being resident in this project. Change-Id: I7c8457ebaec5c261217587d60a95a7b35e473d1e Signed-off-by: Robert Varga --- diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index d49437fbaf..06c1d8df7e 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -93,6 +93,7 @@ 0.0.2-SNAPSHOT 0.1.1-SNAPSHOT 0.5.1-SNAPSHOT + 0.4.0-SNAPSHOT 0.0.1-SNAPSHOT 4.0.10.Final 2.4 @@ -693,12 +694,13 @@ yang-ext ${yang-ext.version} - - - org.opendaylight.bgpcep - framework - ${bgpcep.version} - + + + org.opendaylight.controller + protocol-framework + ${protocol-framework.version} + + io.netty diff --git a/opendaylight/commons/protocol-framework/.gitignore b/opendaylight/commons/protocol-framework/.gitignore new file mode 100644 index 0000000000..fc1d35eb24 --- /dev/null +++ b/opendaylight/commons/protocol-framework/.gitignore @@ -0,0 +1,3 @@ +target +.classpath +.settings diff --git a/opendaylight/commons/protocol-framework/pom.xml b/opendaylight/commons/protocol-framework/pom.xml new file mode 100644 index 0000000000..0ea865fa6a --- /dev/null +++ b/opendaylight/commons/protocol-framework/pom.xml @@ -0,0 +1,96 @@ + + + + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.1-SNAPSHOT + ../../commons/opendaylight + + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main + HEAD + + + protocol-framework + ${protocol-framework.version} + Common protocol framework + bundle + ${project.artifactId} + + 3.0.4 + + + + + io.netty + netty-buffer + + + io.netty + netty-codec + + + io.netty + netty-common + + + io.netty + netty-transport + + + com.google.guava + guava + + + com.google.code.findbugs + jsr305 + + + org.slf4j + slf4j-api + + + + + org.opendaylight.yangtools + mockito-configuration + + + junit + junit + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.groupId}.${project.artifactId} + + + + + org.apache.maven.plugins + maven-jar-plugin + + + package + + test-jar + + + + + + + diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractDispatcher.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractDispatcher.java new file mode 100644 index 0000000000..e90af73785 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractDispatcher.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.util.concurrent.DefaultPromise; +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GlobalEventExecutor; +import io.netty.util.concurrent.Promise; + +import java.io.Closeable; +import java.net.InetSocketAddress; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +/** + * Dispatcher class for creating servers and clients. The idea is to first create servers and clients and the run the + * start method that will handle sockets in different thread. + */ +public abstract class AbstractDispatcher, L extends SessionListener> implements Closeable { + + protected interface PipelineInitializer> { + /** + * Initializes channel by specifying the handlers in its pipeline. Handlers are protocol specific, therefore this + * method needs to be implemented in protocol specific Dispatchers. + * + * @param channel whose pipeline should be defined, also to be passed to {@link SessionNegotiatorFactory} + * @param promise to be passed to {@link SessionNegotiatorFactory} + */ + void initializeChannel(SocketChannel channel, Promise promise); + } + + + private static final Logger LOG = LoggerFactory.getLogger(AbstractDispatcher.class); + + private final EventLoopGroup bossGroup; + + private final EventLoopGroup workerGroup; + + private final EventExecutor executor; + + protected AbstractDispatcher(final EventLoopGroup bossGroup, final EventLoopGroup workerGroup) { + this(GlobalEventExecutor.INSTANCE, bossGroup, workerGroup); + } + + protected AbstractDispatcher(final EventExecutor executor, final EventLoopGroup bossGroup, final EventLoopGroup workerGroup) { + this.bossGroup = Preconditions.checkNotNull(bossGroup); + this.workerGroup = Preconditions.checkNotNull(workerGroup); + this.executor = Preconditions.checkNotNull(executor); + } + + + /** + * Creates server. Each server needs factories to pass their instances to client sessions. + * + * @param address address to which the server should be bound + * @param initializer instance of PipelineInitializer used to initialize the channel pipeline + * + * @return ChannelFuture representing the binding process + */ + protected ChannelFuture createServer(final InetSocketAddress address, final PipelineInitializer initializer) { + final ServerBootstrap b = new ServerBootstrap(); + b.group(this.bossGroup, this.workerGroup); + b.channel(NioServerSocketChannel.class); + b.option(ChannelOption.SO_BACKLOG, 128); + b.childHandler(new ChannelInitializer() { + + @Override + protected void initChannel(final SocketChannel ch) { + initializer.initializeChannel(ch, new DefaultPromise(executor)); + } + }); + b.childOption(ChannelOption.SO_KEEPALIVE, true); + + // Bind and start to accept incoming connections. + final ChannelFuture f = b.bind(address); + LOG.debug("Initiated server {} at {}.", f, address); + return f; + + } + + /** + * Creates a client. + * + * @param address remote address + * @param connectStrategy Reconnection strategy to be used when initial connection fails + * + * @return Future representing the connection process. Its result represents the combined success of TCP connection + * as well as session negotiation. + */ + protected Future createClient(final InetSocketAddress address, final ReconnectStrategy strategy, final PipelineInitializer initializer) { + final Bootstrap b = new Bootstrap(); + final ProtocolSessionPromise p = new ProtocolSessionPromise(executor, address, strategy, b); + b.group(this.workerGroup).channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true).handler( + new ChannelInitializer() { + + @Override + protected void initChannel(final SocketChannel ch) { + initializer.initializeChannel(ch, p); + } + }); + p.connect(); + LOG.debug("Client created."); + return p; + } + + /** + * Creates a client. + * + * @param address remote address + * @param connectStrategyFactory Factory for creating reconnection strategy to be used when initial connection fails + * @param reestablishStrategy Reconnection strategy to be used when the already-established session fails + * + * @return Future representing the reconnection task. It will report completion based on reestablishStrategy, e.g. + * success if it indicates no further attempts should be made and failure if it reports an error + */ + protected Future createReconnectingClient(final InetSocketAddress address, final ReconnectStrategyFactory connectStrategyFactory, + final ReconnectStrategy reestablishStrategy, final PipelineInitializer initializer) { + + final ReconnectPromise p = new ReconnectPromise(GlobalEventExecutor.INSTANCE, this, address, connectStrategyFactory, reestablishStrategy, initializer); + p.connect(); + + return p; + + } + + /** + * @deprecated Should only be used with {@link AbstractDispatcher#AbstractDispatcher()} + */ + @Deprecated + @Override + public void close() { + try { + this.workerGroup.shutdownGracefully(); + } finally { + this.bossGroup.shutdownGracefully(); + } + } + +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractProtocolSession.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractProtocolSession.java new file mode 100644 index 0000000000..e7bd665740 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractProtocolSession.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class AbstractProtocolSession extends SimpleChannelInboundHandler implements ProtocolSession { + private static final Logger LOG = LoggerFactory.getLogger(AbstractProtocolSession.class); + + /** + * Handles incoming message (parsing, reacting if necessary). + * + * @param msg incoming message + */ + protected abstract void handleMessage(final M msg); + + /** + * Called when reached the end of input stream while reading. + */ + protected abstract void endOfInput(); + + /** + * Called when the session is added to the pipeline. + */ + protected abstract void sessionUp(); + + @Override + public final void channelInactive(final ChannelHandlerContext ctx) { + LOG.debug("Channel {} inactive.", ctx.channel()); + endOfInput(); + } + + @Override + protected final void channelRead0(final ChannelHandlerContext ctx, final Object msg) { + LOG.debug("Message was received: {}", msg); + handleMessage((M) msg); + } + + @Override + public final void handlerAdded(final ChannelHandlerContext ctx) { + sessionUp(); + } +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractSessionNegotiator.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractSessionNegotiator.java new file mode 100644 index 0000000000..9ecfb1bb0d --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractSessionNegotiator.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.util.concurrent.Promise; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +/** + * Abstract base class for session negotiators. It implements the basic + * substrate to implement SessionNegotiator API specification, with subclasses + * needing to provide only + * + * @param Protocol message type + * @param Protocol session type, has to extend ProtocolSession + */ +public abstract class AbstractSessionNegotiator> extends ChannelInboundHandlerAdapter implements SessionNegotiator { + private final Logger logger = LoggerFactory.getLogger(AbstractSessionNegotiator.class); + private final Promise promise; + protected final Channel channel; + + public AbstractSessionNegotiator(final Promise promise, final Channel channel) { + this.promise = Preconditions.checkNotNull(promise); + this.channel = Preconditions.checkNotNull(channel); + } + + protected abstract void startNegotiation() throws Exception; + protected abstract void handleMessage(M msg) throws Exception; + + protected final void negotiationSuccessful(final S session) { + logger.debug("Negotiation on channel {} successful with session {}", channel, session); + channel.pipeline().replace(this, "session", session); + promise.setSuccess(session); + } + + protected final void negotiationFailed(final Throwable cause) { + logger.debug("Negotiation on channel {} failed", channel, cause); + channel.close(); + promise.setFailure(cause); + } + + @Override + public final void channelActive(final ChannelHandlerContext ctx) { + logger.debug("Starting session negotiation on channel {}", channel); + try { + startNegotiation(); + } catch (Exception e) { + logger.info("Unexpected negotiation failure", e); + negotiationFailed(e); + } + } + + @Override + public final void channelRead(final ChannelHandlerContext ctx, final Object msg) { + logger.debug("Negotiation read invoked on channel {}", channel); + try { + handleMessage((M)msg); + } catch (Exception e) { + logger.debug("Unexpected exception during negotiation", e); + negotiationFailed(e); + } + } +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/DeserializerException.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/DeserializerException.java new file mode 100644 index 0000000000..608e949085 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/DeserializerException.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +/** + * Used when something occurs during parsing bytes to java objects. + * + * @deprecated This exception no longer carries any special meaning. Users + * are advised to stop using it and define their own replacement. + */ +@Deprecated +public class DeserializerException extends Exception { + + private static final long serialVersionUID = -2247000673438452870L; + + /** + * Creates a deserializer exception. + * @param err string + */ + public DeserializerException(final String err) { + super(err); + } + + /** + * Creates a deserializer exception. + * @param err string + * @param e underlying exception + */ + public DeserializerException(final String err, final Throwable e) { + super(err, e); + } +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/DocumentedException.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/DocumentedException.java new file mode 100644 index 0000000000..5e5f29eafe --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/DocumentedException.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +/** + * Documented exception occurrs when an error is thrown that is documented + * in any RFC or draft for the specific protocol. + * + * @deprecated This exception no longer carries any special meaning. Users + * are advised to stop using it and define their own replacement. + */ +@Deprecated +public class DocumentedException extends Exception { + + private static final long serialVersionUID = -3727963789710833704L; + + /** + * Creates a documented exception + * @param message string + */ + public DocumentedException(final String message) { + super(message); + } + + /** + * Creates a documented exception + * @param err string + * @param cause the cause (which is saved for later retrieval by the + * Throwable.getCause() method). (A null value is permitted, and indicates + * that the cause is nonexistent or unknown.) + */ + public DocumentedException(final String err, final Exception cause) { + super(err, cause); + } +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/NeverReconnectStrategy.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/NeverReconnectStrategy.java new file mode 100644 index 0000000000..c480294417 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/NeverReconnectStrategy.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.concurrent.Future; + +import javax.annotation.concurrent.ThreadSafe; + +import com.google.common.base.Preconditions; + +/** + * Utility ReconnectStrategy singleton, which will cause the reconnect process + * to always fail. + */ +@ThreadSafe +public final class NeverReconnectStrategy implements ReconnectStrategy { + private final EventExecutor executor; + private final int timeout; + + public NeverReconnectStrategy(final EventExecutor executor, final int timeout) { + Preconditions.checkArgument(timeout >= 0); + this.executor = Preconditions.checkNotNull(executor); + this.timeout = timeout; + } + + @Override + public Future scheduleReconnect(final Throwable cause) { + return executor.newFailedFuture(new Throwable()); + } + + @Override + public void reconnectSuccessful() { + // Nothing to do + } + + @Override + public int getConnectTimeout() { + return timeout; + } +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolHandlerFactory.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolHandlerFactory.java new file mode 100644 index 0000000000..5c1377d9f5 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolHandlerFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.channel.ChannelHandler; + +import com.google.common.base.Preconditions; + +/** + * @deprecated This is an adaptor class for turning ProtocolMessageFactory into + * Netty encoder/decoder. Use Netty-provided classes directly, by subclassing + * {@link io.netty.handler.codec.ByteToMessageDecoder} or similar instead. + */ +@Deprecated +public class ProtocolHandlerFactory { + private final ProtocolMessageEncoder encoder; + protected final ProtocolMessageFactory msgFactory; + + public ProtocolHandlerFactory(final ProtocolMessageFactory msgFactory) { + this.msgFactory = Preconditions.checkNotNull(msgFactory); + this.encoder = new ProtocolMessageEncoder(msgFactory); + } + + public ChannelHandler[] getEncoders() { + return new ChannelHandler[] { this.encoder }; + } + + public ChannelHandler[] getDecoders() { + return new ChannelHandler[] { new ProtocolMessageDecoder(this.msgFactory) }; + } +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolMessageDecoder.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolMessageDecoder.java new file mode 100644 index 0000000000..725e0a21a7 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolMessageDecoder.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +/** + * @deprecated This is an adaptor class for turning ProtocolMessageFactory into Netty decoder. Use Netty-provided + * classes directly, by subclassing {@link io.netty.handler.codec.ByteToMessageDecoder} or similar instead. + */ +@Deprecated +public final class ProtocolMessageDecoder extends ByteToMessageDecoder { + + private static final Logger LOG = LoggerFactory.getLogger(ProtocolMessageDecoder.class); + + private final ProtocolMessageFactory factory; + + public ProtocolMessageDecoder(final ProtocolMessageFactory factory) { + this.factory = Preconditions.checkNotNull(factory); + } + + @Override + protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final List out) throws Exception { + if (in.readableBytes() == 0) { + LOG.debug("No more content in incoming buffer."); + return; + } + in.markReaderIndex(); + try { + LOG.trace("Received to decode: {}", ByteBufUtil.hexDump(in)); + final byte[] bytes = new byte[in.readableBytes()]; + in.readBytes(bytes); + out.add(this.factory.parse(bytes)); + } catch (DeserializerException | DocumentedException e) { + LOG.debug("Failed to decode protocol message", e); + this.exceptionCaught(ctx, e); + } + in.discardReadBytes(); + } +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolMessageEncoder.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolMessageEncoder.java new file mode 100644 index 0000000000..66378e75d6 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolMessageEncoder.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler.Sharable; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @deprecated This is an adaptor class for turning ProtocolMessageFactory into Netty encoder. Use Netty-provided + * classes directly, by subclassing {@link io.netty.handler.codec.MessageToByteDecoder} or similar instead. + */ +@Deprecated +@Sharable +public final class ProtocolMessageEncoder extends MessageToByteEncoder { + + private static final Logger LOG = LoggerFactory.getLogger(ProtocolMessageEncoder.class); + + private final ProtocolMessageFactory factory; + + public ProtocolMessageEncoder(final ProtocolMessageFactory factory) { + this.factory = factory; + } + + @Override + protected void encode(final ChannelHandlerContext ctx, final Object msg, final ByteBuf out) throws Exception { + LOG.debug("Sent to encode : {}", msg); + final byte[] bytes = this.factory.put((T) msg); + out.writeBytes(bytes); + } +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolMessageFactory.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolMessageFactory.java new file mode 100644 index 0000000000..9b89dc3592 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolMessageFactory.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + + +/** + * Interface for factory for parsing and serializing protocol specific messages. Needs to be implemented by a protocol + * specific message factory. The methods put/parse should delegate parsing to specific message parsers, e.g. + * OpenMessageParser etc. + * + * @param type of messages created by this factory + * + * @deprecated Interact with Netty 4.0 directly, by subclassing {@link io.netty.handler.codec.ByteToMessageCodec} or + * similar. + */ +@Deprecated +public interface ProtocolMessageFactory { + + /** + * Parses message from byte array. Requires specific protocol message header object to parse the header. + * + * @param bytes byte array from which the message will be parsed + * @return List of specific protocol messages + * @throws DeserializerException if some parsing error occurs + * @throws DocumentedException if some documented error occurs + */ + T parse(byte[] bytes) throws DeserializerException, DocumentedException; + + /** + * Serializes protocol specific message to byte array. + * + * @param msg message to be serialized. + * @return byte array resulting message + */ + byte[] put(T msg); +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolSession.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolSession.java new file mode 100644 index 0000000000..6e79d6765d --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolSession.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import java.io.Closeable; + +/** + * Protocol Session represents the finite state machine in underlying protocol, including timers and its purpose is to + * create a connection between server and client. Session is automatically started, when TCP connection is created, but + * can be stopped manually. If the session is up, it has to redirect messages to/from user. Handles also malformed + * messages and unknown requests. + * + * This interface should be implemented by a final class representing a protocol specific session. + */ +public interface ProtocolSession extends Closeable { + @Override + void close(); +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolSessionPromise.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolSessionPromise.java new file mode 100644 index 0000000000..c54bf84987 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolSessionPromise.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelOption; +import io.netty.util.concurrent.DefaultPromise; +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.FutureListener; +import io.netty.util.concurrent.Promise; + +import java.net.InetSocketAddress; + +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +@ThreadSafe +final class ProtocolSessionPromise> extends DefaultPromise { + private static final Logger LOG = LoggerFactory.getLogger(ProtocolSessionPromise.class); + private final ReconnectStrategy strategy; + private final InetSocketAddress address; + private final Bootstrap b; + + @GuardedBy("this") + private Future pending; + + ProtocolSessionPromise(final EventExecutor executor, final InetSocketAddress address, final ReconnectStrategy strategy, + final Bootstrap b) { + super(executor); + this.strategy = Preconditions.checkNotNull(strategy); + this.address = Preconditions.checkNotNull(address); + this.b = Preconditions.checkNotNull(b); + } + + synchronized void connect() { + final Object lock = this; + + try { + final int timeout = this.strategy.getConnectTimeout(); + + LOG.debug("Promise {} attempting connect for {}ms", lock, timeout); + + this.b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeout); + this.pending = this.b.connect(this.address).addListener(new ChannelFutureListener() { + @Override + public void operationComplete(final ChannelFuture cf) throws Exception { + synchronized (lock) { + + LOG.debug("Promise {} connection resolved", lock); + + // Triggered when a connection attempt is resolved. + Preconditions.checkState(ProtocolSessionPromise.this.pending.equals(cf)); + + /* + * The promise we gave out could have been cancelled, + * which cascades to the connect getting cancelled, + * but there is a slight race window, where the connect + * is already resolved, but the listener has not yet + * been notified -- cancellation at that point won't + * stop the notification arriving, so we have to close + * the race here. + */ + if (isCancelled()) { + if (cf.isSuccess()) { + LOG.debug("Closing channel for cancelled promise {}", lock); + cf.channel().close(); + } + return; + } + + if (!cf.isSuccess()) { + LOG.info("Attempt to connect to connect to {} failed", ProtocolSessionPromise.this.address, cf.cause()); + final Future rf = ProtocolSessionPromise.this.strategy.scheduleReconnect(cf.cause()); + rf.addListener(new FutureListener() { + @Override + public void operationComplete(final Future sf) { + synchronized (lock) { + // Triggered when a connection attempt is to be made. + Preconditions.checkState(ProtocolSessionPromise.this.pending.equals(sf)); + + /* + * The promise we gave out could have been cancelled, + * which cascades to the reconnect attempt getting + * cancelled, but there is a slight race window, where + * the reconnect attempt is already enqueued, but the + * listener has not yet been notified -- if cancellation + * happens at that point, we need to catch it here. + */ + if (!isCancelled()) { + if (sf.isSuccess()) { + connect(); + } else { + setFailure(sf.cause()); + } + } + } + } + }); + + ProtocolSessionPromise.this.pending = rf; + } else { + LOG.debug("Promise {} connection successful", lock); + } + } + } + }); + } catch (final Exception e) { + LOG.info("Failed to connect to {}", e); + setFailure(e); + } + } + + @Override + public synchronized boolean cancel(final boolean mayInterruptIfRunning) { + if (super.cancel(mayInterruptIfRunning)) { + this.pending.cancel(mayInterruptIfRunning); + return true; + } + + return false; + } + + @Override + public synchronized Promise setSuccess(final S result) { + LOG.debug("Promise {} completed", this); + this.strategy.reconnectSuccessful(); + return super.setSuccess(result); + } +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectImmediatelyStrategy.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectImmediatelyStrategy.java new file mode 100644 index 0000000000..a567af1f76 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectImmediatelyStrategy.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.concurrent.Future; + +import javax.annotation.concurrent.ThreadSafe; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +/** + * Utility ReconnectStrategy singleton, which will cause the reconnect process + * to immediately schedule a reconnection attempt. + */ +@ThreadSafe +public final class ReconnectImmediatelyStrategy implements ReconnectStrategy { + private static final Logger LOG = LoggerFactory.getLogger(ReconnectImmediatelyStrategy.class); + private final EventExecutor executor; + private final int timeout; + + public ReconnectImmediatelyStrategy(final EventExecutor executor, final int timeout) { + Preconditions.checkArgument(timeout >= 0); + this.executor = Preconditions.checkNotNull(executor); + this.timeout = timeout; + } + + @Override + public Future scheduleReconnect(final Throwable cause) { + LOG.debug("Connection attempt failed", cause); + return executor.newSucceededFuture(null); + } + + @Override + public void reconnectSuccessful() { + // Nothing to do + } + + @Override + public int getConnectTimeout() { + return timeout; + } +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectPromise.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectPromise.java new file mode 100644 index 0000000000..1fa6a81753 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectPromise.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.channel.ChannelFuture; +import io.netty.channel.socket.SocketChannel; +import io.netty.util.concurrent.DefaultPromise; +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.FutureListener; +import io.netty.util.concurrent.Promise; + +import java.io.Closeable; +import java.net.InetSocketAddress; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.opendaylight.protocol.framework.AbstractDispatcher.PipelineInitializer; + +import com.google.common.base.Preconditions; + +final class ReconnectPromise, L extends SessionListener> extends DefaultPromise { + private final AbstractDispatcher dispatcher; + private final InetSocketAddress address; + private final ReconnectStrategyFactory strategyFactory; + private final ReconnectStrategy strategy; + private final PipelineInitializer initializer; + private Future pending; + + private final AtomicBoolean negotiationFinished = new AtomicBoolean(false); + + public ReconnectPromise(final EventExecutor executor, final AbstractDispatcher dispatcher, final InetSocketAddress address, + final ReconnectStrategyFactory connectStrategyFactory, final ReconnectStrategy reestablishStrategy, + final PipelineInitializer initializer) { + super(executor); + this.dispatcher = Preconditions.checkNotNull(dispatcher); + this.address = Preconditions.checkNotNull(address); + this.strategyFactory = Preconditions.checkNotNull(connectStrategyFactory); + this.strategy = Preconditions.checkNotNull(reestablishStrategy); + this.initializer = Preconditions.checkNotNull(initializer); + } + + // FIXME: BUG-190: refactor + + synchronized void connect() { + negotiationFinished.set(false); + + final ReconnectStrategy cs = this.strategyFactory.createReconnectStrategy(); + final ReconnectStrategy rs = new ReconnectStrategy() { + @Override + public Future scheduleReconnect(final Throwable cause) { + return cs.scheduleReconnect(cause); + } + + @Override + public void reconnectSuccessful() { + cs.reconnectSuccessful(); + } + + @Override + public int getConnectTimeout() throws Exception { + final int cst = cs.getConnectTimeout(); + final int rst = ReconnectPromise.this.strategy.getConnectTimeout(); + + if (cst == 0) { + return rst; + } + if (rst == 0) { + return cst; + } + return Math.min(cst, rst); + } + }; + + final Future cf = this.dispatcher.createClient(this.address, rs, new PipelineInitializer() { + @Override + public void initializeChannel(final SocketChannel channel, final Promise promise) { + addChannelClosedListener(channel.closeFuture()); + initializer.initializeChannel(channel, promise); + } + }); + + final Object lock = this; + this.pending = cf; + + cf.addListener(new FutureListener() { + + @Override + public void operationComplete(final Future future) { + synchronized (lock) { + if (!future.isSuccess()) { + final Future rf = ReconnectPromise.this.strategy.scheduleReconnect(cf.cause()); + + if(rf == null) { + // This should reflect: no more reconnecting strategies, enough + // Currently all reconnect strategies fail with exception, should return null + return; + } + + ReconnectPromise.this.pending = rf; + + rf.addListener(new FutureListener() { + @Override + public void operationComplete(final Future sf) { + synchronized (lock) { + /* + * The promise we gave out could have been cancelled, + * which cascades to the reconnect attempt getting + * cancelled, but there is a slight race window, where + * the reconnect attempt is already enqueued, but the + * listener has not yet been notified -- if cancellation + * happens at that point, we need to catch it here. + */ + if (!isCancelled()) { + if (sf.isSuccess()) { + connect(); + } else { + setFailure(sf.cause()); + } + } + } + } + }); + } else { + /* + * FIXME: BUG-190: we have a slight race window with cancellation + * here. Analyze and define its semantics. + */ + ReconnectPromise.this.strategy.reconnectSuccessful(); + negotiationFinished.set(true); + } + } + } + }); + } + + private final ClosedChannelListener closedChannelListener = new ClosedChannelListener(); + + class ClosedChannelListener implements Closeable, FutureListener { + + private final AtomicBoolean stop = new AtomicBoolean(false); + + @Override + public void operationComplete(final Future future) throws Exception { + if (stop.get()) { + return; + } + + // Start reconnecting crashed session after negotiation was successful + if (!negotiationFinished.get()) { + return; + } + + connect(); + } + + @Override + public void close() { + this.stop.set(true); + } + } + + private void addChannelClosedListener(final ChannelFuture channelFuture) { + channelFuture.addListener(closedChannelListener); + } + + @Override + public synchronized boolean cancel(final boolean mayInterruptIfRunning) { + closedChannelListener.close(); + + if (super.cancel(mayInterruptIfRunning)) { + this.pending.cancel(mayInterruptIfRunning); + return true; + } + + return false; + } +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectStrategy.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectStrategy.java new file mode 100644 index 0000000000..24ff84b6ab --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectStrategy.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.util.concurrent.Future; + +/** + * Interface exposed by a reconnection strategy provider. A reconnection + * strategy decides whether to attempt reconnection and when to do that. + * + * The proper way of using this API is such that when a connection attempt + * has failed, the user will call scheduleReconnect() to obtain a future, + * which tracks schedule of the next connect attempt. The user should add its + * own listener to be get notified when the future is done. Once the + * the notification fires, user should examine the future to see whether + * it is successful or not. If it is successful, the user should immediately + * initiate a connection attempt. If it is unsuccessful, the user must + * not attempt any more connection attempts and should abort the reconnection + * process. + */ +public interface ReconnectStrategy { + /** + * Query the strategy for the connect timeout. + * + * @return connect try timeout in milliseconds, or + * 0 for infinite (or system-default) timeout + * @throws Exception if the connection should not be attempted + */ + int getConnectTimeout() throws Exception; + + /** + * Schedule a connection attempt. The precise time when the connection + * should be attempted is signaled by successful completion of returned + * future. + * + * @param cause Cause of previous failure + * @return a future tracking the schedule, may not be null + * @throws IllegalStateException when a connection attempt is currently + * scheduled. + */ + Future scheduleReconnect(Throwable cause); + + /** + * Reset the strategy state. Users call this method once the reconnection + * process succeeds. + */ + void reconnectSuccessful(); +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectStrategyFactory.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectStrategyFactory.java new file mode 100644 index 0000000000..3c61044bee --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectStrategyFactory.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +/** + * Factory interface for creating new ReconnectStrategy instances. This is + * primarily useful for allowing injection of a specific type of strategy for + * on-demand use, pretty much like you would use a ThreadFactory. + */ +public interface ReconnectStrategyFactory { + /** + * Create a new ReconnectStrategy. + * + * @return a new reconnecty strategy + */ + ReconnectStrategy createReconnectStrategy(); +} + diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionListener.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionListener.java new file mode 100644 index 0000000000..3c429fc774 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionListener.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import java.util.EventListener; + +/** + * Listener that receives session state informations. This interface should be + * implemented by a protocol specific abstract class, that is extended by + * a final class that implements the methods. + */ +public interface SessionListener, T extends TerminationReason> extends EventListener { + /** + * Fired when the session was established successfully. + * + * @param remoteParams Peer address families which we accepted + */ + void onSessionUp(S session); + + /** + * Fired when the session went down because of an IO error. Implementation should take care of closing underlying + * session. + * + * @param session that went down + * @param e Exception that was thrown as the cause of session being down + */ + void onSessionDown(S session, Exception e); + + /** + * Fired when the session is terminated locally. The session has already been closed and transitioned to IDLE state. + * Any outstanding queued messages were not sent. The user should not attempt to make any use of the session. + * + * @param reason the cause why the session went down + */ + void onSessionTerminated(S session, T reason); + + /** + * Fired when a normal protocol message is received. + * + * @param message Protocol message + */ + void onMessage(S session, M message); +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionListenerFactory.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionListenerFactory.java new file mode 100644 index 0000000000..11871286cf --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionListenerFactory.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + + +/** + * Factory for generating Session Listeners. Used by a server. This interface should be + * implemented by a protocol specific abstract class, that is extended by + * a final class that implements the methods. + */ +public interface SessionListenerFactory> { + /** + * Returns one session listener + * @return specific session listener + */ + T getSessionListener(); +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionNegotiator.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionNegotiator.java new file mode 100644 index 0000000000..3de64b07ff --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionNegotiator.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.channel.ChannelInboundHandler; + +/** + * Session negotiator concepts. A negotiator is responsible for message + * handling while the exact session parameters are not known. Once the + * session parameters are finalized, the negotiator replaces itself in + * the channel pipeline with the session. + * + * @param Protocol session type. + */ +public interface SessionNegotiator> extends ChannelInboundHandler { + +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionNegotiatorFactory.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionNegotiatorFactory.java new file mode 100644 index 0000000000..90844ca712 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionNegotiatorFactory.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.channel.Channel; +import io.netty.util.concurrent.Promise; + +/** + * A factory class creating SessionNegotiators. + * + * @param session type + */ +public interface SessionNegotiatorFactory, L extends SessionListener> { + /** + * Create a new negotiator attached to a channel, which will notify + * a promise once the negotiation completes. + * + * @param channel Underlying channel + * @param promise Promise to be notified + * @return new negotiator instance + */ + SessionNegotiator getSessionNegotiator(SessionListenerFactory factory, Channel channel, Promise promise); +} diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/TerminationReason.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/TerminationReason.java new file mode 100644 index 0000000000..1a6179dc35 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/TerminationReason.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +/** + * Marker interface for grouping session termination cause. + */ +public interface TerminationReason { + + /** + * Get cause of session termination. + * @return human-readable cause. + */ + String getErrorMessage(); +} + diff --git a/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/TimedReconnectStrategy.java b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/TimedReconnectStrategy.java new file mode 100644 index 0000000000..8bb326821d --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/TimedReconnectStrategy.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.concurrent.Future; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +/** + * Swiss army knife equivalent for reconnect strategies. + * + * This strategy continues to schedule reconnect attempts, each having to complete in a fixed time (connectTime). + * + * Initial sleep time is specified as minSleep. Each subsequent unsuccessful attempt multiplies this time by a constant + * factor (sleepFactor) -- this allows for either constant reconnect times (sleepFactor = 1), or various degrees of + * exponential back-off (sleepFactor > 1). Maximum sleep time between attempts can be capped to a specific value + * (maxSleep). + * + * The strategy can optionally give up based on two criteria: + * + * A preset number of connection retries (maxAttempts) has been reached, or + * + * A preset absolute deadline is reached (deadline nanoseconds, as reported by System.nanoTime(). In this specific case, + * both connectTime and maxSleep will be controlled such that the connection attempt is resolved as closely to the + * deadline as possible. + * + * Both these caps can be combined, with the strategy giving up as soon as the first one is reached. + */ +@ThreadSafe +public final class TimedReconnectStrategy implements ReconnectStrategy { + private static final Logger LOG = LoggerFactory.getLogger(TimedReconnectStrategy.class); + private final EventExecutor executor; + private final Long deadline, maxAttempts, maxSleep; + private final double sleepFactor; + private final int connectTime; + private final long minSleep; + + @GuardedBy("this") + private long attempts; + + @GuardedBy("this") + private long lastSleep; + + @GuardedBy("this") + private boolean scheduled; + + public TimedReconnectStrategy(final EventExecutor executor, final int connectTime, final long minSleep, final double sleepFactor, + final Long maxSleep, final Long maxAttempts, final Long deadline) { + Preconditions.checkArgument(maxSleep == null || minSleep <= maxSleep); + Preconditions.checkArgument(sleepFactor >= 1); + Preconditions.checkArgument(connectTime >= 0); + this.executor = Preconditions.checkNotNull(executor); + this.deadline = deadline; + this.maxAttempts = maxAttempts; + this.minSleep = minSleep; + this.maxSleep = maxSleep; + this.sleepFactor = sleepFactor; + this.connectTime = connectTime; + } + + @Override + public synchronized Future scheduleReconnect(final Throwable cause) { + LOG.debug("Connection attempt failed", cause); + + // Check if a reconnect attempt is scheduled + Preconditions.checkState(!this.scheduled); + + // Get a stable 'now' time for deadline calculations + final long now = System.nanoTime(); + + // Obvious stop conditions + if (this.maxAttempts != null && this.attempts >= this.maxAttempts) { + return this.executor.newFailedFuture(new Throwable("Maximum reconnection attempts reached")); + } + if (this.deadline != null && this.deadline <= now) { + return this.executor.newFailedFuture(new TimeoutException("Reconnect deadline reached")); + } + + /* + * First connection attempt gets initialized to minimum sleep, + * each subsequent is exponentially backed off by sleepFactor. + */ + if (this.attempts != 0) { + this.lastSleep *= this.sleepFactor; + } else { + this.lastSleep = this.minSleep; + } + + // Cap the sleep time to maxSleep + if (this.maxSleep != null && this.lastSleep > this.maxSleep) { + LOG.debug("Capped sleep time from {} to {}", this.lastSleep, this.maxSleep); + this.lastSleep = this.maxSleep; + } + + this.attempts++; + + // Check if the reconnect attempt is within the deadline + if (this.deadline != null && this.deadline <= now + TimeUnit.MILLISECONDS.toNanos(this.lastSleep)) { + return this.executor.newFailedFuture(new TimeoutException("Next reconnect would happen after deadline")); + } + + LOG.debug("Connection attempt {} sleeping for {} milliseconds", this.attempts, this.lastSleep); + + // If we are not sleeping at all, return an already-succeeded future + if (this.lastSleep == 0) { + return this.executor.newSucceededFuture(null); + } + + // Need to retain a final reference to this for locking purposes, + // also set the scheduled flag. + final Object lock = this; + this.scheduled = true; + + // Schedule a task for the right time. It will also clear the flag. + return this.executor.schedule(new Callable() { + @Override + public Void call() throws TimeoutException { + synchronized (lock) { + Preconditions.checkState(TimedReconnectStrategy.this.scheduled); + TimedReconnectStrategy.this.scheduled = false; + } + + return null; + } + }, this.lastSleep, TimeUnit.MILLISECONDS); + } + + @Override + public synchronized void reconnectSuccessful() { + Preconditions.checkState(!this.scheduled); + this.attempts = 0; + } + + @Override + public int getConnectTimeout() throws TimeoutException { + int timeout = this.connectTime; + + if (this.deadline != null) { + + // If there is a deadline, we may need to cap the connect + // timeout to meet the deadline. + final long now = System.nanoTime(); + if (now >= this.deadline) { + throw new TimeoutException("Reconnect deadline already passed"); + } + + final long left = TimeUnit.NANOSECONDS.toMillis(this.deadline - now); + if (left < 1) { + throw new TimeoutException("Connect timeout too close to deadline"); + } + + /* + * A bit of magic: + * - if time left is less than the timeout, set it directly + * - if there is no timeout, and time left is: + * - less than maximum integer, set timeout to time left + * - more than maximum integer, set timeout Integer.MAX_VALUE + */ + if (timeout > left) { + timeout = (int) left; + } else if (timeout == 0) { + timeout = left <= Integer.MAX_VALUE ? (int) left : Integer.MAX_VALUE; + } + } + return timeout; + } +} diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/ComplementaryTest.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/ComplementaryTest.java new file mode 100644 index 0000000000..80e6ad9cde --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/ComplementaryTest.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +@Deprecated +public class ComplementaryTest { + + @Test + public void testExceptions() { + final DeserializerException de = new DeserializerException("some error"); + final DocumentedException ee = new DocumentedException("some error"); + + assertEquals(de.getMessage(), ee.getMessage()); + } +} diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/ServerTest.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/ServerTest.java new file mode 100644 index 0000000000..bead1ee49e --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/ServerTest.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.util.concurrent.DefaultPromise; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GlobalEventExecutor; +import io.netty.util.concurrent.Promise; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ServerTest { + SimpleDispatcher clientDispatcher, dispatcher; + + final SimpleSessionListener pce = new SimpleSessionListener(); + + SimpleSession session = null; + + ChannelFuture server = null; + + InetSocketAddress serverAddress; + private NioEventLoopGroup eventLoopGroup; + + + @Before + public void setUp() { + final int port = 10000 + (int)(10000 * Math.random()); + serverAddress = new InetSocketAddress("127.0.0.1", port); + eventLoopGroup = new NioEventLoopGroup(); + } + + @Test + public void testConnectionEstablished() throws Exception { + final Promise p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE); + + this.dispatcher = new SimpleDispatcher(new SessionNegotiatorFactory() { + + @Override + public SessionNegotiator getSessionNegotiator(final SessionListenerFactory factory, + final Channel channel, final Promise promise) { + p.setSuccess(true); + return new SimpleSessionNegotiator(promise, channel); + } + }, new DefaultPromise(GlobalEventExecutor.INSTANCE), eventLoopGroup); + + this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory() { + @Override + public SimpleSessionListener getSessionListener() { + return new SimpleSessionListener(); + } + }); + + this.server.get(); + + this.clientDispatcher = new SimpleDispatcher(new SessionNegotiatorFactory() { + @Override + public SessionNegotiator getSessionNegotiator(final SessionListenerFactory factory, + final Channel channel, final Promise promise) { + return new SimpleSessionNegotiator(promise, channel); + } + }, new DefaultPromise(GlobalEventExecutor.INSTANCE), eventLoopGroup); + + this.session = this.clientDispatcher.createClient(this.serverAddress, + new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000), new SessionListenerFactory() { + @Override + public SimpleSessionListener getSessionListener() { + return new SimpleSessionListener(); + } + }).get(6, TimeUnit.SECONDS); + + assertEquals(true, p.get(3, TimeUnit.SECONDS)); + } + + @Test + public void testConnectionFailed() throws IOException, InterruptedException, ExecutionException, TimeoutException { + final Promise p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE); + + this.dispatcher = new SimpleDispatcher(new SessionNegotiatorFactory() { + + @Override + public SessionNegotiator getSessionNegotiator(final SessionListenerFactory factory, + final Channel channel, final Promise promise) { + p.setSuccess(true); + return new SimpleSessionNegotiator(promise, channel); + } + }, new DefaultPromise(GlobalEventExecutor.INSTANCE), eventLoopGroup); + + this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory() { + @Override + public SimpleSessionListener getSessionListener() { + return new SimpleSessionListener(); + } + }); + + this.server.get(); + + this.clientDispatcher = new SimpleDispatcher(new SessionNegotiatorFactory() { + @Override + public SessionNegotiator getSessionNegotiator(final SessionListenerFactory factory, + final Channel channel, final Promise promise) { + return new SimpleSessionNegotiator(promise, channel); + } + }, new DefaultPromise(GlobalEventExecutor.INSTANCE), eventLoopGroup); + + this.session = this.clientDispatcher.createClient(this.serverAddress, + new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000), new SessionListenerFactory() { + @Override + public SimpleSessionListener getSessionListener() { + return new SimpleSessionListener(); + } + }).get(6, TimeUnit.SECONDS); + + final Future session = this.clientDispatcher.createClient(this.serverAddress, + new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000), new SessionListenerFactory() { + @Override + public SimpleSessionListener getSessionListener() { + return new SimpleSessionListener(); + } + }); + assertFalse(session.isSuccess()); + } + + @After + public void tearDown() throws IOException, InterruptedException { + this.server.channel().close(); + this.eventLoopGroup.shutdownGracefully(); + try { + Thread.sleep(500); + } catch (final InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/Session.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/Session.java new file mode 100644 index 0000000000..22ad930656 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/Session.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Lists; + +public class Session extends AbstractProtocolSession { + + private static final Logger logger = LoggerFactory.getLogger(Session.class); + + public final List msgs = Lists.newArrayList(); + + public boolean up = false; + + @Override + public void close() { + + } + + @Override + public void handleMessage(final SimpleMessage msg) { + logger.debug("Message received: {}", msg.getMessage()); + this.up = true; + this.msgs.add(msg); + logger.debug(this.msgs.size() + ""); + } + + @Override + public void endOfInput() { + logger.debug("End of input reported."); + } + + @Override + protected void sessionUp() { + logger.debug("Session up reported."); + } +} diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleByteToMessageDecoder.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleByteToMessageDecoder.java new file mode 100644 index 0000000000..cb9e180858 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleByteToMessageDecoder.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; + +import java.util.List; + +import com.google.common.base.Charsets; + +/** + * + */ +public class SimpleByteToMessageDecoder extends ByteToMessageDecoder { + @Override + protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final List out) { + out.add(new SimpleMessage(Charsets.UTF_8.decode(in.nioBuffer()).toString())); + } +} diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleDispatcher.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleDispatcher.java new file mode 100644 index 0000000000..12aac9ecc5 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleDispatcher.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelOutboundHandler; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.Promise; + +import java.net.InetSocketAddress; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +public class SimpleDispatcher extends AbstractDispatcher { + private static final Logger logger = LoggerFactory.getLogger(SimpleDispatcher.class); + + private final SessionNegotiatorFactory negotiatorFactory; + private final ChannelOutboundHandler encoder = new SimpleMessageToByteEncoder(); + + private final class SimplePipelineInitializer implements PipelineInitializer { + final SessionListenerFactory listenerFactory; + + SimplePipelineInitializer(final SessionListenerFactory listenerFactory) { + this.listenerFactory = Preconditions.checkNotNull(listenerFactory); + } + + @Override + public void initializeChannel(final SocketChannel channel, final Promise promise) { + channel.pipeline().addLast(new SimpleByteToMessageDecoder()); + channel.pipeline().addLast("negotiator", negotiatorFactory.getSessionNegotiator(listenerFactory, channel, promise)); + channel.pipeline().addLast(encoder); + logger.debug("initialization completed for channel {}", channel); + } + + } + + public SimpleDispatcher(final SessionNegotiatorFactory negotiatorFactory, + final Promise promise, final EventLoopGroup eventLoopGroup) { + super(eventLoopGroup, eventLoopGroup); + this.negotiatorFactory = Preconditions.checkNotNull(negotiatorFactory); + } + + public Future createClient(final InetSocketAddress address, final ReconnectStrategy strategy, final SessionListenerFactory listenerFactory) { + return super.createClient(address, strategy, new SimplePipelineInitializer(listenerFactory)); + } + + public ChannelFuture createServer(final InetSocketAddress address, final SessionListenerFactory listenerFactory) { + return super.createServer(address, new SimplePipelineInitializer(listenerFactory)); + } + + @Override + public void close() { + } +} diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleMessage.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleMessage.java new file mode 100644 index 0000000000..551d657c34 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleMessage.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +public class SimpleMessage { + + private final String s; + + public SimpleMessage(final String s) { + this.s = s; + } + + public String getMessage() { + return this.s; + } +} diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleMessageToByteEncoder.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleMessageToByteEncoder.java new file mode 100644 index 0000000000..b3ed3b7c04 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleMessageToByteEncoder.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler.Sharable; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +/** + * + */ +@Sharable +public class SimpleMessageToByteEncoder extends MessageToByteEncoder { + @Override + protected void encode(final ChannelHandlerContext ctx, final SimpleMessage msg, final ByteBuf out) { + out.writeBytes(msg.getMessage().getBytes()); + } +} diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleSession.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleSession.java new file mode 100644 index 0000000000..9056f8d341 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleSession.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +public final class SimpleSession extends AbstractProtocolSession { + + public SimpleSession() { + } + + @Override + public void close() { + } + + @Override + public void handleMessage(final SimpleMessage msg) { + } + + @Override + public void endOfInput() { + } + + @Override + protected void sessionUp() { + } +} diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleSessionListener.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleSessionListener.java new file mode 100644 index 0000000000..7004ee6141 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleSessionListener.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Simple Session Listener that is notified about messages and changes in the session. + */ +public class SimpleSessionListener implements SessionListener { + private static final Logger logger = LoggerFactory.getLogger(SimpleSessionListener.class); + + public List messages = new ArrayList(); + + public boolean up = false; + + public boolean failed = false; + + @Override + public void onMessage(final SimpleSession session, final SimpleMessage message) { + logger.debug("Received message: " + message.getClass() + " " + message); + this.messages.add(message); + } + + @Override + public void onSessionUp(final SimpleSession session) { + this.up = true; + } + + @Override + public void onSessionDown(final SimpleSession session, final Exception e) { + this.failed = true; + this.notifyAll(); + } + + @Override + public void onSessionTerminated(final SimpleSession session, final TerminationReason reason) { + this.failed = true; + this.notifyAll(); + } +} diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleSessionListenerFactory.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleSessionListenerFactory.java new file mode 100644 index 0000000000..3fabe3cc8f --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleSessionListenerFactory.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +public class SimpleSessionListenerFactory implements SessionListenerFactory { + + @Override + public SimpleSessionListener getSessionListener() { + return new SimpleSessionListener(); + } +} diff --git a/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleSessionNegotiator.java b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleSessionNegotiator.java new file mode 100644 index 0000000000..39d5855bc0 --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleSessionNegotiator.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.protocol.framework; + +import io.netty.channel.Channel; +import io.netty.util.concurrent.Promise; + +public class SimpleSessionNegotiator extends AbstractSessionNegotiator { + + public SimpleSessionNegotiator(Promise promise, Channel channel) { + super(promise, channel); + } + + @Override + protected void startNegotiation() throws Exception { + negotiationSuccessful(new SimpleSession()); + } + + @Override + protected void handleMessage(SimpleMessage msg) throws Exception { + throw new IllegalStateException("This method should never be invoked"); + } +} diff --git a/opendaylight/commons/protocol-framework/src/test/resources/logback-test.xml b/opendaylight/commons/protocol-framework/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..7ace93024a --- /dev/null +++ b/opendaylight/commons/protocol-framework/src/test/resources/logback-test.xml @@ -0,0 +1,13 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + diff --git a/opendaylight/config/pom.xml b/opendaylight/config/pom.xml index 4a9d37623c..2ee792628b 100644 --- a/opendaylight/config/pom.xml +++ b/opendaylight/config/pom.xml @@ -100,11 +100,6 @@ org.osgi.core ${osgi.version} - - com.google.code.findbugs - jsr305 - 2.0.1 - commons-io commons-io diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index 22799281e2..7aa25320e3 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -168,6 +168,10 @@ concepts ${concepts.version} + + org.opendaylight.controller + protocol-framework + org.opendaylight.yangtools concepts @@ -1364,12 +1368,6 @@ opendaylight-l2-types - - - org.opendaylight.bgpcep - framework - - io.netty diff --git a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java index 82ad7cffe2..c0dec7c029 100644 --- a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java +++ b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java @@ -38,10 +38,10 @@ public class TestHelper { public static Option configMinumumBundles() { return new DefaultCompositeOption( - mavenBundle("org.opendaylight.bgpcep", "framework").versionAsInProject(), // mavenBundle("commons-codec", "commons-codec").versionAsInProject(), mavenBundle(CONTROLLER, "config-api").versionAsInProject(), // // + mavenBundle(CONTROLLER, "protocol-framework").versionAsInProject(), // mavenBundle(CONTROLLER, "config-manager").versionAsInProject(), // // mavenBundle("commons-io", "commons-io").versionAsInProject(), // mavenBundle(CONTROLLER, "config-api").versionAsInProject(), // diff --git a/opendaylight/md-sal/test/sal-rest-connector-it/src/test/java/org/opendaylight/controller/test/restconf/it/ServiceProviderController.java b/opendaylight/md-sal/test/sal-rest-connector-it/src/test/java/org/opendaylight/controller/test/restconf/it/ServiceProviderController.java index a9637f8672..8763724560 100644 --- a/opendaylight/md-sal/test/sal-rest-connector-it/src/test/java/org/opendaylight/controller/test/restconf/it/ServiceProviderController.java +++ b/opendaylight/md-sal/test/sal-rest-connector-it/src/test/java/org/opendaylight/controller/test/restconf/it/ServiceProviderController.java @@ -211,6 +211,7 @@ public class ServiceProviderController { mavenBundle(ODL, "logback-config").versionAsInProject(), mavenBundle(ODL, "config-persister-api").versionAsInProject(), // mavenBundle(ODL,"config-persister-file-adapter").versionAsInProject(), + mavenBundle(ODL, "protocol-framework").versionAsInProject(), mavenBundle(ODL, "netconf-api").versionAsInProject(), mavenBundle(ODL, "netconf-impl").versionAsInProject(), mavenBundle(ODL, "netconf-client").versionAsInProject(), @@ -220,7 +221,6 @@ public class ServiceProviderController { mavenBundle(ODL, "config-netconf-connector").versionAsInProject(), mavenBundle(ODL, "config-persister-impl").versionAsInProject(), - mavenBundle("org.opendaylight.bgpcep", "framework").versionAsInProject(), mavenBundle(YANG, "binding-generator-spi").versionAsInProject(), // mavenBundle(YANG, "binding-model-api").versionAsInProject(), // mavenBundle(YANG, "binding-generator-util").versionAsInProject(), diff --git a/opendaylight/netconf/netconf-api/pom.xml b/opendaylight/netconf/netconf-api/pom.xml index 856bd77c20..d085986d2a 100644 --- a/opendaylight/netconf/netconf-api/pom.xml +++ b/opendaylight/netconf/netconf-api/pom.xml @@ -18,6 +18,10 @@ org.opendaylight.controller config-api + + org.opendaylight.controller + protocol-framework + org.opendaylight.yangtools.model ietf-inet-types @@ -35,12 +39,6 @@ ietf-netconf-monitoring-extension ${project.version} - - - org.opendaylight.bgpcep - framework - ${bgpcep.version} - diff --git a/opendaylight/netconf/netconf-client/pom.xml b/opendaylight/netconf/netconf-client/pom.xml index de4fb101f9..56982a134b 100644 --- a/opendaylight/netconf/netconf-client/pom.xml +++ b/opendaylight/netconf/netconf-client/pom.xml @@ -22,9 +22,8 @@ netconf-api - org.opendaylight.bgpcep - framework - ${bgpcep.version} + org.opendaylight.controller + protocol-framework com.google.guava diff --git a/opendaylight/netconf/netconf-impl/pom.xml b/opendaylight/netconf/netconf-impl/pom.xml index 981a81d7b6..766772b1e9 100644 --- a/opendaylight/netconf/netconf-impl/pom.xml +++ b/opendaylight/netconf/netconf-impl/pom.xml @@ -36,6 +36,10 @@ org.opendaylight.controller config-util + + org.opendaylight.controller + protocol-framework + ${project.groupId} netconf-mapping-api @@ -46,11 +50,6 @@ ietf-inet-types - - org.opendaylight.bgpcep - framework - - org.osgi org.osgi.core diff --git a/opendaylight/netconf/netconf-util/pom.xml b/opendaylight/netconf/netconf-util/pom.xml index 2f6da9e3c6..9078d7de9d 100644 --- a/opendaylight/netconf/netconf-util/pom.xml +++ b/opendaylight/netconf/netconf-util/pom.xml @@ -27,8 +27,8 @@ config-api - org.opendaylight.bgpcep - framework + org.opendaylight.controller + protocol-framework diff --git a/opendaylight/netconf/pom.xml b/opendaylight/netconf/pom.xml index 21a1ffe81d..3a96b42263 100644 --- a/opendaylight/netconf/pom.xml +++ b/opendaylight/netconf/pom.xml @@ -74,12 +74,6 @@ org.osgi.core ${osgi.version} - - org.opendaylight.bgpcep - mockito-configuration - ${bgpcep.version} - test - ${project.groupId} config-api diff --git a/pom.xml b/pom.xml index f7f9bc2256..f5eb230b6e 100644 --- a/pom.xml +++ b/pom.xml @@ -124,6 +124,7 @@ opendaylight/commons/concepts + opendaylight/commons/protocol-framework opendaylight/commons/httpclient opendaylight/commons/checkstyle opendaylight/commons/opendaylight