Log the address and port on bind errors
[netconf.git] / restconf / restconf-nb-rfc8040 / src / main / java / org / opendaylight / restconf / nb / rfc8040 / streams / websockets / WebSocketServer.java
1 /*
2  * Copyright (c) 2014, 2015 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
9 package org.opendaylight.restconf.nb.rfc8040.streams.websockets;
10
11 import com.google.common.base.Preconditions;
12 import io.netty.bootstrap.ServerBootstrap;
13 import io.netty.channel.Channel;
14 import io.netty.channel.EventLoopGroup;
15 import io.netty.channel.nio.NioEventLoopGroup;
16 import io.netty.channel.socket.nio.NioServerSocketChannel;
17 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.Notificator;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 /**
22  * {@link WebSocketServer} is the singleton responsible for starting and stopping the
23  * web socket server.
24  */
25 public final class WebSocketServer implements Runnable {
26
27     private static final Logger LOG = LoggerFactory.getLogger(WebSocketServer.class);
28
29     private static WebSocketServer instance = null;
30
31     private final int port;
32
33     private EventLoopGroup bossGroup;
34     private EventLoopGroup workerGroup;
35
36
37     private WebSocketServer(final int port) {
38         this.port = port;
39     }
40
41     /**
42      * Create singleton instance of {@link WebSocketServer}.
43      *
44      * @param port TCP port used for this server
45      * @return instance of {@link WebSocketServer}
46      */
47     public static WebSocketServer createInstance(final int port) {
48         Preconditions.checkState(instance == null, "createInstance() has already been called");
49         Preconditions.checkArgument(port >= 1024, "Privileged port (below 1024) is not allowed");
50
51         instance = new WebSocketServer(port);
52         return instance;
53     }
54
55     /**
56      * Get the websocket of TCP port.
57      *
58      * @return websocket TCP port
59      */
60     public int getPort() {
61         return port;
62     }
63
64     /**
65      * Get instance of {@link WebSocketServer} created by {@link #createInstance(int)}.
66      *
67      * @return instance of {@link WebSocketServer}
68      */
69     public static WebSocketServer getInstance() {
70         Preconditions.checkNotNull(instance, "createInstance() must be called prior to getInstance()");
71         return instance;
72     }
73
74     /**
75      * Destroy the existing instance.
76      */
77     public static void destroyInstance() {
78         Preconditions.checkState(instance != null, "createInstance() must be called prior to destroyInstance()");
79
80         instance.stop();
81         instance = null;
82     }
83
84     @Override
85     @SuppressWarnings("checkstyle:IllegalCatch")
86     public void run() {
87         bossGroup = new NioEventLoopGroup();
88         workerGroup = new NioEventLoopGroup();
89         try {
90             final ServerBootstrap serverBootstrap = new ServerBootstrap();
91             serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
92                     .childHandler(new WebSocketServerInitializer());
93
94             final Channel channel = serverBootstrap.bind(port).sync().channel();
95             LOG.info("Web socket server started at port {}.", port);
96
97             channel.closeFuture().sync();
98         } catch (final InterruptedException e) {
99             LOG.error("Web socket server encountered an error during startup attempt on port {}", port, e);
100         } catch (Throwable throwable) {
101             // sync() re-throws exceptions declared as Throwable, so the compiler doesn't see them
102             LOG.error("Error while binding to port {}", port, throwable);
103             throw throwable;
104         } finally {
105             stop();
106         }
107     }
108
109     /**
110      * Stops the web socket server and removes all listeners.
111      */
112     private void stop() {
113         LOG.debug("Stopping the web socket server instance on port {}", port);
114         Notificator.removeAllListeners();
115         if (bossGroup != null) {
116             bossGroup.shutdownGracefully();
117             bossGroup = null;
118         }
119         if (workerGroup != null) {
120             workerGroup.shutdownGracefully();
121             workerGroup = null;
122         }
123     }
124
125 }