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