001    package org.apache.jcs.utils.net;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.net.InetAddress;
023    import java.net.NetworkInterface;
024    import java.net.UnknownHostException;
025    import java.util.Enumeration;
026    
027    import org.apache.commons.logging.Log;
028    import org.apache.commons.logging.LogFactory;
029    
030    /**
031     * Simple utility for getting the local host name.
032     * <p>
033     * @author Aaron Smuts
034     */
035    public class HostNameUtil
036    {
037        /** The logger. */
038        private final static Log log = LogFactory.getLog( HostNameUtil.class );
039    
040        /**
041         * Gets the address for the local machine.
042         * <p>
043         * @return InetAddress.getLocalHost().getHostAddress()
044         * @throws UnknownHostException
045         */
046        public static String getLocalHostAddress() throws UnknownHostException
047        {
048            try
049            {
050                String hostAddress = getLocalHostLANAddress().getHostAddress();
051                if ( log.isDebugEnabled() )
052                {
053                    log.debug( "hostAddress = [" + hostAddress + "]" );
054                }
055                return hostAddress;
056            }
057            catch ( UnknownHostException e1 )
058            {
059                log.error( "Couldn't get localhost address", e1 );
060                throw e1;
061            }
062        }
063    
064        /**
065         * Returns an <code>InetAddress</code> object encapsulating what is most likely the machine's
066         * LAN IP address.
067         * <p>
068         * This method is intended for use as a replacement of JDK method
069         * <code>InetAddress.getLocalHost</code>, because that method is ambiguous on Linux systems.
070         * Linux systems enumerate the loopback network interface the same way as regular LAN network
071         * interfaces, but the JDK <code>InetAddress.getLocalHost</code> method does not specify the
072         * algorithm used to select the address returned under such circumstances, and will often return
073         * the loopback address, which is not valid for network communication. Details <a
074         * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4665037">here</a>.
075         * <p>
076         * This method will scan all IP addresses on all network interfaces on the host machine to
077         * determine the IP address most likely to be the machine's LAN address. If the machine has
078         * multiple IP addresses, this method will prefer a site-local IP address (e.g. 192.168.x.x or
079         * 10.10.x.x, usually IPv4) if the machine has one (and will return the first site-local address
080         * if the machine has more than one), but if the machine does not hold a site-local address,
081         * this method will return simply the first non-loopback address found (IPv4 or IPv6). <p/> If
082         * this method cannot find a non-loopback address using this selection algorithm, it will fall
083         * back to calling and returning the result of JDK method <code>InetAddress.getLocalHost</code>.
084         * <p>
085         * <a href="http://issues.apache.org/jira/browse/JCS-40">JIR ISSUE JCS-40</a>
086         * <p>
087         * @return InetAddress
088         * @throws UnknownHostException If the LAN address of the machine cannot be found.
089         */
090        private static InetAddress getLocalHostLANAddress()
091            throws UnknownHostException
092        {
093            try
094            {
095                InetAddress candidateAddress = null;
096                // Iterate all NICs (network interface cards)...
097                for ( Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); )
098                {
099                    NetworkInterface iface = ifaces.nextElement();
100                    // Iterate all IP addresses assigned to each card...
101                    for ( Enumeration<InetAddress> inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); )
102                    {
103                        InetAddress inetAddr = inetAddrs.nextElement();
104                        if ( !inetAddr.isLoopbackAddress() )
105                        {
106                            if ( inetAddr.isSiteLocalAddress() )
107                            {
108                                // Found non-loopback site-local address. Return it immediately...
109                                return inetAddr;
110                            }
111                            else if ( candidateAddress == null )
112                            {
113                                // Found non-loopback address, but not necessarily site-local.
114                                // Store it as a candidate to be returned if site-local address is not subsequently found...
115                                candidateAddress = inetAddr;
116                                // Note that we don't repeatedly assign non-loopback non-site-local addresses as candidates,
117                                // only the first. For subsequent iterations, candidate will be non-null.
118                            }
119                        }
120                    }
121                }
122                if ( candidateAddress != null )
123                {
124                    // We did not find a site-local address, but we found some other non-loopback address.
125                    // Server might have a non-site-local address assigned to its NIC (or it might be running
126                    // IPv6 which deprecates the "site-local" concept).
127                    // Return this non-loopback candidate address...
128                    return candidateAddress;
129                }
130                // At this point, we did not find a non-loopback address.
131                // Fall back to returning whatever InetAddress.getLocalHost() returns...
132                InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
133                if ( jdkSuppliedAddress == null )
134                {
135                    throw new UnknownHostException( "The JDK InetAddress.getLocalHost() method unexpectedly returned null." );
136                }
137                return jdkSuppliedAddress;
138            }
139            catch ( Exception e )
140            {
141                UnknownHostException unknownHostException = new UnknownHostException( "Failed to determine LAN address: "
142                    + e );
143                unknownHostException.initCause( e );
144                throw unknownHostException;
145            }
146        }
147    }