aboutsummaryrefslogtreecommitdiffstats
path: root/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/context/HostAddressCache.java
blob: 6e63728a076fffb26d704beda156008ee35c419f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
 * Copyright © 2016-2018 European Support Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openecomp.sdc.logging.context;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Optional;
import java.util.function.Supplier;

/**
 * Holds a reference to local host address as returned by Java runtime. A value of host address will be cached for the
 * interval specified in the constructor or {@link #DEFAULT_REFRESH_INTERVAL}. The caching helps to avoid many low-level
 * calls, but at the same time pick up any IP or FQDN changes. Although the underlying JDK implementation uses caching
 * too, the refresh interval for logging may be much longer due to the nature of the use.
 *
 * @author evitaliy
 * @since 26 Mar 2018
 */
@SuppressWarnings({"UseOfSystemOutOrSystemErr", "CallToPrintStackTrace", "squid:S106", "squid:S1148", "squid:S1166"})
public class HostAddressCache {

    private static final long DEFAULT_REFRESH_INTERVAL = 60000L; // 1 min

    private final long interval;

    private volatile CacheEntry cachedAddress;

    private final Supplier<InetAddress> readAddress;

    public HostAddressCache() {
        this(DEFAULT_REFRESH_INTERVAL);
    }

    /**
     * Creates a cache for host address with a custom refresh interval.
     */
    public HostAddressCache(long refreshInterval) {
        this.interval = refreshInterval;
        this.readAddress = HostAddressCache::read;
        this.cachedAddress = new CacheEntry(System.currentTimeMillis(), readAddress.get());
    }

    /**
     * Package level constructor used for unit test in order to avoid static mock
     *
     * @param readAddress
     * @param refreshInterval
     */
    HostAddressCache(Supplier<InetAddress> readAddress, long refreshInterval) {
        this.interval = refreshInterval;
        this.readAddress = readAddress;
        this.cachedAddress = new CacheEntry(System.currentTimeMillis(), this.readAddress.get());
    }

    /**
     * Returns an address (host name and IP address) of the local system.
     *
     * @return local host address or <code>null</code> if it could not be read for some reason
     */
    public synchronized Optional<InetAddress> get() {

        long current = System.currentTimeMillis();
        if (current - cachedAddress.lastUpdated < interval) {
            return Optional.ofNullable(cachedAddress.address);
        }

        InetAddress address = readAddress.get(); // register the attempt even if null, i.e. failed to get a meaningful address
        cachedAddress = new CacheEntry(current, address);
        return Optional.ofNullable(address);
    }

    private static InetAddress read() {

        try {
            return InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            System.err.println(
                    "[WARNING] Failed to get local host address. Using a fallback. If you are on Linux, make sure "
                            + "/etc/hosts contains the host name of your machine, "
                            + "e.g. '127.0.0.1 localhost my-host.example.com'.");

            e.printStackTrace(); // can't really use logging
            return getFallbackLocalHost();
        }
    }

    private static InetAddress getFallbackLocalHost() {

        try {

            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();

            while (networkInterfaces.hasMoreElements()) {

                InetAddress address = getAddress(networkInterfaces.nextElement());
                if (address != null) {
                    return address;
                }
            }

            return null;

        } catch (SocketException e) {
            e.printStackTrace(); // can't really use logging
            return null;
        }
    }

    private static InetAddress getAddress(NetworkInterface networkInterface) throws SocketException {

        if (networkInterface.isLoopback() || networkInterface.isUp()) {
            return null;
        }

        Enumeration<InetAddress> interfaceAddresses = networkInterface.getInetAddresses();
        while (interfaceAddresses.hasMoreElements()) {

            InetAddress address = interfaceAddresses.nextElement();
            if (isHostAddress(address)) {
                return address;
            }
        }

        return null;
    }

    private static boolean isHostAddress(InetAddress address) {
        return !address.isLoopbackAddress() && !address.isAnyLocalAddress() && !address.isLinkLocalAddress()
                       && !address.isMulticastAddress();
    }

    private static class CacheEntry {

        private final long lastUpdated;
        private final InetAddress address;

        private CacheEntry(long lastUpdated, InetAddress address) {
            this.lastUpdated = lastUpdated;
            this.address = address;
        }
    }
}