Sitelet https://web.archive.org/web/20201023082352/https://github.com/dnsjava/dnsjava/issues/112
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JNA required on windows #112

Closed
andrewscode opened this issue May 27, 2020 · 8 comments
Closed

JNA required on windows #112

andrewscode opened this issue May 27, 2020 · 8 comments
Milestone

Comments

@andrewscode
Copy link

@andrewscode andrewscode commented May 27, 2020

JNA is currently required on windows. Although the code seems to imply it checks for JNA:

 public WindowsResolverConfigProvider() {
    if (System.getProperty("os.name").contains("Windows")) {
      try {
        this.inner = new WindowsResolverConfigProvider.InnerWindowsResolverConfigProvider();
      } catch (NoClassDefFoundError var2) {
        log.debug("JNA not available");
      }
    }

  }

Just creating the WindowsResolverConfigProvider.InnerWindowsResolverConfigProvider will not cause a NoClassDefFoundError exception.

But will be thrown later on by the initialize() method causing the entire ResolverConfig to fail

Caused by: java.lang.NoClassDefFoundError: com/sun/jna/Memory
	at org.xbill.DNS.config.WindowsResolverConfigProvider$InnerWindowsResolverConfigProvider.initialize(WindowsResolverConfigProvider.java:53)
	at org.xbill.DNS.config.WindowsResolverConfigProvider.initialize(WindowsResolverConfigProvider.java:108)
	at org.xbill.DNS.ResolverConfig.<init>(ResolverConfig.java:84)
	at org.xbill.DNS.ResolverConfig.refresh(ResolverConfig.java:74)
	at org.xbill.DNS.ResolverConfig.<clinit>(ResolverConfig.java:59)
@ibauersachs
Copy link
Member

@ibauersachs ibauersachs commented May 27, 2020

I can't verify this with the following minimal example (Sample code, console output, Maven config). Can you please provide some more context? Can you provide the content of your os.name property? Which JDK are you running this on?

import org.xbill.DNS.*;
public class Test {
  public static void main(String[] args) throws TextParseException {
    ResolverConfig.refresh();
    Lookup lookup = new Lookup("example.com");
    for (Record r : lookup.run()) {
      System.out.println(r);
    }
  }
}
[main] DEBUG org.xbill.DNS.config.WindowsResolverConfigProvider - JNA not available
[main] DEBUG org.xbill.DNS.config.JndiContextResolverConfigProvider$InnerJndiContextResolverConfigProvider - Added /62.2.17.61:53 to nameservers
[main] DEBUG org.xbill.DNS.config.JndiContextResolverConfigProvider$InnerJndiContextResolverConfigProvider - Added /62.2.24.158:53 to nameservers
[main] DEBUG org.xbill.DNS.config.JndiContextResolverConfigProvider$InnerJndiContextResolverConfigProvider - Added /62.2.17.60:53 to nameservers
[main] DEBUG org.xbill.DNS.config.JndiContextResolverConfigProvider$InnerJndiContextResolverConfigProvider - Added /62.2.24.162:53 to nameservers
[main] DEBUG org.xbill.DNS.Lookup - lookup example.com. A, cache answer: unknown
[main] DEBUG org.xbill.DNS.ExtendedResolver - Sending example.com./A, id=41041 to resolver 0 (SimpleResolver [/62.2.17.60:53]), attempt 1 of 3
[main] DEBUG org.xbill.DNS.SimpleResolver - Sending example.com./A, id=41041 to udp/62.2.17.60:53
[main] DEBUG org.xbill.DNS.Client - Starting dnsjava NIO selector thread
[main] DEBUG org.xbill.DNS.Cache - caching successful for example.com.
[main] DEBUG org.xbill.DNS.Lookup - Queried example.com. A: successful
[dnsjava NIO selector] DEBUG org.xbill.DNS.Client - dnsjava NIO selector thread stopped
example.com.		86210	IN	A	93.184.216.34
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <artifactId>sample</artifactId>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>dnsjava</groupId>
      <artifactId>dnsjava</artifactId>
      <version>3.1.0</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>1.7.30</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
