Merge "Bug 1362: New AsyncWriteTransaction#submit method"
[controller.git] / opendaylight / netconf / netconf-ssh / src / main / java / org / opendaylight / controller / netconf / ssh / NetconfSSHServer.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.controller.netconf.ssh;
9
10 import com.google.common.annotations.VisibleForTesting;
11 import io.netty.channel.EventLoopGroup;
12 import io.netty.channel.local.LocalAddress;
13 import java.io.IOException;
14 import java.net.InetSocketAddress;
15 import java.net.ServerSocket;
16 import java.net.Socket;
17 import java.util.concurrent.ExecutorService;
18 import java.util.concurrent.Executors;
19 import java.util.concurrent.atomic.AtomicLong;
20 import javax.annotation.concurrent.ThreadSafe;
21 import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
22 import org.opendaylight.controller.netconf.ssh.threads.Handshaker;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 /**
27  * Thread that accepts client connections. Accepted socket is forwarded to {@link org.opendaylight.controller.netconf.ssh.threads.Handshaker},
28  * which is executed in {@link #handshakeExecutor}.
29  */
30 @ThreadSafe
31 public final class NetconfSSHServer extends Thread implements AutoCloseable {
32
33     private static final Logger logger = LoggerFactory.getLogger(NetconfSSHServer.class);
34     private static final AtomicLong sessionIdCounter = new AtomicLong();
35
36     private final ServerSocket serverSocket;
37     private final LocalAddress localAddress;
38     private final EventLoopGroup bossGroup;
39     private final AuthProvider authProvider;
40     private final ExecutorService handshakeExecutor;
41     private volatile boolean up;
42
43     private NetconfSSHServer(int serverPort, LocalAddress localAddress, AuthProvider authProvider, EventLoopGroup bossGroup) throws IOException {
44         super(NetconfSSHServer.class.getSimpleName());
45         this.bossGroup = bossGroup;
46         logger.trace("Creating SSH server socket on port {}", serverPort);
47         this.serverSocket = new ServerSocket(serverPort);
48         if (serverSocket.isBound() == false) {
49             throw new IllegalStateException("Socket can't be bound to requested port :" + serverPort);
50         }
51         logger.trace("Server socket created.");
52         this.localAddress = localAddress;
53         this.authProvider = authProvider;
54         this.up = true;
55         handshakeExecutor = Executors.newFixedThreadPool(10);
56     }
57
58     public static NetconfSSHServer start(int serverPort, LocalAddress localAddress, AuthProvider authProvider, EventLoopGroup bossGroup) throws IOException {
59         NetconfSSHServer netconfSSHServer = new NetconfSSHServer(serverPort, localAddress, authProvider, bossGroup);
60         netconfSSHServer.start();
61         return netconfSSHServer;
62     }
63
64     @Override
65     public void close() throws IOException {
66         up = false;
67         logger.trace("Closing SSH server socket.");
68         serverSocket.close();
69         bossGroup.shutdownGracefully();
70         logger.trace("SSH server socket closed.");
71     }
72
73     @VisibleForTesting
74     public InetSocketAddress getLocalSocketAddress() {
75         return (InetSocketAddress) serverSocket.getLocalSocketAddress();
76     }
77
78     @Override
79     public void run() {
80         while (up) {
81             Socket acceptedSocket = null;
82             try {
83                 acceptedSocket = serverSocket.accept();
84             } catch (IOException e) {
85                 if (up == false) {
86                     logger.trace("Exiting server thread", e);
87                 } else {
88                     logger.warn("Exception occurred during socket.accept", e);
89                 }
90             }
91             if (acceptedSocket != null) {
92                 try {
93                     Handshaker task = new Handshaker(acceptedSocket, localAddress, sessionIdCounter.incrementAndGet(), authProvider, bossGroup);
94                     handshakeExecutor.submit(task);
95                 } catch (IOException e) {
96                     logger.warn("Cannot set PEMHostKey, closing connection", e);
97                     try {
98                         acceptedSocket.close();
99                     } catch (IOException e1) {
100                         logger.warn("Ignoring exception while closing socket", e);
101                     }
102                 }
103             }
104         }
105         logger.debug("Server thread is exiting");
106     }
107 }