Core Java Technologies Tech Tips Tips, Techniques, and Sample Code Welcome to the Core Java Technologies Tech Tips for August 16, 2005. Here you'll get tips on using core Java technologies and APIs, such as those in Java 2 Platform, Standard Edition (J2SE). This issue covers: * Using InetAddress for Host Name Lookup and Host Reachability * Locks These tips were developed using Java 2 Platform Standard Edition Development Kit 5.0 (JDK 5.0). You can download JDK 5.0 at http://java.sun.com/j2se/1.5.0/download.jsp. This issue of the Core Java Technologies Tech Tips is written by John Zukowski, president of JZ Ventures, Inc. (http://www.jzventures.com). You can view this issue of the Tech Tips on the Web at http://java.sun.com/developer/JDCTechTips/2005/tt0816.html See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms. For more Java technology content, visit these sites: java.sun.com - The latest Java platform releases, tutorials, and newsletters. java.net - A web forum for collaborating and building solutions together. java.com - Hot games, cool apps -- Experience the power of Java technology. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - USING INETADDRESS FOR HOST NAME LOOKUP AND HOST REACHABILITY The InetAddress class has been around since the initial release of the Java platform. It provides for the identity of a host through an Internet Protocol (IP) address. In other words, if you type in a name like yahoo.com, you can find out its IP address with the help of the InetAddress class. The InetAddress class design might seem a bit odd. There are no public constructors for the class, only static creators. Instances are immutable, meaning that once you have an instance of the class, you can't change it. However, that design was done for Java security purposes. A user can't change the results between lookups. Here's an example that demonstrates the use of the InetAddress class. The following program, Lookup, identifies the name and IP address of the arguments you pass in from the command line: import java.net.*; public class Lookup { public static void main(String args[]) { for (String name: args) { try { InetAddress address = InetAddress.getByName(name); System.out.println("Name: " + address.getHostName()); System.out.println("Addr: " + address.getHostAddress()); } catch (UnknownHostException e) { System.err.println("Unable to lookup " + name); } } } } The getByName() method in the program acts like the nslookup command in Microsoft Windows and Unix. It takes the name specified in the command line argument and then does a lookup in the designated name server for the system. Typically, it does the lookup using the Domain Name System (DNS). The lookup works either for a name, like yahoo.com, or an IP address, like 66.94.234.13. If you enter yahoo.com, you get 66.94.234.13. If you enter 66.94.234.13, you get the name of the host farm at Yahoo. In this case, the public name of the host is w2.rc.vip.scd.yahoo.com. > java Lookup sun.com yahoo.com 66.94.234.13 Name: sun.com Addr: 209.249.116.195 Name: yahoo.com Addr: 216.109.112.135 Name: w2.rc.vip.scd.yahoo.com Addr: 66.94.234.13 Some host names resolve to multiple IP addresses. Instead of using the getByName() method of InetAddress to look up a single name, you can use the getAllByName() method to get an array of InetAddress objects. Also, the InetAddress class offers support for reverse name resolution. This means that you can look up the IP address for a particular host and then use that IP address to look up the host name. If the two don't match, this isn't necessarily wrong. However if you know that the two are supposed to match, a mismatch might indicate a spoofing attack. The LookupAll program below uses the getAllByName() method to look up all addresses for a host. It then uses the getCanonicalHostName() method to look up the fully-qualified domain name for the host. import java.net.*; public class LookupAll { public static void main(String args[]) { for (String name: args) { try { InetAddress address[] = InetAddress.getAllByName(name); for (InetAddress each: address) { System.out.println("Name: " + each.getHostName()); System.out.println("Addr: " + each.getHostAddress()); System.out.println("Canonical: " + each.getCanonicalHostName()); } System.out.println("----"); } catch (UnknownHostException e) { System.err.println("Unable to lookup " + name); } } } } One such host with multiple addresses is google.com. The following example includes google.com and yahoo.com as arguments. The example better demonstrates how the reverse lookup of the IP address for yahoo.com resolves to a different host name. >> java LookupAll yahoo.com google.com Name: yahoo.com Addr: 216.109.112.135 Canonical: w2.rc.vip.dcn.yahoo.com Name: yahoo.com Addr: 66.94.234.13 Canonical: w2.rc.vip.scd.yahoo.com ---- Name: google.com Addr: 216.239.39.99 Canonical: 216.239.39.99 Name: google.com Addr: 216.239.57.99 Canonical: 216.239.57.99 Name: google.com Addr: 216.239.37.99 Canonical: 216.239.37.99 ---- The getCanonicalHostName() method was added to the 1.4 version of the J2SE platform. With J2SE 5.0, you can also use InetAddress to check for a host for reachability. In other words, you can determine if the host is "alive." This test is typically done from the command line with the "ping" command or with a simple TCP ECHO request. But why bother opening a command line prompt, possibly using the ProcessBuilder class explained in the July 27, 2005 Tech Tip "From Runtime.exe to ProcessBuilder" (http://java.sun.com/developer/JDCTechTips/2005/tt0727.html#2)? Instead, all you need to use is the isReachable() method in InetAddress. The isReachable() method comes in two forms: o public boolean isReachable(int timeout) throws IOException o public boolean isReachable(NetworkInterface netif, int ttl, int timeout) throws IOException For most people, the first version of isReachable is sufficient. With this format, you simply provide a timeout for the check. The timeout represents the number of milliseconds to wait to see if the destination host is alive. The method returns "false" if the host is dead, the ping service is disabled, or the request is blocked at a firewall. If you locate a reachable host, the method returns "true". If a network error occurs, the method throws an IOException. The second version of isReachable allows you to identify which NetworkInterface to use for the test. It also allows you to specify the maximum number of hops to try to reach a particular host. If you only have one network interface, but you want to specify the number of hops, pass in null for any interface. As is the case for the first version of isReachable, you also specify a timeout value. The possible return values for this version of isReachable are the same as for the one-argument version. The following program, LookupReach, adds a reachable check to the earlier single name lookup: import java.io.*; import java.net.*; public class LookupReach { public static void main(String args[]) { for (String name: args) { try { InetAddress address = InetAddress.getByName(name); System.out.println("Name: " + address.getHostName()); System.out.println("Addr: " + address.getHostAddress()); System.out.println("Reach: " + address.isReachable(3000)); } catch (UnknownHostException e) { System.err.println("Unable to lookup " + name); } catch (IOException e) { System.err.println("Unable to reach " + name); } } } } Try running LookupReach with yahoo.com. sun.com, and google.com as arguments. You'll see that the program returns "false". Internet sites for most big web companies have disabled the service or blocked the requests. Also try running the program with an argument for a university site, like web.mit.edu. Sites for most universities allow the request. > java LookupReach yahoo.com sun.com google.com web.mit.edu Name: yahoo.com Addr: 216.109.112.135 Reach: false Name: sun.com Addr: 209.249.116.195 Reach: false Name: google.com Addr: 216.239.37.99 Reach: false Name: web.mit.edu Addr: 18.7.22.69 Reach: true When you use the InetAddress class, keep in mind that you can specify commonly-used IPv4 names in the form of a.b.c.d. You can also specify the newer IPv6 names in the form x:x:x:x:x:x:x:x, where the 'x's are the hex values of the eight 16-bit pieces of the address. For more information about the IPv6 architecture, see RFC 2373 (http://www.ietf.org/rfc/rfc2373.txt). If you aren't sure which version a name represents, you can check if the InetAddress is an instanceof Inet4Address or Inet6Address. This is more commonly done for user-supplied addresses. One last thing worth mentioning: name lookups and reverse lookups can be expensive operations. The system uses a cache mechanism for both performance and security reasons. Once you look up a host, you always know you'll get the same one, and the results can't change between lookups. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - LOCKS One of the popular features of the J2SE 5.0 libraries is the addition of concurrency utilities. Provided as part of JSR 166 (http://jcp.org/en/jsr/detail?id=166), the utilities provide advanced concurrency programming capabilities that take developers beyond the synchronized keyword and related synchronized blocks. One of the areas improved by the concurrency utilities is locking. Among the capabilities that it offers, the concurrency utilities support lock timeouts, multiple condition variables per lock, read-write locks, and the ability to interrupt a thread waiting for a lock. For more information about the additional lock support, see the documentation for the java.util.concurrent.locks package (http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/package-summary.html#package_description). The foundation of the package is the Lock interface, which offers a series of methods for acquiring and releasing a lock. The typical usage sequence is: get a lock, access the protected resource, and release the lock. Here is the typical usage pattern: Lock l = ...; l.lock(); try { // protected resource access } finally { l.unlock(); } When the Lock class is used in this way, it works similarly to a typical synchronized lock: synchronized(lockVariable) { // protected resource access } Similar but not the same. If you don't get the unlock() call in a finally block, your application won't function properly. With the synchronized block, that isn't necessary. Another method provided by the Lock interface that acquires a lock is lockInterruptibly(). After a thread requests a lock, it waits. The lockInterruptibly() method allows you to interrupt the waiting thread. The lock() method doesn't. If interrupt() is called on a thread that is waiting for the lock with lockInterruptibly(), the wait will be interrupted. The waiting thread then wakes up and an InterruptedException is thrown. In this case, no attempt is made to access the protected resource, and any other operations continue. The typical usage pattern for the lockInterruptibly() method is: Lock l = new ReentrantLock(); try { l.lockInterruptibly(); try { // protected resource access } finally { l.unlock(); } } catch (InterruptedException e) { System.err.println("Interrupted wait"); } Here, the inner try-finally handles exceptions while the resources are held. The outer try-catch handles exceptions acquiring the resource. You should never call unlock if the lock acquisition fails. A word of caution: calling lockInterruptibly() does not necessarily mean you can interrupt the wait. Not all Lock implementations support the operation. Another method provided by the Lock interface, tryLock(), brings timeouts into play. The tryLock() method comes in two versions. The first version accepts no arguments. It tries to get the lock, and immediately fails if the lock isn't available: Lock lock = ...; if (lock.tryLock()) { try { // protected resource access } finally { lock.unlock(); } } else { // alternative operation } This is like going to the copy machine, seeing that someone is busy with a long copy job, and moving on to a slower but available machine for the task. The second version of the tryLock method accepts two parameters to signify a timeout. The first argument is a long variable to specify the maximum wait time. The second argument is a TimeUnit for specifying the unit of time. For instance, lock.tryLock(300, TimeUnit.MILLISECONDS) attempts to acquire a lock. If it can't get the lock after waiting for 300 milliseconds, it returns false, indicating the inability to get the lock. By comparison, lock.tryLock(30, TimeUnit.SECONDS) waits a maximum of 30 seconds. You need to explicitly specify both the value and the units. The TimeUnit class allows you to specify units as seconds, milliseconds, microseconds, or nanoseconds. 1 second = 1 thousand milliseconds 1 second = 1 million microseconds 1 second = 1 billion nanoseconds Although you've seen how to use the Lock interface, you haven't seen how to actually create a Lock. The prior examples all show: Lock lock = ...; The java.util.concurrent.locks package includes three implementations of the Lock interface: o ReentrantLock o ReentrantReadWriteLock.ReadLock o ReentrantReadWriteLock.WriteLock The first implementation, ReentrantLock, is the one typically used. The other two implementations are inner classes of the ReentrantReadWriteLock class. The ReentrantReadWriteLock class contains two locks: ReadLock and WriteLock. For ReentrantLock, the typical usage pattern is: Lock l = new ReentrantLock(); l.lock(); try { // protected resource access } finally { l.unlock(); } Use a read-write lock when reads are long and frequent, and writes are infrequent. Your access to a protected object would then use separate locks acquired as shown here: ReadWriteLock rwl = new ReentrantReadWriteLock(); Lock readLock = rwl.readLock(); Lock writeLock = rwl.writeLock(); You then use the locks in the same manner as the ReentractLock, using the lock appropriate for the type of access you want to perform. The only implementation of the ReadWriteLock interface is the ReentrantReadWriteLock class itself. For more information on the Lock support, see the package description (http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/package-summary.html#package_description) . . . . . . . . . . . . . . . . . . . . . . . IMPORTANT: Please read our Terms of Use, Privacy, and Licensing policies: http://www.sun.com/share/text/termsofuse.html http://www.sun.com/privacy/ http://developers.sun.com/dispatcher.jsp?uid=6910008 * FEEDBACK Comments? Please enter your feedback on the Tech Tips at: http://developers.sun.com/contact/feedback.jsp?category=sdn * SUBSCRIBE/UNSUBSCRIBE Subscribe to other Java developer Tech Tips: - Enterprise Java Technologies Tech Tips. Get tips on using enterprise Java technologies and APIs, such as those in the Java 2 Platform, Enterprise Edition (J2EE). - Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME). To subscribe to these and other JDC publications: - Go to the Sun Developer Network - Subscriptions page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), choose the newsletters you want to subscribe to and click "Submit". - To unsubscribe, go to the Subscriptions page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), uncheck the appropriate checkbox, and click "Submit". - To use our one-click unsubscribe facility, see the link at the end of this email: - ARCHIVES You'll find the Core Java Technologies Tech Tips archives at: http://java.sun.com/developer/TechTips/index.html - COPYRIGHT Copyright 2005 Sun Microsystems, Inc. All rights reserved. 4150 Network Circle, Santa Clara, California 95054 USA. This document is protected by copyright. For more information, see: http://java.sun.com/developer/copyright.html Core Java Technologies Tech Tips August 16, 2005 Trademark Information: http://www.sun.com/suntrademarks/ Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.