Update to Worldwind release 0.4.1
[worldwind-tracker.git] / gov / nasa / worldwind / util / BasicNetworkStatus.java
blob7374490dd39d860e8cbcc98b695c51d1a6ecd117
1 /*
2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
5 All Rights Reserved.
6 */
7 package gov.nasa.worldwind.util;
9 import gov.nasa.worldwind.avlist.*;
10 import gov.nasa.worldwind.Configuration;
12 import java.net.*;
13 import java.util.concurrent.ConcurrentHashMap;
14 import java.util.concurrent.atomic.*;
15 import java.io.IOException;
17 /**
18 * Provides tracking of per-host network availability. Host computers that fail network requests can be logged to this
19 * class' tracking list. When a host has been logged a specified number of times, it is marked as unreachable. Users can
20 * query instances of this class to determine whether a host has been marked as unreachable.
21 * <p/>
22 * Users are expected to invoke this class' {@link #logUnavailableHost(java.net.URL)} method when an attempt to contact
23 * a host fails. Each invocation increments the failure count by one. When the count exceeds the attempt limit, the host
24 * is marked as unreachable. When attempts to contact the host <em>are</em> successful, users should invoke this class'
25 * {@link #logAvailableHost(java.net.URL)} method to clear its status.
26 * <p/>
27 * A host may become reachable at a time subsequent to its being logged. To detect this, this class will mark a host as
28 * not unreachable after a specifiable interval of time. If the host is once more logged as unavailable, its entry will
29 * return to the unavailable state. This cycle continues indefinitely.
30 * <p/>
31 * Methods are also provided to determine whether the public network can be reached and whether the NASA World Wind
32 * servers cab be reached.
34 * @author tag
35 * @version $Id: BasicNetworkStatus.java 3735 2007-12-06 02:20:43Z tgaskins $
37 public class BasicNetworkStatus extends AVListImpl implements NetworkStatus
39 private static final long DEFAULT_TRY_AGAIN_INTERVAL = (long) 12e3; // seconds
40 private static final int DEFAULT_ATTEMPT_LIMIT = 10; // number of unavailable events to declare host unavailable
41 private static final String[] networkTestSites = new String[]
42 {"www.nasa.gov", "worldwind.arc.nasa.gov", "google.com", "microsoft.com", "yahoo.com"};
43 private static final long NETWORK_STATUS_REPORT_INTERVAL = (long) 60e3;
45 private static class HostInfo
47 private final long tryAgainInterval;
48 private final int attemptLimit;
49 private AtomicInteger logCount = new AtomicInteger();
50 private AtomicLong lastLogTime = new AtomicLong();
52 private HostInfo(int attemptLimit, long tryAgainInterval)
54 this.lastLogTime.set(System.currentTimeMillis());
55 this.logCount.set(1);
56 this.tryAgainInterval = tryAgainInterval;
57 this.attemptLimit = attemptLimit;
60 private boolean isUnavailable()
62 return this.logCount.get() >= this.attemptLimit;
65 private boolean isTimeToTryAgain()
67 return System.currentTimeMillis() - this.lastLogTime.get() >= this.tryAgainInterval;
71 private ConcurrentHashMap<String, HostInfo> hostMap = new ConcurrentHashMap<String, HostInfo>();
73 private AtomicLong tryAgainInterval = new AtomicLong(DEFAULT_TRY_AGAIN_INTERVAL);
74 private AtomicInteger attemptLimit = new AtomicInteger(DEFAULT_ATTEMPT_LIMIT);
76 // Fields for determining overall network status.
77 private boolean offlineMode;
78 private AtomicLong lastUnavailableLogTime = new AtomicLong(System.currentTimeMillis());
79 private AtomicLong lastAvailableLogTime = new AtomicLong(System.currentTimeMillis() + 1);
80 private AtomicLong lastNetworkCheckTime = new AtomicLong(System.currentTimeMillis());
81 private AtomicLong lastNetworkStatusReportTime = new AtomicLong(0);
82 private AtomicBoolean lastNetworkUnavailableResult = new AtomicBoolean(false);
84 public BasicNetworkStatus()
86 String oms = Configuration.getStringValue(AVKey.OFFLINE_MODE, "false");
87 this.offlineMode = oms.startsWith("t") || oms.startsWith("T");
90 /**
91 * Indicates whether World Wind will attempt to connect to the network to retrieve data or for other reasons.
93 * @return <code>true</code> if World Wind is in off-line mode, <code>false</code> if not.
95 public boolean isOfflineMode()
97 return offlineMode;
101 * Indicate whether World Wind should attempt to connect to the network to retrieve data or for other reasons.
102 * The default value for this attribute is <code>false</code>, indicating that the network should be used.
104 * @param offlineMode <code>true</code> if World Wind should use the network, <code>false</code> otherwise
106 public void setOfflineMode(boolean offlineMode)
108 this.offlineMode = offlineMode;
112 * Set the number of times a host must be logged as unavailable before it is marked unavailable in this class.
114 * @param limit the number of log-unavailability invocations necessary to consider the host unreachable.
115 * @throws IllegalArgumentException if the limit is less than 1.
117 public void setAttemptLimit(int limit)
119 if (limit < 1)
121 String message = Logging.getMessage("NetworkStatus.InvalidAttemptLimit");
122 Logging.logger().severe(message);
123 throw new IllegalArgumentException(message);
126 this.attemptLimit.set(limit);
130 * Set the length of time to wait until a host is marked as not unreachable subsequent to its being marked
131 * unreachable.
133 * @param interval The length of time, in milliseconds, to wait to unmark a host as unreachable.
134 * @throws IllegalArgumentException if the interval is less than 0.
136 public void setTryAgainInterval(long interval)
138 if (interval < 0)
140 String message = Logging.getMessage("NetworkStatus.InvalidTryAgainInterval");
141 Logging.logger().severe(message);
142 throw new IllegalArgumentException(message);
145 this.tryAgainInterval.set(interval);
149 * Returns the number of times a host must be logged as unavailable before it is marked unavailable in this class.
151 * @return the limit.
153 public int getAttemptLimit()
155 return this.attemptLimit.get();
159 * Returns the length of time to wait until a host is marked as not unreachable subsequent to its being marked
160 * unreachable.
162 * @return the interval, in milliseconds.
164 public long getTryAgainInterval()
166 return this.tryAgainInterval.get();
170 * Log a host as unavailable. Each invocation increments the host's attempt count. When the count equals or exceeds
171 * the attempt limit, the host is marked as unavailable.
173 * @param url a url containing the host to mark as unavailable.
175 public void logUnavailableHost(URL url)
177 if (this.offlineMode)
178 return;
180 if (url == null)
182 String message = Logging.getMessage("nullValue.URLIsNull");
183 Logging.logger().severe(message);
184 throw new IllegalArgumentException(message);
187 String hostName = url.getHost();
188 if (this.hostMap.containsKey(hostName))
190 HostInfo hi = this.hostMap.get(hostName);
191 if (!hi.isUnavailable())
192 hi.logCount.incrementAndGet();
193 hi.lastLogTime.set(System.currentTimeMillis());
195 else
197 HostInfo hi = new HostInfo(this.attemptLimit.get(), this.tryAgainInterval.get());
198 this.hostMap.put(hostName, hi);
201 this.lastUnavailableLogTime.set(System.currentTimeMillis());
205 * Log a host as available. Each invocation causes the host to no longer be marked as unavailable. Its
206 * unavailability count is effectively set to 0.
208 * @param url a url containing the host to mark as available.
210 public void logAvailableHost(URL url)
212 if (this.offlineMode)
213 return;
215 if (url == null)
217 String message = Logging.getMessage("nullValue.URLIsNull");
218 Logging.logger().severe(message);
219 throw new IllegalArgumentException(message);
222 String hostName = url.getHost();
223 if (this.hostMap.containsKey(hostName))
224 this.hostMap.remove(hostName);
226 this.lastAvailableLogTime.set(System.currentTimeMillis());
230 * Indicates whether the host has been marked as unavailable. To be marked unavailable a host's attempt count must
231 * exceed the specified attempt limit.
233 * @param url a url containing the host to check for availability.
234 * @return true if the host is marked as unavailable, otherwise false.
236 public boolean isHostUnavailable(URL url)
238 if (this.offlineMode)
239 return true;
241 if (url == null)
243 String message = Logging.getMessage("nullValue.URLIsNull");
244 Logging.logger().severe(message);
245 throw new IllegalArgumentException(message);
248 String hostName = url.getHost();
249 if (!this.hostMap.containsKey(hostName))
250 return false;
252 HostInfo hi = this.hostMap.get(hostName);
253 if (hi.isTimeToTryAgain())
255 this.removeKey(hostName);
256 return false;
259 return hi.isUnavailable();
263 * Indicates whether a public network can be reached or has been reached in the previous five seconds.
265 * @return false if the network can be reached or has been reached in the previous five seconds, otherwise true.
267 public boolean isNetworkUnavailable()
269 return this.offlineMode || this.isNetworkUnavailable(5000L);
273 * Indicates whether a public network can be reached or has been reached in a specified previous amount of time.
275 * @param checkInterval the number of milliseconds in the past used to determine whether the server was avaialble
276 * recently.
277 * @return false if the network can be reached or has been reached in a specified time, otherwise true.
279 public boolean isNetworkUnavailable(long checkInterval)
281 if (this.offlineMode)
282 return true;
284 // If there's been success since failure, network assumed to be reachable.
285 if (this.lastAvailableLogTime.get() > this.lastUnavailableLogTime.get())
287 this.lastNetworkUnavailableResult.set(false);
288 return this.lastNetworkUnavailableResult.get();
291 long now = System.currentTimeMillis();
293 // If there's been success recently, network assumed to be reachable.
294 if (!this.lastNetworkUnavailableResult.get() && now - this.lastAvailableLogTime.get() < checkInterval)
296 return this.lastNetworkUnavailableResult.get();
299 // If query comes too soon after an earlier one that addressed the network, return the earlier result.
300 if (now - this.lastNetworkCheckTime.get() < checkInterval)
302 return this.lastNetworkUnavailableResult.get();
305 this.lastNetworkCheckTime.set(now);
307 if (!this.isWorlWindServerUnavailable())
309 this.lastNetworkUnavailableResult.set(false); // network not unreachable
310 return this.lastNetworkUnavailableResult.get();
313 for (String testHost : networkTestSites)
315 if (this.isHostReachable(testHost))
318 this.lastNetworkUnavailableResult.set(false); // network not unreachable
319 return this.lastNetworkUnavailableResult.get();
324 if (now - this.lastNetworkStatusReportTime.get() > NETWORK_STATUS_REPORT_INTERVAL)
326 this.lastNetworkStatusReportTime.set(now);
327 String message = Logging.getMessage("NetworkStatus.NetworkUnreachable");
328 Logging.logger().info(message);
331 this.lastNetworkUnavailableResult.set(true); // if no successful contact then network is unreachable
332 return this.lastNetworkUnavailableResult.get();
336 * Indicates whether the NASA World Wind servers can be reached.
338 * @return false if the servers can be reached, otherwise true.
340 public boolean isWorlWindServerUnavailable()
342 return this.offlineMode || !this.isHostReachable("worldwind.arc.nasa.gov");
345 private boolean isHostReachable(String hostName)
349 // Assume host is unreachable if we can't get its dns entry without getting an exception
350 //noinspection ResultOfMethodCallIgnored
351 InetAddress.getByName(hostName);
353 catch (UnknownHostException e)
355 String message = Logging.getMessage("NetworkStatus.UnreachableTestHost", hostName);
356 Logging.logger().fine(message);
357 return false;
359 catch (Exception e)
361 String message = Logging.getMessage("NetworkStatus.ExceptionTestingHost", hostName);
362 Logging.logger().info(message);
363 return false;
366 // Was able to get internet address, but host still might not be reachable because the address might have been
367 // cached earlier when it was available. So need to try something else.
369 URLConnection connection = null;
372 URL url = new URL("http://" + hostName);
373 Proxy proxy = WWIO.configureProxy();
374 if (proxy != null)
375 connection = url.openConnection(proxy);
376 else
377 connection = url.openConnection();
379 connection.setConnectTimeout(2000);
380 String ct = connection.getContentType();
381 if (ct != null)
382 return true;
384 catch (IOException e)
386 String message = Logging.getMessage("NetworkStatus.ExceptionTestingHost", hostName);
387 Logging.logger().info(message);
389 finally
391 if (connection != null && connection instanceof HttpURLConnection)
392 ((HttpURLConnection) connection).disconnect();
395 return false;
398 // public static void main(String[] args)
399 // {
400 // try
401 // {
402 // NetworkStatus ns = new BasicNetworkStatus();
403 // boolean tf = ns.isWorlWindServerUnavailable();
404 // tf = ns.isNetworkUnavailable();
405 // }
406 // catch (Exception e)
407 // {
408 // e.printStackTrace();
409 // }
410 // }