My laptop goes where I go. Some people like to read; I like to hack code. Just ask my wife - I took the laptop on vacation to the Cayman Islands. The problem I'm having is that I can never predict the state of my online connection. At home I'm wireless; at work, wired. In between, such as my 90-minute daily train commute, I'm neither. Don't get me started on the lousy connectivity from the beach. That downtime is a serious productivity killer, since all my Web-enabled code becomes unstable. There are many layers to a J2EE application these days and I don't control all of them, so I have no guarantee that my app will behave properly if a connection fails. Worst case scenarios - I might time out, hang, or crash altogether.
The thing is I can predict my downtimes. If I take my network card out, it's a safe bet I don't have a connection. Instead of tackling the rather large problem of detecting a good network connection, I took a different approach. I started researching whether I could tell my application that I definitely did not have a connection. If I could easily write something that says "If not connected, then don't try to perform Net-sensitive code," that would take care of the majority of my downtime. After some research I discovered that java.net.NetworkInterface, new in Java 1.4, does almost exactly what I need.
Guaranteeing that a machine is "online" is near impossible (see sidebar). Luckily I don't need to solve that problem. As I mentioned, I want to say the opposite - to tell the computer that I'm not online, rather than have it try to guess. If either of my network adapters (wireless and wired) is present and active, I can assume I have connectivity. If neither is present, or if either is present but for some reason inactive (e.g., the DHCP server hasn't given me an address or my network cable is unplugged), then I don't.
Java.net.NetworkInterface has a static method "getNetworkInterfaces" that does what I need. Listing 1 provides a test program to display a machine's interfaces. (Listings 1-4 can be downloaded from below.) The display refreshes every second. Figure 1 shows the program in action on my machine. I see either wlan0 (my wireless card) or eth0 (regular card). There are several static methods I could have used including getByName(String) that, if I knew what interface I was looking for, might work better for me. But why limit myself? Don't get too attached to any names you see. A Mac friend of mine reports "en0" as his network identifier. Don't limit yourself by checking for an interface because you think you know its name.
Test out the program by disabling your network adapter. On Linux, you can issue an "ifdown" command on the interface you're disabling. On Windows 2000 it's "Settings, LAN connection, Properties, Disable." The interface disappears from the screen. Pop in another card or just turn this one back on, and it should come back.
Try deactivating the connection instead. Leave the card in, but unplug the network cable. The network interface stays active, but there's no longer an InetAddress associated with it. This didn't work for me under Linux, only Win2000 (and Mac). If you are working with a DHCP server, you could also issue a manual "DHCP release" command to simulate the same effect. It isn't sufficient just to check for the existence of an interface. I must also verify that there's an associated InetAddress object.
Note: The "127.0.0.1" interface never changes. This is the local loopback device (it should say "loopback=true" on the test program), and all machines have it. It won't help you connect to the outside world. What I really want to check for is any active network interface with an associated InetAddress that is not the local loopback device. NetworkInterface doesn't have anything to help here, but InetAddress has a method isLoopbackAddress().
With algorithm firmly in hand, I wrote InterfaceCheck.java (see Listing 2). It provides a two-function API, isNetworkAvailable() and displayInterfaceInfo(). The first is where my test for good interfaces will be. The second I lifted from my earlier test program and use as a way to dump information on the available interfaces. This is just for demo purposes; it's not necessary. I deliberately kept this bean small and simple so I could use it anywhere.
The first place I applied my new test was a JSP page that reads and formats an RSS newsfeed. The taglib I'm using doesn't offer a way to fail gracefully if I ask for an input stream from an external machine and an exception is thrown. Since I don't need this particular JSP to run properly in order to work on other aspects of the system, I wanted a way to wrap this whole section in a piece of logic that says, "If network is available, try to load this RSS feed. Else say 'Network is down' and move on."
The JSP in Listing 3 shows the result. The Net-sensitive code is surrounded by an if-then block that uses my InterfaceCheck bean to see if I'm connected. Figures 2 and 3 show some sample tests of the program in action.
Here I found a hole in my logic. My test program was working fine, and then I tried to access a URL of "http://localhost:8080/..." When my interfaces were down, it failed. But it shouldn't, since I should have access to localhost (it's my own machine after all). Listing 4 shows my final version of InterfaceCheck2.java with an extended API for this situation. Provide the address to which you want to connect; the code handles it differently if it's a local address. If an external address is provided and interfaces are down, an UnknownHostException is thrown, so return false (since the logic still holds, the URL is still not available).
I could do other things with this program. You probably noticed that the logic is called every time. The most obvious thing would be to cache the results and update periodically. There's something elegant about leaving it small and simple, however. You can drop it in anywhere you like (JSP, custom tag, Swing application, etc.) and have it work as is. Why keep messing with it?
This technique doesn't detect good connections; it detects their absence. It's easier. Your connection could fail for any number of unpredictable reasons on either end or somewhere in the middle. But in situations where you know you don't have any connectivity and temporarily need to tell your code not to even try it, NetworkInterface handles the job nicely.
Hello, Net? Are You There?
Detecting if a machine is connected to the network is popular in any language. The problem is it makes no sense. What it often means is, can this machine use a certain port to access a certain application on a certain machine? To answer that question there's only one true test. Try it. If it works, you answered your own question.
What if it doesn't? Now you'll want to figure out where the connection failed and why. Maybe the application you wanted to connect to is not running on the destination machine. Maybe the machine is not up, period. Maybe the DNS is screwed up or a firewall is getting in the way. For all of these issues, it would still be true to say that your machine is connected to the Net. You just can't connect where you want, the way you want. When this happens, you'll want to contact the administrators of the remote machine as soon as you can, if possible. There's nothing as annoying as debugging your connection for hours before discovering that the remote machine was down for maintenance and somebody forgot to tell you.
Assuming that the remote machine is up, the safest strategy for debugging your faulty connection is to work from your own machine outward. Prove (or disprove) what you can, and move on. This article demonstrates a technique for detecting active network interfaces on a computer. No interfaces, no connectivity. If there are interfaces, move on to the next step. Usually this means "pinging" the remote machine. Unfortunately for Java programmers there's no pure solution for implementing ping, due to the low-level socket (ICMP) required. It can be done, but most Java implementations rely on JNI. If attempting to ping several remote machines returns "No route to host" (or similar), then most likely you have a gateway problem, and no traffic is getting out from your machine. Of course, if ping runs but doesn't respond, this doesn't mean anything - the remote machine can be configured to not respond to ping. Besides, since ping runs at a different level of the TCP/IP stack than most applications, you still haven't found out anything about the remote machine even if it does respond. "I can ping it but can't telnet to it" is a common bug indeed.
Everyone has different strategies for debugging a "down" machine, depending on the circumstances. The key is to work with the knowns and try to reduce the variables in the problem. Assuming you can know the status of everything between your application and the other guy's is just asking for trouble. Know what you can and can't properly test. Don't forget to call and ask if the other machine is up! Why guess when a phone call or e-mail will tell you for sure?
About The Author
Duane Morin is an independent consultant with seven years of experience as a Java architect for the financial services industry. Will hack code for food, or a trip to the Cayman Islands.
Vol. 8, Issue 7, p. 58