Merge "VES Heartbeat and Software Management Feature"
[oam/tr069-adapter.git] / netconf-server / src / main / java / org / commscope / tr069adapter / netconf / server / helper / ServerPortAllocationHelper.java
1 /*\r
2  * ============LICENSE_START========================================================================\r
3  * ONAP : tr-069-adapter\r
4  * =================================================================================================\r
5  * Copyright (C) 2020 CommScope Inc Intellectual Property.\r
6  * =================================================================================================\r
7  * This tr-069-adapter software file is distributed by CommScope Inc under the Apache License,\r
8  * Version 2.0 (the "License"); you may not use this file except in compliance with the License. You\r
9  * may obtain a copy of the License at\r
10  *\r
11  * http://www.apache.org/licenses/LICENSE-2.0\r
12  *\r
13  * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\r
14  * either express or implied. See the License for the specific language governing permissions and\r
15  * limitations under the License.\r
16  * ===============LICENSE_END=======================================================================\r
17  */\r
18 \r
19 package org.commscope.tr069adapter.netconf.server.helper;\r
20 \r
21 import java.io.IOException;\r
22 import java.net.Socket;\r
23 import java.util.HashMap;\r
24 import java.util.Map;\r
25 import java.util.PriorityQueue;\r
26 import java.util.concurrent.Semaphore;\r
27 \r
28 import javax.annotation.PostConstruct;\r
29 \r
30 import org.commscope.tr069adapter.netconf.config.NetConfServerProperties;\r
31 import org.commscope.tr069adapter.netconf.error.ServerPortAllocationException;\r
32 import org.slf4j.Logger;\r
33 import org.slf4j.LoggerFactory;\r
34 import org.springframework.beans.factory.annotation.Autowired;\r
35 import org.springframework.stereotype.Component;\r
36 \r
37 @Component\r
38 public class ServerPortAllocationHelper {\r
39 \r
40   private static final Logger LOG = LoggerFactory.getLogger(ServerPortAllocationHelper.class);\r
41 \r
42   private static Map<String, Semaphore> semaphoreMap = new HashMap<>();\r
43 \r
44   private PriorityQueue<String> availablePorts = new PriorityQueue<>();\r
45 \r
46   @Autowired\r
47   NetConfServerProperties config;\r
48 \r
49   @PostConstruct\r
50   public void init() {\r
51     // read the port range and it the available ports.\r
52 \r
53     Integer startPort = config.getDefaultNetconfStartPort();\r
54     Integer maxServers = config.getDefaultMaxServers();\r
55 \r
56     try {\r
57       startPort = Integer.parseInt(config.getNetconfServersStartPort());\r
58     } catch (Exception e) {\r
59       LOG.warn(\r
60           "Failed to initialize the starting port from the environment {}. Hence using the default port range.",\r
61           config.getNetconfServersStartPort());\r
62     }\r
63 \r
64     try {\r
65       maxServers = Integer.parseInt(config.getMaxNumOfNetconfServers());\r
66     } catch (Exception e) {\r
67       LOG.warn(\r
68           "Failed to initialize the max netconf server from the environment {} Hence using the default max servers.",\r
69           config.getMaxNumOfNetconfServers());\r
70     }\r
71 \r
72     for (int i = startPort + maxServers - 1; i >= startPort; i--) {\r
73       semaphoreMap.put(String.valueOf(i), new Semaphore(1));\r
74       availablePorts.add(String.valueOf(i));\r
75     }\r
76     LOG.debug("Successfully populated available ports list.");\r
77   }\r
78 \r
79   public String reserveServerPort() throws ServerPortAllocationException {\r
80 \r
81     if (availablePorts.isEmpty()) {\r
82       LOG.debug(\r
83           "All ports are exhausted. Hence cannot allocate a port to start new netconf server.");\r
84       return null;\r
85     }\r
86 \r
87     String port = availablePorts.peek();\r
88 \r
89     LOG.debug("Trying to reserve port : {}", port);\r
90     if (isServerPortInUse(port)) {\r
91       LOG.debug("Port {} is already in use.", port);\r
92       return reserveServerPort(); // retry if current port is not available\r
93     }\r
94 \r
95     Semaphore semaphore = semaphoreMap.get(port);\r
96     boolean isAcquired = semaphore.tryAcquire();\r
97     if (isAcquired) {\r
98       LOG.debug("Failed to acquire a lock for port :{}. Hence retrying...", port);\r
99       return reserveServerPort();\r
100     }\r
101 \r
102     availablePorts.poll();\r
103     semaphore.release();\r
104     LOG.debug("Rserved port is {}", port);\r
105     return port;\r
106   }\r
107 \r
108   public boolean unReserveServerPort(String port) {\r
109 \r
110     try {\r
111       Semaphore semaphore = semaphoreMap.get(port);\r
112       semaphore.acquire();\r
113       availablePorts.add(port);\r
114       semaphore.release();\r
115       LOG.error("Successfully un-reserved the port " + port + " to start netconf server.");\r
116     } catch (InterruptedException e) {\r
117       LOG.warn("Failed to un-reserve the port " + port, e);\r
118       Thread.currentThread().interrupt();\r
119       return false;\r
120     }\r
121 \r
122     return true;\r
123   }\r
124 \r
125   public boolean checkAndReserveServerPort(String port) {\r
126 \r
127     try {\r
128       Semaphore semaphore = semaphoreMap.get(port);\r
129       semaphore.acquire();\r
130       if (isServerPortInUse(port)) {\r
131         LOG.error("Port {}  already in use.", port);\r
132         semaphore.release();\r
133         return false;\r
134       }\r
135       availablePorts.remove(port);\r
136       semaphore.release();\r
137       LOG.error("Successfully reserved the port {} to start netconf server", port);\r
138     } catch (InterruptedException e) {\r
139       Thread.currentThread().interrupt();\r
140       LOG.error("Failed to lock the port {} : Exception :{}", port, e.toString());\r
141       return checkAndReserveServerPort(port); // retry acquiring the lock.\r
142     }\r
143 \r
144     return true;\r
145   }\r
146 \r
147   public boolean isServerPortInUse(String port) {\r
148     return checkIfPortAvailable(port);\r
149   }\r
150 \r
151   private static boolean checkIfPortAvailable(String portStr) {\r
152     Integer port = Integer.parseInt(portStr);\r
153     try (Socket ignored = new Socket("localhost", port)) {\r
154       return true;\r
155     } catch (IOException e) {\r
156       LOG.error("while checkIfPortAvailable {}", e.toString());\r
157       return false;\r
158     }\r
159   }\r
160 }\r