BUG-4344: Expose PCEP local session characteristics
[bgpcep.git] / pcep / impl / src / main / java / org / opendaylight / protocol / pcep / impl / PCEPDispatcherImpl.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.protocol.pcep.impl;
9
10 import com.google.common.base.Preconditions;
11 import io.netty.bootstrap.ServerBootstrap;
12 import io.netty.buffer.PooledByteBufAllocator;
13 import io.netty.channel.ChannelFuture;
14 import io.netty.channel.ChannelInitializer;
15 import io.netty.channel.ChannelOption;
16 import io.netty.channel.EventLoopGroup;
17 import io.netty.channel.socket.SocketChannel;
18 import io.netty.channel.socket.nio.NioServerSocketChannel;
19 import io.netty.util.concurrent.DefaultPromise;
20 import io.netty.util.concurrent.EventExecutor;
21 import io.netty.util.concurrent.GlobalEventExecutor;
22 import io.netty.util.concurrent.Promise;
23 import java.io.Closeable;
24 import java.net.InetSocketAddress;
25 import org.opendaylight.protocol.pcep.PCEPDispatcher;
26 import org.opendaylight.protocol.pcep.PCEPPeerProposal;
27 import org.opendaylight.protocol.pcep.PCEPSessionListenerFactory;
28 import org.opendaylight.protocol.pcep.PCEPSessionNegotiatorFactory;
29 import org.opendaylight.protocol.pcep.spi.MessageRegistry;
30 import org.opendaylight.tcpmd5.api.KeyMapping;
31 import org.opendaylight.tcpmd5.netty.MD5ChannelOption;
32 import org.opendaylight.tcpmd5.netty.MD5ServerChannelFactory;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 /**
37  * Implementation of PCEPDispatcher.
38  */
39 public class PCEPDispatcherImpl implements PCEPDispatcher, Closeable {
40     private static final Logger LOG = LoggerFactory.getLogger(PCEPDispatcherImpl.class);
41     private static final Integer SOCKET_BACKLOG_SIZE = 128;
42     private final PCEPSessionNegotiatorFactory snf;
43     private final PCEPHandlerFactory hf;
44
45
46     private final EventLoopGroup bossGroup;
47     private final EventLoopGroup workerGroup;
48     private final EventExecutor executor;
49     private final MD5ServerChannelFactory<?> scf;
50     private KeyMapping keys;
51
52     /**
53      * Creates an instance of PCEPDispatcherImpl, gets the default selector and opens it.
54      *
55      * @param registry a message registry
56      * @param negotiatorFactory a negotiation factory
57      * @param bossGroup accepts an incoming connection
58      * @param workerGroup handles the traffic of accepted connection
59      */
60     public PCEPDispatcherImpl(final MessageRegistry registry,
61                               final PCEPSessionNegotiatorFactory negotiatorFactory,
62                               final EventLoopGroup bossGroup, final EventLoopGroup workerGroup) {
63         this(registry, negotiatorFactory, bossGroup, workerGroup, null);
64     }
65
66     /**
67      * Creates an instance of PCEPDispatcherImpl, gets the default selector and opens it.
68      *
69      * @param registry a message registry
70      * @param negotiatorFactory a negotiation factory
71      * @param bossGroup accepts an incoming connection
72      * @param workerGroup handles the traffic of accepted connection
73      * @param scf MD5ServerChannelFactory
74      */
75     public PCEPDispatcherImpl(final MessageRegistry registry,
76                               final PCEPSessionNegotiatorFactory negotiatorFactory,
77                               final EventLoopGroup bossGroup, final EventLoopGroup workerGroup,
78                               final MD5ServerChannelFactory<?> scf) {
79         this.snf = Preconditions.checkNotNull(negotiatorFactory);
80         this.hf = new PCEPHandlerFactory(registry);
81         this.bossGroup = Preconditions.checkNotNull(bossGroup);
82         this.workerGroup = Preconditions.checkNotNull(workerGroup);
83         this.executor = Preconditions.checkNotNull(GlobalEventExecutor.INSTANCE);
84         this.scf = scf;
85     }
86
87     @Override
88     public synchronized ChannelFuture createServer(final InetSocketAddress address,
89                                                    final PCEPSessionListenerFactory listenerFactory, final PCEPPeerProposal peerProposal) {
90         return createServer(address, null, listenerFactory, peerProposal);
91     }
92
93     @Override
94     public synchronized ChannelFuture createServer(final InetSocketAddress address, final KeyMapping keys,
95                                                    final PCEPSessionListenerFactory listenerFactory, final PCEPPeerProposal peerProposal) {
96         this.keys = keys;
97
98         final ChannelPipelineInitializer initializer = new ChannelPipelineInitializer() {
99             @Override
100             public void initializeChannel(final SocketChannel ch, final Promise<PCEPSessionImpl> promise) {
101                 ch.pipeline().addLast(PCEPDispatcherImpl.this.hf.getDecoders());
102                 ch.pipeline().addLast("negotiator", PCEPDispatcherImpl.this.snf.getSessionNegotiator(listenerFactory, ch, promise, peerProposal));
103                 ch.pipeline().addLast(PCEPDispatcherImpl.this.hf.getEncoders());
104             }
105         };
106         final ServerBootstrap b = new ServerBootstrap();
107         b.childHandler(new ChannelInitializer<SocketChannel>() {
108             @Override
109             protected void initChannel(final SocketChannel ch) {
110                 initializer.initializeChannel(ch, new DefaultPromise(PCEPDispatcherImpl.this.executor));
111             }
112         });
113         b.option(ChannelOption.SO_BACKLOG, SOCKET_BACKLOG_SIZE);
114
115         b.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
116         this.customizeBootstrap(b);
117         if (b.group() == null) {
118             b.group(this.bossGroup, this.workerGroup);
119         }
120
121         try {
122             b.channel(NioServerSocketChannel.class);
123         } catch (final IllegalStateException e) {
124             LOG.trace("Not overriding channelFactory on bootstrap {}", b, e);
125         }
126
127         final ChannelFuture f = b.bind(address);
128         LOG.debug("Initiated server {} at {}.", f, address);
129
130         this.keys = null;
131         return f;
132     }
133
134     protected void customizeBootstrap(final ServerBootstrap b) {
135         if (this.keys != null && !this.keys.isEmpty()) {
136             if (this.scf == null) {
137                 throw new UnsupportedOperationException("No key access instance available, cannot use key mapping");
138             }
139
140             LOG.debug("Adding MD5 keys {} to bootstrap {}", this.keys, b);
141             b.channelFactory(this.scf);
142             b.option(MD5ChannelOption.TCP_MD5SIG, this.keys);
143         }
144
145         // Make sure we are doing round-robin processing
146         b.childOption(ChannelOption.MAX_MESSAGES_PER_READ, 1);
147     }
148
149     @Override
150     public void close() {
151     }
152
153     protected interface ChannelPipelineInitializer {
154         void initializeChannel(SocketChannel socketChannel, Promise<PCEPSessionImpl> promise);
155     }
156 }