Initial framework migration to netty.
[bgpcep.git] / framework / src / main / java / org / opendaylight / protocol / framework / ProtocolServer.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.framework;
9
10 import io.netty.channel.Channel;
11
12 import java.io.IOException;
13 import java.net.Inet4Address;
14 import java.net.Inet6Address;
15 import java.net.InetAddress;
16 import java.net.InetSocketAddress;
17 import java.util.HashMap;
18 import java.util.Map;
19 import java.util.Timer;
20
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 import com.google.common.collect.BiMap;
25 import com.google.common.collect.HashBiMap;
26
27 /**
28  * Representation of a server, created by {@link Dispatcher}. Should be extended by a protocol specific server
29  * implementation.
30  */
31 public class ProtocolServer implements SessionParent {
32
33         private static final Logger logger = LoggerFactory.getLogger(ProtocolServer.class);
34
35         private static final int SESSIONS_LIMIT = 255;
36
37         private final InetSocketAddress serverAddress;
38
39         private final ProtocolConnectionFactory connectionFactory;
40         private final ProtocolSessionFactory sessionFactory;
41
42         /**
43          * Maps clients of this server to their address. The client is represented as PCEP session. Used BiMap for
44          * implementation to allow easy manipulation with both InetSocketAddress and PCEPSessionImpl representing a key.
45          */
46         private final BiMap<InetSocketAddress, ProtocolSession> sessions;
47
48         private final Map<InetSocketAddress, Integer> sessionIds;
49
50         /**
51          * Creates a Protocol server.
52          * 
53          * @param dispatcher Dispatcher
54          * @param address address to which this server is bound
55          * @param connectionFactory factory for connection specific properties
56          * @param channel server socket channel
57          * @param sessionFactory factory for sessions
58          * @param inputStreamFactory factory for input streams
59          */
60         public ProtocolServer(final InetSocketAddress address, final ProtocolConnectionFactory connectionFactory,
61                         final ProtocolSessionFactory sessionFactory) {
62                 this.serverAddress = address;
63                 this.sessions = HashBiMap.create();
64                 this.connectionFactory = connectionFactory;
65                 this.sessionFactory = sessionFactory;
66                 this.sessionIds = new HashMap<InetSocketAddress, Integer>();
67         }
68
69         /**
70          * Creates a session. This method is called after the server accepts incoming client connection. A session is
71          * created for each client. If a session for a client (represented by the address) was already created, return this,
72          * else create a new one.
73          * 
74          * @param clientAddress IP address of the client
75          * @param timer Timer common for all sessions
76          * @return new or existing PCEPSession
77          * @see <a href="http://tools.ietf.org/html/rfc5440#appendix-A">RFC</a>
78          */
79         public ProtocolSession createSession(final Timer timer, final Channel channel) {
80                 ProtocolSession session = null;
81                 final InetSocketAddress clientAddress = (InetSocketAddress) channel.remoteAddress();
82                 if (this.sessions.containsKey(clientAddress)) { // when the session is created, the key is the InetSocketAddress
83                         session = this.sessions.get(clientAddress);
84                         if (compareTo(this.serverAddress.getAddress(), clientAddress.getAddress()) > 0) {
85                                 try {
86                                         session.close();
87                                 } catch (final IOException e) {
88                                         logger.error("Session {} could not be closed.", session);
89                                 }
90                         }
91                 } else {
92                         final int sessionId = getNextId(this.sessionIds.get(clientAddress), SESSIONS_LIMIT - 1);
93                         session = this.sessionFactory.getProtocolSession(this, timer, this.connectionFactory.createProtocolConnection(clientAddress),
94                                         sessionId, channel.pipeline().context(ProtocolSessionOutboundHandler.class));
95                         this.sessionIds.put(clientAddress, sessionId);
96                 }
97                 this.sessions.put(clientAddress, session);
98                 return session;
99         }
100
101         /**
102          * Returns server address.
103          * 
104          * @return server address
105          */
106         public InetSocketAddress getAddress() {
107                 return this.serverAddress;
108         }
109
110         @Override
111         public synchronized void close() throws IOException {
112                 // TODO:
113                 logger.debug("Server {} closed.", this);
114         }
115
116         @Override
117         public synchronized void onSessionClosed(final ProtocolSession session) {
118                 this.sessions.inverse().remove(session); // when the session is closed, the key is the instance of the session
119         }
120
121         private static int getNextId(Integer lastId, final int maxId) {
122                 return (lastId == null || maxId == lastId) ? 0 : ++lastId;
123         }
124
125         /**
126          * Compares byte array representations of two InetAddresses.
127          * 
128          * @param addrOne
129          * @param addrTwo
130          * @throws IllegalArgumentException if InetAddresses don't belong to the same subclass of InetAddress.
131          * @return 1 if addrOne is greater than addrTwo, 0 if they are the same, -1 if addrOne is lower than addrTwo
132          */
133         private static int compareTo(final InetAddress addrOne, final InetAddress addrTwo) {
134                 if ((addrOne instanceof Inet4Address && addrOne instanceof Inet6Address)
135                                 || (addrOne instanceof Inet6Address && addrOne instanceof Inet4Address)) {
136                         throw new IllegalArgumentException("Cannot compare InetAddresses. They both have to be the same subclass of InetAddress.");
137                 }
138                 final byte[] byteOne = addrOne.getAddress();
139                 final byte[] byteTwo = addrTwo.getAddress();
140                 for (int i = 0; i < byteOne.length; i++) {
141                         if (byteOne[i] > byteTwo[i])
142                                 return 1;
143                         else if (byteOne[i] < byteTwo[i])
144                                 return -1;
145                 }
146                 return 0;
147         }
148
149         @Override
150         public String toString() {
151                 return "ProtocolServer [serverAddress=" + this.serverAddress + ", hashCode()=" + hashCode() + "]";
152         }
153 }