9e5479d934aee536a3b3bec3a259be321b539552
[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 synchronized 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       availablePorts.poll();\r
93       return reserveServerPort(); // retry if current port is not available\r
94     }\r
95 \r
96     Semaphore semaphore = semaphoreMap.get(port);\r
97     boolean isAcquired = semaphore.tryAcquire();\r
98     if (isAcquired) {\r
99       LOG.debug("Failed to acquire a lock for port :{}. Hence retrying...", port);\r
100       return reserveServerPort();\r
101     }\r
102 \r
103     availablePorts.poll();\r
104     semaphore.release();\r
105     LOG.debug("Rserved port is {}", port);\r
106     return port;\r
107   }\r
108 \r
109   public boolean unReserveServerPort(String port) {\r
110 \r
111     try {\r
112       Semaphore semaphore = semaphoreMap.get(port);\r
113       semaphore.acquire();\r
114       availablePorts.add(port);\r
115       semaphore.release();\r
116       LOG.error("Successfully un-reserved the port " + port + " to start netconf server.");\r
117     } catch (InterruptedException e) {\r
118       LOG.warn("Failed to un-reserve the port " + port, e);\r
119       Thread.currentThread().interrupt();\r
120       return false;\r
121     }\r
122 \r
123     return true;\r
124   }\r
125 \r
126   public boolean checkAndReserveServerPort(String port) {\r
127 \r
128     try {\r
129       Semaphore semaphore = semaphoreMap.get(port);\r
130       semaphore.acquire();\r
131       if (isServerPortInUse(port)) {\r
132         LOG.error("Port {}  already in use.", port);\r
133         semaphore.release();\r
134         return false;\r
135       }\r
136       availablePorts.remove(port);\r
137       semaphore.release();\r
138       LOG.error("Successfully reserved the port {} to start netconf server", port);\r
139     } catch (InterruptedException e) {\r
140       Thread.currentThread().interrupt();\r
141       LOG.error("Failed to lock the port {} : Exception :{}", port, e.toString());\r
142       return checkAndReserveServerPort(port); // retry acquiring the lock.\r
143     }\r
144 \r
145     return true;\r
146   }\r
147 \r
148   public boolean isServerPortInUse(String port) {\r
149     return checkIfPortAvailable(port);\r
150   }\r
151 \r
152   private static boolean checkIfPortAvailable(String portStr) {\r
153     Integer port = Integer.parseInt(portStr);\r
154     try (Socket ignored = new Socket("localhost", port)) {\r
155       return true;\r
156     } catch (IOException e) {\r
157       LOG.error("while checkIfPortAvailable {}", e.toString());\r
158       return false;\r
159     }\r
160   }\r
161 }\r