2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.protocol.pcep.impl;
10 import io.netty.channel.Channel;
11 import io.netty.channel.ChannelFuture;
12 import io.netty.channel.ChannelFutureListener;
13 import io.netty.util.concurrent.Promise;
15 import java.io.Closeable;
16 import java.io.IOException;
17 import java.net.InetSocketAddress;
18 import java.util.Comparator;
20 import java.util.WeakHashMap;
22 import javax.annotation.concurrent.GuardedBy;
24 import org.opendaylight.protocol.framework.AbstractSessionNegotiator;
25 import org.opendaylight.protocol.framework.SessionListenerFactory;
26 import org.opendaylight.protocol.framework.SessionNegotiator;
27 import org.opendaylight.protocol.framework.SessionNegotiatorFactory;
28 import org.opendaylight.protocol.pcep.PCEPSessionListener;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.Message;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 import com.google.common.collect.BiMap;
34 import com.google.common.collect.HashBiMap;
35 import com.google.common.primitives.UnsignedBytes;
38 * SessionNegotiator which takes care of making sure sessions between PCEP peers are kept unique. This needs to be
39 * further subclassed to provide either a client or server factory.
41 public abstract class AbstractPCEPSessionNegotiatorFactory implements
42 SessionNegotiatorFactory<Message, PCEPSessionImpl, PCEPSessionListener> {
43 private static final Comparator<byte[]> comparator = UnsignedBytes.lexicographicalComparator();
44 private static final Logger logger = LoggerFactory.getLogger(AbstractPCEPSessionNegotiatorFactory.class);
45 private final BiMap<byte[], Closeable> sessions = HashBiMap.create();
46 private final Map<byte[], Short> sessionIds = new WeakHashMap<>();
49 * Create a new negotiator. This method needs to be implemented by subclasses to actually provide a negotiator.
51 * @param promise Session promise to be completed by the negotiator
52 * @param channel Associated channel
53 * @param sessionId Session ID assigned to the resulting session
54 * @return a PCEP session negotiator
56 protected abstract AbstractPCEPSessionNegotiator createNegotiator(Promise<PCEPSessionImpl> promise, PCEPSessionListener listener,
57 Channel channel, short sessionId);
60 public final SessionNegotiator<PCEPSessionImpl> getSessionNegotiator(final SessionListenerFactory<PCEPSessionListener> factory,
61 final Channel channel, final Promise<PCEPSessionImpl> promise) {
63 final Object lock = this;
65 logger.debug("Instantiating bootstrap negotiator for channel {}", channel);
66 return new AbstractSessionNegotiator<Message, PCEPSessionImpl>(promise, channel) {
68 protected void startNegotiation() throws Exception {
69 logger.debug("Bootstrap negotiation for channel {} started", this.channel);
72 * We have a chance to see if there's a client session already
73 * registered for this client.
75 final byte[] clientAddress = ((InetSocketAddress) this.channel.remoteAddress()).getAddress().getAddress();
79 if (AbstractPCEPSessionNegotiatorFactory.this.sessions.containsKey(clientAddress)) {
80 // FIXME: cross-reference this to RFC5440
82 final byte[] serverAddress = ((InetSocketAddress) this.channel.localAddress()).getAddress().getAddress();
83 if (comparator.compare(serverAddress, clientAddress) > 0) {
84 final Closeable n = AbstractPCEPSessionNegotiatorFactory.this.sessions.remove(clientAddress);
87 } catch (final IOException e) {
88 logger.warn("Unexpected failure to close old session", e);
91 negotiationFailed(new RuntimeException("A conflicting session for address "
92 + ((InetSocketAddress) this.channel.remoteAddress()).getAddress() + " found."));
97 final short sessionId = nextSession(clientAddress);
98 final AbstractPCEPSessionNegotiator n = createNegotiator(promise, factory.getSessionListener(), this.channel, sessionId);
100 AbstractPCEPSessionNegotiatorFactory.this.sessions.put(clientAddress, new Closeable() {
102 public void close() {
107 this.channel.closeFuture().addListener(new ChannelFutureListener() {
109 public void operationComplete(final ChannelFuture future) throws Exception {
110 synchronized (lock) {
111 AbstractPCEPSessionNegotiatorFactory.this.sessions.inverse().remove(this);
116 logger.debug("Replacing bootstrap negotiator for channel {}", this.channel);
117 this.channel.pipeline().replace(this, "negotiator", n);
118 n.startNegotiation();
123 protected void handleMessage(final Message msg) throws Exception {
124 throw new IllegalStateException("Bootstrap negotiator should have been replaced");
130 private short nextSession(final byte[] clientAddress) {
132 * FIXME: Improve the allocation algorithm to make sure:
133 * - no duplicate IDs are assigned
134 * - we retain former session IDs for a reasonable time
136 Short next = this.sessionIds.get(clientAddress);
141 this.sessionIds.put(clientAddress, (short) ((next + 1) % 255));