Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 java-module. 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.

like image 764
Naman Avatar asked Sep 21 '17 19:09

Naman


1 Answers

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);
}
like image 122
Nikhil Nanivadekar Avatar answered Oct 20 '22 15:10

Nikhil Nanivadekar