@andrewscode
Copy link
Author

@andrewscode andrewscode commented May 27, 2020 •

I'm using Version 3.0.2 of dnsjava

os.name = Windows 10

JDK 1.8.241

This is running under a spring boot application.

@andrewscode
Copy link
Author

@andrewscode andrewscode commented May 27, 2020

Seems to be fine on it's own but not under spring boot.

On a side note, there doesn't seem to be a way of initializing the ResolverConfigProviders without initializing the ResolverConfig.refresh() being called first on it's existing list.

We always pass in a resolver for all Lookup. So we don't want the default resolvers to be initialized.

@ibauersachs
Copy link
Member

@ibauersachs ibauersachs commented May 27, 2020

Seems to be fine on it's own but not under spring boot.

I'm not familiar with Spring Boot. Any idea what it's doing different?

On a side note, there doesn't seem to be a way of initializing the ResolverConfigProviders without initializing the ResolverConfig.refresh() being called first on it's existing list.

This is/was intentional to avoid the need of calling refresh first, and I need to keep that behavior for backwards compatibility. I could add a property that skips the initialization. Would that help you?

We always pass in a resolver for all Lookup. So we don't want the default resolvers to be initialized.

@ibauersachs ibauersachs added this to the v3.2 milestone May 27, 2020
@andrewscode
Copy link
Author

@andrewscode andrewscode commented May 28, 2020

Seems to be fine on it's own but not under spring boot.

I'm not familiar with Spring Boot. Any idea what it's doing different?

I'm not entirely certain. If you provide that property to skip initialization that'd definitely help my problem.

Thanks

@ibauersachs
Copy link
Member

@ibauersachs ibauersachs commented May 28, 2020

The property dnsjava.configprovider.skipinit is already in master, see the linked commit. You can expect a release in a couple of days or so.

@andrewscode
Copy link
Author

@andrewscode andrewscode commented Jun 5, 2020 •

I've spent a bit more time looking at this.

When running standalone, the NoClassDefFoundError is thrown for the class com.sun.jna.Pointer. This doesn't happen with SpringBoot. But the real question is why does it get thrown without spring boot?

The WindowsResolverConfigProvider creates a new InnerWindowsResolverConfigProvider. That has no constructor and although the parent BaseResolverConfigProvider has some local members, nothing constructs a com.sun.jna.Pointer object. I wouldn't expect a NoClassDefFoundError thrown when constructing that Inner class.

I think what would help is simply calling Class.forName("com.sun.jna.Pointer", false, this.getClass().getClassLoader()) in the constructor of WindowsResolverConfigProvider. If a ClassNotFoundException is thrown, then you know jna is not on the classpath.

try {
      Class.forName("com.sun.jna.Pointer", false, this.getClass().getClassLoader());
      Class.forName("com.sun.jna.platform.win32.Win32Exception", false, this.getClass().getClassLoader());

    } catch (ClassNotFoundException e) {
      log.debug("JNA not available");
    }

I test Win32Exception as well as that is apart of the jna-platform (whereas Pointer is in the jna library)

ibauersachs added a commit that referenced this issue Jun 6, 2020
According to JLS §12.3, resolution of references in the Constant
Pool can be lazy. Some classloaders like Spring Boot make use of
this and would pass the NoClassDefFound check during construction,
but fail later.

See #112
@ibauersachs
Copy link
Member

@ibauersachs ibauersachs commented Jun 6, 2020

I did some reading, and the different behavior is allowed according to JLS §12.3. Class loading consists of verification, preparation, resolution, initialization. Resolution can optionally be lazy, i.e. references to other classes like Pointer or Win32Exception can be resolved on demand; after initialization (= static and instance constructor(s)). Spring Boot seems to take adavantage of that option.

I went for a slightly different approach: reference the two required classes in the static constructor. To pass initialization, they must be resolved. This is equivalent to Class.forName, the only difference is avoiding reflection (+ the log statement).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.