This is an automated email from the ASF dual-hosted git repository.

andy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git

commit 7ad441cf5b45bcb99e0dbbf159361ea5067877ce
Author: Andy Seaborne <[email protected]>
AuthorDate: Sat Nov 30 09:03:05 2024 +0000

    Find non-loopback IP address
---
 .../main/java/org/apache/jena/atlas/net/Host.java  | 154 +++++++++++++++++++++
 .../test/java/org/apache/jena/atlas/TC_Atlas.java  |  12 +-
 .../jena/atlas/{TC_Atlas.java => net/TS_Net.java}  |  25 +---
 .../java/org/apache/jena/atlas/net/TestHost.java   |  60 ++++++++
 4 files changed, 224 insertions(+), 27 deletions(-)

diff --git a/jena-base/src/main/java/org/apache/jena/atlas/net/Host.java 
b/jena-base/src/main/java/org/apache/jena/atlas/net/Host.java
new file mode 100644
index 0000000000..1028472c30
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/net/Host.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.atlas.net;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.UnknownHostException;
+import java.util.Enumeration;
+
+import org.apache.jena.atlas.RuntimeIOException;
+import org.apache.jena.atlas.io.IOX;
+
+public class Host {
+    /**
+     * Returns a host address, for what is most likely the machine's LAN IP 
address
+     * (not a loopback address, 127.0.0.x or ::1). If there is no other 
choice, it returns
+     * <code>a local host address</code>.
+     */
+    public static String getHostAddress() {
+        try {
+            InetAddress addr = getLocalHostLANAddress$();
+            if ( addr == null )
+                return null;
+            return addr.getHostAddress();
+        } catch ( UnknownHostException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns an <code>InetAddress</code> object encapsulating what is most 
likely the machine's LAN IP address
+     * (not a loopback address, 127.0.0.x or ::1). If there is no other 
choice, it returns
+     * <code>InetAddress.getLocalHost</code>.
+     * Throw a {@link RuntimeIOException runtime IO exception} if no address 
choice found.
+     */
+    public static InetAddress getLocalHostLANAddress() {
+        try {
+            return getLocalHostLANAddress$();
+        } catch (IOException ioException) {
+            throw IOX.exception(ioException);
+        }
+    }
+
+    /**
+     * Returns an <code>InetAddress</code> object encapsulating what is most 
likely the machine's LAN IP address.
+     * <p/>
+     * This method is intended for use as a replacement of JDK method 
<code>InetAddress.getLocalHost</code>, because
+     * that method is ambiguous on Linux systems. Linux systems enumerate the 
loopback network interface the same
+     * way as regular LAN network interfaces, but the JDK 
<code>InetAddress.getLocalHost</code> method does not
+     * specify the algorithm used to select the address returned under such 
circumstances, and will often return the
+     * loopback address, which is not valid for network communication. Details
+     * <a 
href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4665037";>here</a>.
+     * <p/>
+     * This method will scan all IP addresses on all network interfaces on the 
host machine to determine the IP address
+     * most likely to be the machine's LAN address. If the machine has 
multiple IP addresses, this method will prefer
+     * a site-local IP address (e.g. 192.168.x.x or 10.10.x.x, usually IPv4) 
if the machine has one (and will return the
+     * first site-local address if the machine has more than one), but if the 
machine does not hold a site-local
+     * address, this method will return simply the first non-loopback address 
found (IPv4 or IPv6).
+     * <p/>
+     * If this method cannot find a non-loopback address using this selection 
algorithm, it will fall back to
+     * calling and returning the result of JDK method 
<code>InetAddress.getLocalHost</code>.
+     * <p/>
+     *
+     * @throws UnknownHostException If the LAN address of the machine cannot 
be found.
+     */
+    private static InetAddress getLocalHostLANAddress$() throws 
UnknownHostException {
+        // This code comes from:
+        // https://issues.apache.org/jira/browse/JCS-40
+        try {
+            InetAddress candidateAddress = null;
+            // Iterate all NICs (network interface cards)...
+            for (Enumeration<NetworkInterface> ifaces = 
NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements();) {
+                NetworkInterface iface = ifaces.nextElement();
+                // Iterate all IP addresses assigned to each card...
+                for (Enumeration<InetAddress> inetAddrs = 
iface.getInetAddresses(); inetAddrs.hasMoreElements();) {
+                    InetAddress inetAddr = inetAddrs.nextElement();
+                    if (!inetAddr.isLoopbackAddress()) {
+                        if (inetAddr.isSiteLocalAddress()) {
+                            // Found non-loopback site-local address. Return 
it immediately...
+                            return inetAddr;
+                        }
+                        if (candidateAddress == null) {
+                            // Found non-loopback address, but not necessarily 
site-local.
+                            // Store it as a candidate to be returned if 
site-local address is not subsequently found...
+                            candidateAddress = inetAddr;
+                            // Note that we don't repeatedly assign 
non-loopback non-site-local addresses as candidates,
+                            // only the first. For subsequent iterations, 
candidate will be non-null.
+                        }
+                    }
+                }
+            }
+            if (candidateAddress != null) {
+                // We did not find a site-local address, but we found some 
other non-loopback address.
+                // Server might have a non-site-local address assigned to its 
NIC (or it might be running
+                // IPv6 which deprecates the "site-local" concept).
+                // Return this non-loopback candidate address...
+                return candidateAddress;
+            }
+            // At this point, we did not find a non-loopback address.
+            // Fall back to returning whatever InetAddress.getLocalHost() 
returns...
+            InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
+            if (jdkSuppliedAddress == null) {
+                throw new UnknownHostException("The JDK 
InetAddress.getLocalHost() method unexpectedly returned null.");
+            }
+            return jdkSuppliedAddress;
+        }
+        catch (UnknownHostException e) { throw e; }
+        catch (Exception e) {
+            UnknownHostException unknownHostException = new 
UnknownHostException("Failed to determine LAN address: " + e);
+            unknownHostException.initCause(e);
+            throw unknownHostException;
+        }
+    }
+
+    // @formatter:off
+//    public static void main(String ... arg) throws UnknownHostException {
+//        try {
+//            for (Enumeration<NetworkInterface> ifaces = 
NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements();) {
+//                NetworkInterface iface = ifaces.nextElement();
+//                // Iterate all IP addresses assigned to each card...
+//                for (Enumeration<InetAddress> inetAddrs = 
iface.getInetAddresses(); inetAddrs.hasMoreElements();) {
+//                    InetAddress inetAddr = inetAddrs.nextElement();
+//                    System.out.println("IP Address : '" 
+inetAddr.getHostAddress()+"'");
+//                }
+//            }
+//            System.out.println();
+//
+//            InetAddress inetAddr = getLocalHostLANAddress();
+//            //InetAddress localhost = InetAddress.getLocalHost();
+//            System.out.println("System IP Address : '" 
+inetAddr.getHostAddress()+"'");
+//        } catch (Exception ex) {
+//            ex.printStackTrace();
+//            System.exit(0);
+//        }
+//    }
+    // @formatter:on
+}
diff --git a/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java 
b/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java
index 1f3ddc5167..d2d0ae08f7 100644
--- a/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java
+++ b/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java
@@ -18,13 +18,15 @@
 
 package org.apache.jena.atlas;
 
+import org.junit.runner.RunWith ;
+import org.junit.runners.Suite ;
+
 import org.apache.jena.atlas.io.TS_IO ;
 import org.apache.jena.atlas.iterator.TS_Iterator ;
 import org.apache.jena.atlas.lib.TS_Lib ;
 import org.apache.jena.atlas.lib.persistent.TS_Persistent;
 import org.apache.jena.atlas.lib.tuple.TS_Tuple ;
-import org.junit.runner.RunWith ;
-import org.junit.runners.Suite ;
+import org.apache.jena.atlas.net.TS_Net;
 
 @RunWith(Suite.class)
 @Suite.SuiteClasses( {
@@ -34,11 +36,7 @@ import org.junit.runners.Suite ;
     , TS_Iterator.class
     , TS_IO.class
     , TS_Persistent.class
-//    , TS_Event.class
-//    , TS_JSON.class
-//    , TS_Data.class
-//    , TS_Web.class
-//    , TestCSVParser.class
+    , TS_Net.class
 })
 
 public class TC_Atlas
diff --git a/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java 
b/jena-base/src/test/java/org/apache/jena/atlas/net/TS_Net.java
similarity index 63%
copy from jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java
copy to jena-base/src/test/java/org/apache/jena/atlas/net/TS_Net.java
index 1f3ddc5167..1f2a65d94a 100644
--- a/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java
+++ b/jena-base/src/test/java/org/apache/jena/atlas/net/TS_Net.java
@@ -16,30 +16,15 @@
  * limitations under the License.
  */
 
-package org.apache.jena.atlas;
+package org.apache.jena.atlas.net;
+
 
-import org.apache.jena.atlas.io.TS_IO ;
-import org.apache.jena.atlas.iterator.TS_Iterator ;
-import org.apache.jena.atlas.lib.TS_Lib ;
-import org.apache.jena.atlas.lib.persistent.TS_Persistent;
-import org.apache.jena.atlas.lib.tuple.TS_Tuple ;
 import org.junit.runner.RunWith ;
 import org.junit.runners.Suite ;
 
+
 @RunWith(Suite.class)
 @Suite.SuiteClasses( {
-    // Library
-      TS_Lib.class
-    , TS_Tuple.class
-    , TS_Iterator.class
-    , TS_IO.class
-    , TS_Persistent.class
-//    , TS_Event.class
-//    , TS_JSON.class
-//    , TS_Data.class
-//    , TS_Web.class
-//    , TestCSVParser.class
+    TestHost.class
 })
-
-public class TC_Atlas
-{}
+public class TS_Net {}
diff --git a/jena-base/src/test/java/org/apache/jena/atlas/net/TestHost.java 
b/jena-base/src/test/java/org/apache/jena/atlas/net/TestHost.java
new file mode 100644
index 0000000000..4cd94688d7
--- /dev/null
+++ b/jena-base/src/test/java/org/apache/jena/atlas/net/TestHost.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.atlas.net;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.junit.Test;
+
+import org.apache.jena.atlas.RuntimeIOException;
+
+public class TestHost {
+    @Test public void host_localNonLoopback() {
+        // Assumes the machine has some kind of IP networking.
+        try {
+            InetAddress addr = Host.getLocalHostLANAddress();
+            assertNotNull(addr);
+            assertFalse(addr.isLoopbackAddress());
+        } catch ( RuntimeIOException ex) {
+            Throwable x = ex.getCause();
+            if ( x instanceof UnknownHostException )
+                return;
+            throw ex;
+        }
+    }
+
+    @Test public void host_localNonLoopbackAddress() {
+        // Assumes the machine has some kind of IP networking.
+        try {
+            // InetAddress.toString is "host/address"
+            String addr = Host.getHostAddress();
+            assertNotNull(addr);
+            assertFalse(addr.contains("/"));
+        } catch ( RuntimeIOException ex) {
+            Throwable x = ex.getCause();
+            if ( x instanceof UnknownHostException )
+                return;
+            throw ex;
+        }
+    }
+}

Reply via email to