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