001package org.apache.commons.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
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import java.net.InetAddress;
026import java.net.NetworkInterface;
027import java.net.UnknownHostException;
028import java.util.Enumeration;
029
030/**
031 * Simple utility for getting the local host name.
032 * <p>
033 * @author Aaron Smuts
034 */
035public class HostNameUtil
036{
037    /** The logger. */
038    private static final 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>
082     * <p>
083     * If this method cannot find a non-loopback address using this selection algorithm, it will
084     * fall back to calling and returning the result of JDK method
085     * <code>InetAddress.getLocalHost</code>.
086     * <p>
087     * <a href="http://issues.apache.org/jira/browse/JCS-40">JIR ISSUE JCS-40</a>
088     * <p>
089     * @return InetAddress
090     * @throws UnknownHostException If the LAN address of the machine cannot be found.
091     */
092    public static InetAddress getLocalHostLANAddress()
093        throws UnknownHostException
094    {
095        try
096        {
097            InetAddress candidateAddress = null;
098            // Iterate all NICs (network interface cards)...
099            for ( Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); )
100            {
101                NetworkInterface iface = ifaces.nextElement();
102                // Iterate all IP addresses assigned to each card...
103                for ( Enumeration<InetAddress> inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); )
104                {
105                    InetAddress inetAddr = inetAddrs.nextElement();
106                    if ( !inetAddr.isLoopbackAddress() )
107                    {
108                        if ( inetAddr.isSiteLocalAddress() )
109                        {
110                            // Found non-loopback site-local address. Return it immediately...
111                            return inetAddr;
112                        }
113                        else if ( candidateAddress == null )
114                        {
115                            // Found non-loopback address, but not necessarily site-local.
116                            // Store it as a candidate to be returned if site-local address is not subsequently found...
117                            candidateAddress = inetAddr;
118                            // Note that we don't repeatedly assign non-loopback non-site-local addresses as candidates,
119                            // only the first. For subsequent iterations, candidate will be non-null.
120                        }
121                    }
122                }
123            }
124            if ( candidateAddress != null )
125            {
126                // We did not find a site-local address, but we found some other non-loopback address.
127                // Server might have a non-site-local address assigned to its NIC (or it might be running
128                // IPv6 which deprecates the "site-local" concept).
129                // Return this non-loopback candidate address...
130                return candidateAddress;
131            }
132            // At this point, we did not find a non-loopback address.
133            // Fall back to returning whatever InetAddress.getLocalHost() returns...
134            InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
135            if ( jdkSuppliedAddress == null )
136            {
137                throw new UnknownHostException( "The JDK InetAddress.getLocalHost() method unexpectedly returned null." );
138            }
139            return jdkSuppliedAddress;
140        }
141        catch ( Exception e )
142        {
143            UnknownHostException unknownHostException = new UnknownHostException( "Failed to determine LAN address: "
144                + e );
145            unknownHostException.initCause( e );
146            throw unknownHostException;
147        }
148    }
149}