1

When using com.sun.net.httpserver.HttpServer, I observed the following in an application:

  • When run not from a jar (e.g. from Eclipse or on command line) System.setProperty("java.net.preferIPv4Stack", "true"), HttpServer.getAddress() returns an IPv4 address as expected.
  • When run from an executable jar generated by hand (i.e. jar cvfe ... rather than Eclipse export), it also returns an IPv4 address as expected.
  • When run from an Eclipse-generated runnable jar (either from command line or the shell), it returns an IPv6 address unless -Djava.net.preferIPv4Stack=true is specified on the command line. That is, it honors the command line setting but ignores the System.setProperty(), only when run from a jar exported by Eclipse.

I looked at the source and was able to create a simple test with ServerSocketChannel:

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.channels.ServerSocketChannel;

public class IP4Test {

    public static void main (String[] args) throws Exception {

        System.setProperty("java.net.preferIPv4Stack", "true");

        InetSocketAddress addr = new InetSocketAddress("0.0.0.0", 80);
        ServerSocketChannel schan = ServerSocketChannel.open();
        ServerSocket socket = schan.socket();
        socket.bind(addr);

        System.out.println(socket.getLocalSocketAddress());

    }

}

When run from a class file with the property set to "true" in the code, it outputs:

/0.0.0.0:80

When run from a class file with the property set to "false" (or not set) in the code, it outputs:

/0:0:0:0:0:0:0:0:80

This is as expected.

When run from a hand-generated jar (e.g. jar cvfe IP4Test.jar IP4Test IP4Test.class) it behaves as expected as well.

However, when exported to a runnable jar from Eclipse and run from anywhere (command line or shell), it outputs an IPv6 address regardless of what the property was set to in code -- yet, specifying -Djava.net.preferIPv4Stack=true on the command line, it outputs an IPv4 address.

That is, it works as expected when run from class files and hand-generated jar files, but from an Eclipse-generated jar it ignores the System.setProperty() call but still obeys the command line.

Why is System.setProperty("java.net.preferIPv4Stack") being ignored when the application is exported to a jar from Eclipse, why is it different than the behavior of no / hand-generated jar, and how can I make it work properly?

Edit: After reading prunge's answer I tried a hand-generated JAR (previously I was using Eclipse) and discovered that the behavior was unique to Eclipse-generated JARs, rather than JARs in general. I modified the above question to reflect that. Prunge's theory seems the most likely - whether or not it's PlainSocketImpl or something else indirectly related to one of the org.eclipse classes that Eclipse puts in the JAR, I do not know. Mostly I want to point out that the answer was posted before this most recent edit.

Update: Actually it's not unique to Eclipse. Even with a hand-generated jar that "works", if I, say, override the security manager on the command line, it stops working. This all even more strongly supports prunge's answer. I may never be able to figure out what class does it but the answer is absolutely to specify the property on the command line and not do it in code. At this point, I'm tired of rewriting the above question.

