Incompatible types, equality constraints and method not found during Java 9 Migration
Asked Answered
E

1

14

While migrating one of our projects to Java 9(build 9+181), I am facing a peculiar problem what looks like an incorrect implementation in some library in use related to type inference and . I am using a dropwizard-core(1.1.0) and guice(4.1.0) configurations as follows:

public class CustomService extends io.dropwizard.Application<CustomServiceConfig> {

    public static void main(String[] args) throws Exception {
        new CustomService().run(args);
    }

    // other initializations

    @Override
    public void run(CustomServiceConfig config, io.dropwizard.setup.Environment environment) throws Exception {
        com.google.inject.Injector injector = createInjector(config, environment);
        environment.jersey().register(injector.getInstance(SomeResource.class)); //line 45
        environment.healthChecks().register("DBHealth", injector.getInstance(HealthCheck.class)); 
        environment.servlets().addFilter("Filter-Name", SomeFilter.class)
                .addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
    }


    private com.google.inject.Injector createInjector(CustomServiceConfig config, Environment environment) {
        return com.google.inject.Guice.createInjector(new CustomServiceModule(config, environment));
    }

}

public class CustomServiceModule extends com.google.inject.AbstractModule {
    private final CustomServiceConfig serviceConfig;
    private final Environment environment;

    public CustomServiceModule(CustomServiceConfig serviceConfig, Environment environment) {
        this.serviceConfig = serviceConfig;
        this.environment = environment;
    }

    @Override
    protected void configure() {
        bind(SomeInterface.class).to(SomeInterfaceImpl.class);
        ..
    }
}

The configuration works fine for me with the following combination :

  • Java 8 + Maven 3 + Compiler Plugin 3.6.1 [Our original setup]
  • Java 9 + Maven 3 + Compiler Plugin 3.7.0 (command line and maven configs updated)

But as I switch to the module structure and then try and compile the maven module consisting of those classes, I get these errors during mvn clean install :

[ERROR] ../service/service/CustomService.java:[45,29] incompatible types: inference variable T has incompatible bounds
    equality constraints: base.SomeResource
    upper bounds: java.lang.Class<?>,java.lang.Object

[ERROR] ../service/service/CustomService.java:[56,31]
no suitable method found for
addFilter(java.lang.String,java.lang.Class< SomeFilter >)
[ERROR]     method io.dropwizard.jetty.setup.ServletEnvironment.addFilter(java.lang.String,javax.servlet.Filter)
is not applicable
[ERROR]       (argument mismatch; java.lang.Class< SomeFilter > cannot be
converted to javax.servlet.Filter)

I am not sure why these errors occurred and if they could be related to the module structure in use.

Q1. Is there any type-inference related change that can impact the maven compilation with module structure changes(if this is possible) keeping in mind the dependencies used?

Also while migrating, I have mostly used the automatic module names as suggested by IntelliJ to construct the module-info as :

module service {
//    Internal modules  which compile successfully
    requires model;
    requires util;

//    Dependent library modules
    requires httpcore;
    requires guice;
    requires guava;
    requires dropwizard.core;
    requires mongo.java.driver;
    requires org.apache.commons.lang3;
    requires javax.servlet.api;
}

Q2. If this is not a regression issue while migrating, why did this not fail with our previous setup? Does this also mandate a code change in our service or shall we wait for libraries to move to modules if that might help?

Note: Have tried to look into the usage of the dependency versions and class implementation. They are same in both the previous and current setup.

Do let me know for any further information I could help with.


Update: I was able to reproduce the same in a microservice sample created by me to isolate it from the rest of my project.

Emulsify answered 21/9, 2017 at 19:48 Comment(8)
I can't see how modules would effect generics in any way compiler-wise; that might simply be a java-9 regression here. can you isolate this problem via a simpler test may be?Welbie
@Welbie Would take some time and come with an isolated project if this can actually be a regression somewhere.Emulsify
Type Inference* not reference.Frumenty
@JornVernee :) Thanks, corrected.Emulsify
when I said a test to isolate a problem, what I actually meant is something much smaller, like a @Test method or the like. I don't think you can open a jdk ticket with this...Welbie
@Welbie Ohk, I get that now. Would update the question in some time for sure.Emulsify
no worries, plz tag me me in when u do that. I really don't know then answer to this, but gladly will track it. thank youWelbie
@Welbie the answer works. Its a transitive dependency of dropwizard-core. Though IMHO, the error is quite misleading and I am not sure if compiler-plugin shall report better.Emulsify
M
12

From the sample project I was able to fix the compilation issue. There were 2 exceptions in com.SomeService#run method. There were missing modules in your module-info.java, once you add these the code should compile.

requires dropwizard.jersey;
requires dropwizard.jetty;

JerseyEnvironment comes from io.dropwizard:dropwizard-jersey:1.1.0

ServletEnvironment comes from io.dropwizard:dropwizard-jetty:1.1.0

Since they are different jars, they export different modules. Hence, the requires needs to be explicitly added. Your code works fine without module-info.java because at that time module system is not used.

I found the fix by doing the below, method mentioned in comments:

@Override
public void run(SomeServiceConfig config, Environment environment) throws Exception {
    Injector injector = Guice.createInjector(new SomeServiceModule());
    // Fix: Extract to variable to find Type of jersey and then find the module to add under requires
    JerseyEnvironment jersey = environment.jersey(); 
    jersey.register(injector.getInstance(SomeResource.class));
    // Fix 2: Same method as Fix 1
    ServletEnvironment servlets = environment.servlets();
    servlets.addFilter("Some-Filter", SomeFilter.class);
}
Met answered 25/9, 2017 at 2:9 Comment(5)
Why would the requires be needed? I have used the automatic module named dropwizard.core for a corresponding maven dependency on dropwizard-core:1.1.0.Emulsify
JerseyEnvironment comes from io.dropwizard:dropwizard-jersey:1.1.0 ServletEnvironment comes from io.dropwizard:dropwizard-jetty:1.1.0 Since they are different jars, they export different modules. Hence, the requires needs to be explicitly added. Remember in Java 9 you cannot infer module dependency, you have mention it explicitly.Met
Ya got it.Transitive dependency and that wouldn't work similarily in the modular structure. Also, the compilation error does not report this. Its kind of misleading when it says incompatible types instead of failing with not found. Any ideas over that?Emulsify
The error is misleading indeed. However, in my past experience making [Eclipse Collections][1] Java 9 compatible, the exceptions and errors were often misleading, it took a bit of trial and error for me at that time. So, kind of now know what are the few first steps to take while debugging. Note: I am a committer for Eclipse Collections [1]: github.com/eclipse/eclipse-collectionsMet
Thank you. Though your answer(would accept this one) resolves the part of the compilation. Optionally you can update the details over the transitive dependency in the answer to make it worth more. I would still wait for an explanation(if) on for such error message. Probably someone from apache-maven team might help there. Holding back the bounty for sometime. :)Emulsify

© 2022 - 2024 — McMap. All rights reserved.