6e63728a076fffb26d704beda156008ee35c419f
[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 import java.util.function.Supplier;
26
27 /**
28  * Holds a reference to local host address as returned by Java runtime. A value of host address will be cached for the
29  * interval specified in the constructor or {@link #DEFAULT_REFRESH_INTERVAL}. The caching helps to avoid many low-level
30  * calls, but at the same time pick up any IP or FQDN changes. Although the underlying JDK implementation uses caching
31  * too, the refresh interval for logging may be much longer due to the nature of the use.
32  *
33  * @author evitaliy
34  * @since 26 Mar 2018
35  */
36 @SuppressWarnings({"UseOfSystemOutOrSystemErr", "CallToPrintStackTrace", "squid:S106", "squid:S1148", "squid:S1166"})
37 public class HostAddressCache {
38
39     private static final long DEFAULT_REFRESH_INTERVAL = 60000L; // 1 min
40
41     private final long interval;
42
43     private volatile CacheEntry cachedAddress;
44
45     private final Supplier<InetAddress> readAddress;
46
47     public HostAddressCache() {
48         this(DEFAULT_REFRESH_INTERVAL);
49     }
50
51     /**
52      * Creates a cache for host address with a custom refresh interval.
53      */
54     public HostAddressCache(long refreshInterval) {
55         this.interval = refreshInterval;
56         this.readAddress = HostAddressCache::read;
57         this.cachedAddress = new CacheEntry(System.currentTimeMillis(), readAddress.get());
58     }
59
60     /**
61      * Package level constructor used for unit test in order to avoid static mock
62      *
63      * @param readAddress
64      * @param refreshInterval
65      */
66     HostAddressCache(Supplier<InetAddress> readAddress, long refreshInterval) {
67         this.interval = refreshInterval;
68         this.readAddress = readAddress;
69         this.cachedAddress = new CacheEntry(System.currentTimeMillis(), this.readAddress.get());
70     }
71
72     /**
73      * Returns an address (host name and IP address) of the local system.
74      *
75      * @return local host address or <code>null</code> if it could not be read for some reason
76      */
77     public synchronized Optional<InetAddress> get() {
78
79         long current = System.currentTimeMillis();
80         if (current - cachedAddress.lastUpdated < interval) {
81             return Optional.ofNullable(cachedAddress.address);
82         }
83
84         InetAddress address = readAddress.get(); // register the attempt even if null, i.e. failed to get a meaningful address
85         cachedAddress = new CacheEntry(current, address);
86         return Optional.ofNullable(address);
87     }
88
89     private static InetAddress read() {
90
91         try {
92             return InetAddress.getLocalHost();
93         } catch (UnknownHostException e) {
94             System.err.println(
95                     "[WARNING] Failed to get local host address. Using a fallback. If you are on Linux, make sure "
96                             + "/etc/hosts contains the host name of your machine, "
97                             + "e.g. '127.0.0.1 localhost my-host.example.com'.");
98
99             e.printStackTrace(); // can't really use logging
100             return getFallbackLocalHost();
101         }
102     }
103
104     private static InetAddress getFallbackLocalHost() {
105
106         try {
107
108             Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
109
110             while (networkInterfaces.hasMoreElements()) {
111
112                 InetAddress address = getAddress(networkInterfaces.nextElement());
113                 if (address != null) {
114                     return address;
115                 }
116             }
117
118             return null;
119
120         } catch (SocketException e) {
121             e.printStackTrace(); // can't really use logging
122             return null;
123         }
124     }
125
126     private static InetAddress getAddress(NetworkInterface networkInterface) throws SocketException {
127
128         if (networkInterface.isLoopback() || networkInterface.isUp()) {
129             return null;
130         }
131
132         Enumeration<InetAddress> interfaceAddresses = networkInterface.getInetAddresses();
133         while (interfaceAddresses.hasMoreElements()) {
134
135             InetAddress address = interfaceAddresses.nextElement();
136             if (isHostAddress(address)) {
137                 return address;
138             }
139         }
140
141         return null;
142     }
143
144     private static boolean isHostAddress(InetAddress address) {
145         return !address.isLoopbackAddress() && !address.isAnyLocalAddress() && !address.isLinkLocalAddress()
146                        && !address.isMulticastAddress();
147     }
148
149     private static class CacheEntry {
150
151         private final long lastUpdated;
152         private final InetAddress address;
153
154         private CacheEntry(long lastUpdated, InetAddress address) {
155             this.lastUpdated = lastUpdated;
156             this.address = address;
157         }
158     }
159 }