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.framework;
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;
19 import java.util.Map.Entry;
20 import java.util.Timer;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
25 import com.google.common.collect.BiMap;
26 import com.google.common.collect.HashBiMap;
29 * Representation of a server, created by {@link Dispatcher}. Should be extended by a protocol specific server
32 public class ProtocolServer implements SessionParent {
34 private static final Logger logger = LoggerFactory.getLogger(ProtocolServer.class);
36 private static final int SESSIONS_LIMIT = 255;
38 private final InetSocketAddress serverAddress;
40 private final ServerSocketChannel channel;
42 private final ProtocolConnectionFactory connectionFactory;
43 private final ProtocolSessionFactory sessionFactory;
44 private final ProtocolInputStreamFactory inputStreamFactory;
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.
50 private final BiMap<InetSocketAddress, ProtocolSession> sessions;
52 private final Map<InetSocketAddress, Integer> sessionIds;
54 private final DispatcherImpl dispatcher;
57 * Creates a Protocol server.
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
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>();
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.
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>
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) {
96 } catch (final IOException e) {
97 logger.error("Session {} could not be closed.", session);
101 final int sessionId = getNextId(this.sessionIds.get(clientAddress), SESSIONS_LIMIT - 1);
102 session = this.sessionFactory.getProtocolSession(this, timer, this.connectionFactory.createProtocolConnection(clientAddress),
104 this.sessionIds.put(clientAddress, sessionId);
106 this.sessions.put(clientAddress, session);
110 ProtocolInputStream createInputStream(final PipedInputStream pis, final ProtocolMessageFactory pmf) {
111 return this.inputStreamFactory.getProtocolInputStream(pis, pmf);
115 * Returns server address.
117 * @return server address
119 public InetSocketAddress getAddress() {
120 return this.serverAddress;
124 public synchronized void close() throws IOException {
125 for (final Entry<InetSocketAddress, ProtocolSession> s : this.sessions.entrySet()) {
126 s.getValue().close();
128 this.sessions.clear();
129 this.dispatcher.removeServer(this);
130 this.channel.close();
131 logger.debug("Server {} closed.", this);
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);
141 public void checkOutputBuffer(final ProtocolSession session) {
142 this.dispatcher.checkOutputBuffer(session);
145 private static int getNextId(Integer lastId, final int maxId) {
146 return (lastId == null || maxId == lastId) ? 0 : ++lastId;
150 * Compares byte array representations of two InetAddresses.
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
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.");
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])
167 else if (byteOne[i] < byteTwo[i])