Jason C
  • 38,729
  • 14
  • 126
  • 182
  • If you want to statically force `System.setProperty("java.net.preferIPv4Stack", "true");` in the code, just insert the following snippet somwehere in in your main class, `static { System.setProperty("java.net.preferIPv4Stack", "true"); }` – ecle Feb 27 '15 at 02:07
  • It is called static initializer block to load something before the main class is loaded. http://stackoverflow.com/questions/804589/use-of-initializers-vs-constructors-in-java – ecle Feb 27 '15 at 02:14
  • @eee Thanks, but I'm not using any ipv4 related settings in eclipse. The above example uses system.setproperty, as the first line in main. In eclipse I'm not specifying the command line property at all, nor do I want to. I only tried the command line outside of eclipse after I noticed setproperty was being ignored. I will try in a static initializer when I am in front of the computer to see if it makes a difference but that doesnt explain the difference in behavior in and out of eclipse. – Jason C Feb 27 '15 at 02:15
  • It's bad behaviour for an application to do that. It should respect its OS environment. If the environment uses IPv6 the application should adapt to that. Forcing IPv4 should be left to the system or whoever runs the application. I wouldn't be surprised that something like that is the reason it is blocked outside the development environment. – Sander Steffann Feb 27 '15 at 02:16
  • Depends...in my case, I have to force IPv4 since my target LAN network system is totally based on IPv4...it doesn't support IPv6 – ecle Feb 27 '15 at 02:17
  • @sander There are reasons that I won't justify here. However, the application is not aware that it is in a development environment, nor is there any information on the internet implying that there would be different behavior along that line (which doesnt mean its not doing that, it would just mean I am one of the only people who has noticed, which I am suspicious of). – Jason C Feb 27 '15 at 02:18
  • @eee if your target environment doesn't support IPv6 then you don't have to force it, it will automatically use IPv4. Only when you have IPv6 addresses and routing on the system and the hostname you connect to has an AAAA DNS record will IPv6 be used. – Sander Steffann Feb 27 '15 at 02:40
  • I just discovered that it's not Eclipse vs. not Eclipse, it's Jar vs not Jar. Rewriting question. – Jason C Feb 27 '15 at 03:38
  • Try it with `null` instead of `"0.0.0.0"`, or try omitting the parameter altogether. – user207421 Feb 27 '15 at 04:09
  • And I've also just discovered that it's actually Eclipse-generated jar (which has the problem) vs. hand-generated jar (which doesn't), which lends support to prunge's answer, if not with PlainSocketImpl then with something else that's involved here (possibly via one of the org.eclipse classes). – Jason C Feb 27 '15 at 06:27
  • @eee Oh, I almost forgot: Moving it to a static initialization block did not have an effect. – Jason C Feb 27 '15 at 06:34
  • Try to see if this is the case http://stackoverflow.com/questions/722003/system-getpropertyuser-dir-anomaly-in-eclipse – ecle Feb 27 '15 at 08:37

1 Answers1

1

Setting java.net.preferIPV6Addresses programmatically is risky. There is other Java code executing before your class, and which code depends on how your Java application is launched. WebStart could affect this. Any pre-loaded Java agents could affect this also. It could also depend on which version of Java is used to launch.

The class PlainSocketImpl class seems to load this system property in a static block once when it is loaded. Depending on whether your class with its main method is the first to load this class, or whether something else such as the Java launcher loads it before your class will affect whether your class's property change takes effect.

(I theorize that if a JAR is loaded, then java.net.URL is loaded also which indirectly causes PlainSocketImpl to load early as opposed to a java.io.File which doesn't - not verified and just a guess though)


As for making it do what you want, how about creating the InetSocketAddress explicitly with an IPv4 address instead of a string:

InetSocketAddress addr = new InetSocketAddress(InetAddress.getByAddress(new byte[] {0, 0, 0, 0}), 80);

which should give you a java.net.Inet4Address because it's 4 bytes. You can verify by doing:

System.out.println(addr.getAddress().getClass());
prunge
  • 22,460
  • 3
  • 73
  • 80
  • 1
    `Inet4Address.getByName()` doesn't exist. It is inherited from `InetAddress.` It isn't reasonable therefore to expect it to have IPv4-specific behaviour. – user207421 Feb 27 '15 at 05:03
  • Thanks! This seems like a great theory and has a lot of supporting evidence. I am going to investigate a little further before accepting it. In particular, according to `-verbose:class`, my class was loaded before any other class I found that referred to this property; so I am working now to get the source for my specific version of the JDK, as well as find out if anything loads prior to `-verbose:class`'s output, and confirm your guess. – Jason C Feb 27 '15 at 05:49
  • So it seems like it's actually unique to jars generated from Eclipse, rather than by hand. Presumably this means one of the org.eclipse classes Eclipse includes is causing something else to load, or something else is different. But I'm burnt out, and don't know enough about class loading in jars to continue. Your answer seems correct. With this new info, while I could theoretically build the jar by hand and make it work, for my situation the safer thing to do is just distribute the jar with a startup script that passes the option on the command line. Some day I will get to the bottom of this. – Jason C Feb 27 '15 at 06:25
  • ... Actually it's not unique to Eclipse. Even with a hand-generated jar that "works", if I, say, override the security manager on the command line, it stops working. This all strongly supports this answer. I may never be able to figure out what class does it but the answer is absolutely to specify the property on the command line and not do it in code. – Jason C Feb 27 '15 at 16:58
  • Incidentally, explicitly using an inet4address doesnt work; at least not for the *any* address. The ServerSocketChannel doesnt seem to use the address directly for its socket, and still leads to an inet6address. But still, the better solution is, as you say, to not set that property programmatically. – Jason C Mar 01 '15 at 04:51