Make sure PCEPMessageHeader is threadsafe
[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.Map;
18 import java.util.Timer;
19
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 import com.google.common.collect.BiMap;
24 import com.google.common.collect.HashBiMap;
25 import com.google.common.collect.Maps;
26
27 /**
28  * Representation of a server, created by {@link Dispatcher}. Should be extended by a protocol specific server
29  * implementation.
30  */
31 public final 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         private final Dispatcher parent;
51
52         /**
53          * Creates a Protocol server.
54          * 
55          * @param address address to which this server is bound
56          * @param connectionFactory factory for connection specific properties
57          * @param parent Dispatcher that created this server
58          * @param sessionFactory factory for sessions
59          */
60         public ProtocolServer(final InetSocketAddress address, final ProtocolConnectionFactory connectionFactory,
61                         final ProtocolSessionFactory<?> sessionFactory, final Dispatcher parent) {
62                 this.serverAddress = address;
63                 this.sessions = HashBiMap.create();
64                 this.connectionFactory = connectionFactory;
65                 this.sessionFactory = sessionFactory;
66                 this.parent = parent;
67                 this.sessionIds = Maps.newHashMap();
68         }
69
70         /**
71          * Creates a session. This method is called after the server accepts incoming client connection. A session is
72          * created for each client. If a session for a client (represented by the address) was already created, return this,
73          * else create a new one.
74          * 
75          * @param clientAddress IP address of the client
76          * @param timer Timer common for all sessions
77          * @return new or existing PCEPSession
78          * @see <a href="http://tools.ietf.org/html/rfc5440#appendix-A">RFC</a>
79          */
80         public ProtocolSession createSession(final Timer timer, final Channel channel) {
81                 ProtocolSession session = null;
82                 final InetSocketAddress clientAddress = (InetSocketAddress) channel.remoteAddress();
83                 if (this.sessions.containsKey(clientAddress)) { // when the session is created, the key is the InetSocketAddress
84                         session = this.sessions.get(clientAddress);
85                         if (compareTo(this.serverAddress.getAddress(), clientAddress.getAddress()) > 0) {
86                                 try {
87                                         session.close();
88                                 } catch (final IOException e) {
89                                         logger.error("Could not close session: {}.", session);
90                                 }
91                         }
92                 } else {
93                         final int sessionId = getNextId(this.sessionIds.get(clientAddress), SESSIONS_LIMIT - 1);
94                         session = this.sessionFactory.getProtocolSession(this, timer, this.connectionFactory.createProtocolConnection(clientAddress),
95                                         sessionId, channel);
96                         this.sessionIds.put(clientAddress, sessionId);
97                 }
98                 this.sessions.put(clientAddress, session);
99                 return session;
100         }
101
102         @Override
103         public synchronized void close() throws IOException {
104                 ((DispatcherImpl) this.parent).onServerClosed(this);
105                 logger.debug("Closed server {}.", this);
106         }
107
108         @Override
109         public synchronized void onSessionClosed(final ProtocolSession session) {
110                 this.sessions.inverse().remove(session); // when the session is closed, the key is the instance of the session
111                 logger.debug("Closed session {}.", session);
112         }
113
114         private static int getNextId(Integer lastId, final int maxId) {
115                 return lastId == null || maxId == lastId ? 0 : ++lastId;
116         }
117
118         /**
119          * Compares byte array representations of two InetAddresses.
120          * 
121          * @param addrOne
122          * @param addrTwo
123          * @throws IllegalArgumentException if InetAddresses don't belong to the same subclass of InetAddress.
124          * @return 1 if addrOne is greater than addrTwo, 0 if they are the same, -1 if addrOne is lower than addrTwo
125          */
126         private static int compareTo(final InetAddress addrOne, final InetAddress addrTwo) {
127                 if (addrOne instanceof Inet4Address && addrOne instanceof Inet6Address
128                                 || addrOne instanceof Inet6Address && addrOne instanceof Inet4Address) {
129                         throw new IllegalArgumentException("Cannot compare InetAddresses. They both have to be the same subclass of InetAddress.");
130                 }
131                 final byte[] byteOne = addrOne.getAddress();
132                 final byte[] byteTwo = addrTwo.getAddress();
133                 for (int i = 0; i < byteOne.length; i++) {
134                         if (byteOne[i] > byteTwo[i]) {
135                                 return 1;
136                         } else if (byteOne[i] < byteTwo[i]) {
137                                 return -1;
138                         }
139                 }
140                 return 0;
141         }
142
143         @Override
144         public String toString() {
145                 return "ProtocolServer [serverAddress=" + this.serverAddress + ", hashCode()=" + hashCode() + "]";
146         }
147
148         @Override
149         public int hashCode() {
150                 final int prime = 31;
151                 int result = 1;
152                 result = prime * result + (this.serverAddress == null ? 0 : this.serverAddress.hashCode());
153                 return result;
154         }
155
156         @Override
157         public boolean equals(final Object obj) {
158                 if (this == obj) {
159                         return true;
160                 }
161                 if (obj == null) {
162                         return false;
163                 }
164                 if (getClass() != obj.getClass()) {
165                         return false;
166                 }
167                 final ProtocolServer other = (ProtocolServer) obj;
168                 if (this.serverAddress == null) {
169                         if (other.serverAddress != null) {
170                                 return false;
171                         }
172                 } else if (!this.serverAddress.equals(other.serverAddress)) {
173                         return false;
174                 }
175                 return true;
176         }
177 }