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