0930888aacc1bdac7618861eef3484baab80f344
[sdc.git] /
1 /*
2  * Copyright © 2016-2018 European Support Limited
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.openecomp.sdc.logging.context;
18
19 import java.net.InetAddress;
20 import java.net.NetworkInterface;
21 import java.net.SocketException;
22 import java.net.UnknownHostException;
23 import java.util.Enumeration;
24 import java.util.Optional;
25
26 /**
27  * Holds a reference to local host address as returned by Java runtime. A value of host address will be cached for the
28  * interval specified in the constructor or {@link #DEFAULT_REFRESH_INTERVAL}. The caching helps to avoid many low-level
29  * calls, but at the same time pick up any IP or FQDN changes. Although the underlying JDK implementation uses caching
30  * too, the refresh interval for logging may be much longer due to the nature of the use.
31  *
32  * @author evitaliy
33  * @since 26 Mar 2018
34  */
35 @SuppressWarnings({"UseOfSystemOutOrSystemErr", "CallToPrintStackTrace", "squid:S106", "squid:S1148", "squid:S1166"})
36 public class HostAddressCache {
37
38     private static final long DEFAULT_REFRESH_INTERVAL = 60000L; // 1 min
39
40     private final long interval;
41
42     private volatile CacheEntry cachedAddress;
43
44     public HostAddressCache() {
45         this(DEFAULT_REFRESH_INTERVAL);
46     }
47
48     /**
49      * Creates a cache for host address with a custom refresh interval.
50      */
51     public HostAddressCache(long refreshInterval) {
52         this.interval = refreshInterval;
53         this.cachedAddress = new CacheEntry(System.currentTimeMillis(), read());
54     }
55
56     /**
57      * Returns an address (host name and IP address) of the local system.
58      *
59      * @return local host address or <code>null</code> if it could not be read for some reason
60      */
61     public synchronized Optional<InetAddress> get() {
62
63         long current = System.currentTimeMillis();
64         if (current - cachedAddress.lastUpdated < interval) {
65             return Optional.ofNullable(cachedAddress.address);
66         }
67
68         InetAddress address = read(); // register the attempt even if null, i.e. failed to get a meaningful address
69         cachedAddress = new CacheEntry(current, address);
70         return Optional.ofNullable(address);
71     }
72
73     private InetAddress read() {
74
75         try {
76             return InetAddress.getLocalHost();
77         } catch (UnknownHostException e) {
78             System.err.println(
79                     "[WARNING] Failed to get local host address. Using a fallback. If you are on Linux, make sure "
80                             + "/etc/hosts contains the host name of your machine, "
81                             + "e.g. '127.0.0.1 localhost my-host.example.com'.");
82
83             e.printStackTrace(); // can't really use logging
84             return getFallbackLocalHost();
85         }
86     }
87
88     private InetAddress getFallbackLocalHost() {
89
90         try {
91
92             Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
93
94             while (networkInterfaces.hasMoreElements()) {
95
96                 InetAddress address = getAddress(networkInterfaces.nextElement());
97                 if (address != null) {
98                     return address;
99                 }
100             }
101
102             return null;
103
104         } catch (SocketException e) {
105             e.printStackTrace(); // can't really use logging
106             return null;
107         }
108     }
109
110     private InetAddress getAddress(NetworkInterface networkInterface) throws SocketException {
111
112         if (networkInterface.isLoopback() || networkInterface.isUp()) {
113             return null;
114         }
115
116         Enumeration<InetAddress> interfaceAddresses = networkInterface.getInetAddresses();
117         while (interfaceAddresses.hasMoreElements()) {
118
119             InetAddress address = interfaceAddresses.nextElement();
120             if (isHostAddress(address)) {
121                 return address;
122             }
123         }
124
125         return null;
126     }
127
128     private boolean isHostAddress(InetAddress address) {
129         return !address.isLoopbackAddress() && !address.isAnyLocalAddress() && !address.isLinkLocalAddress()
130                        && !address.isMulticastAddress();
131     }
132
133     private static class CacheEntry {
134
135         private final long lastUpdated;
136         private final InetAddress address;
137
138         private CacheEntry(long lastUpdated, InetAddress address) {
139             this.lastUpdated = lastUpdated;
140             this.address = address;
141         }
142     }
143 }