0

Let's assume an OpenJDK 11 + OpenJFX 11 based application. The codebase is organized in a single java module (see A Guide to Java 9 Modularity for reference).

Until now I combined all my JavaFX based projects with Google's dependency injection framework Guice. For modular projects, this doesn't seem to work...

SQLiteCompassApplication.java

public class SQLiteCompassApplication extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    public void start(Stage stage) throws Exception {

         Injector injector = Guice.createInjector(new MyModule());

         FXMLLoader fxmlLoader = injector.getInstance(FXMLLoader.class);

         try(InputStream fxmlStream = getClass().getResourceAsStream("/view/View.fxml")) {

            Parent root = fxmlLoader.load(fxmlStream);
            stage.setScene(new Scene(root));
            stage.setTitle("SQLite Compass");
            stage.setOnCloseRequest(event -> System.exit(0));
            stage.show();
        }
    }
}

module-info.java

module org.x1c1b.sqlitecompass {

    requires javafx.controls;
    requires javafx.fxml;
    requires com.google.guice;

    exports org.x1c1b.sqlitecompass;
}

To keep it simple the Guice configuration, which means the declared Guice modules (not to confuse with java modules), isn't pasted but still working for non-modular projects.

When executing this code I receive the following exception:

Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field com.google.inject.Injector org.x1c1b.sqlitecompass.config.provider.FXMLLoaderProvider.injector accessible: module org.x1c1b.sqlitecompass does not "opens org.x1c1b.sqlitecompass.config.provider" to module com.google.guice
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:340)
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:280)
    at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:176)
    at java.base/java.lang.reflect.Field.setAccessible(Field.java:170)
    at [email protected]/com.google.inject.internal.SingleFieldInjector.<init>(SingleFieldInjector.java:38)
    at [email protected]/com.google.inject.internal.MembersInjectorStore.getInjectors(MembersInjectorStore.java:126)
    at [email protected]/com.google.inject.internal.MembersInjectorStore.createWithListeners(MembersInjectorStore.java:93)
    at [email protected]/com.google.inject.internal.MembersInjectorStore.access$000(MembersInjectorStore.java:36)
    at [email protected]/com.google.inject.internal.MembersInjectorStore$1.create(MembersInjectorStore.java:45)
    at [email protected]/com.google.inject.internal.MembersInjectorStore$1.create(MembersInjectorStore.java:41)
    at [email protected]/com.google.inject.internal.FailableCache$1.load(FailableCache.java:40)
    at [email protected]/com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3444)
    at [email protected]/com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2193)
    at [email protected]/com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2152)
    at [email protected]/com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2042)
    ... 39 more
Exception running application org.x1c1b.sqlitecompass.SQLiteCompassApplication

I could fix this issue by pasting opens org.x1c1b.sqlitecompass.config.provider; to module-info.java. But this results in further exceptions...

How should I configure my module/module-info.java to use it with Google Guice in general? Is it possible to use Google Guice and Modularity in combination?

EDIT

It seems to work when I change the following lines:

  • Add exports org.x1c1b.sqlitecompass.config, where my Guice modules are declared, to module-info.java
  • Execute the program with the additional VM option --add-opens java.base/java.lang=com.google.guice

But I would guess it's more a workaround found here than an official solution...

5
  • But this results in further exceptions.. similar to the one stated? Also, the code in question differs from the exception I believe since the absence of a field org.x1c1b.sqlitecompass.config.provider.FXMLLoaderProvider.injector. Apart from which, it seems like something that needs to be fixed at guice end as well for the use of Field.setAccessible. Worth reporting to their issues. (you might find one already reported) Commented Nov 6, 2019 at 15:31
  • Refer to this comment on GitHub, which seems quite related to your issue. Commented Nov 6, 2019 at 15:44
  • @Naman Means it's an internal error right? I can't do very much... Commented Nov 6, 2019 at 16:09
  • I meant it still is not really clear from the question if this can be resolved at your end, for e.g. the tweak of adding --add-opens... shall still work. But until what extend really could be determined by the code. Commented Nov 6, 2019 at 16:15
  • What kind of further exceptions? Does guice require access to classes in other packages of yours? (If it requires access to javafx classes, this seems nothing that you've got control over unless you want to add options to the jvm/compiler.) Furthermore I recommend limiting the access to guice; otherwise everyone could use reflection on your classes. (opens org.x1c1b.sqlitecompass.config.provider to com.google.guice;) Commented Nov 6, 2019 at 17:13

1 Answer 1

1

You have to open all your packages which will be accessed via reflection by Guice. The easiest way to do that is to just declare your whole module as open by putting this word in front of module in your module-info.java.

Sign up to request clarification or add additional context in comments.

4 Comments

Okay seems to be logical... But if I do this, I receive similar error module java.base does not "opens java.lang"
Opening the complete module is slightly risk bearing in terms of proving access to the complete module packages and its member in my opinion.
@0x1C1B Unfortunately, I believe the only way to "fix" that error yourself is to pass --add-opens java.base/java.lang=com.google.guice as a VM argument. You'll have to do that for every package in the JDK which Guice attempts to reflectively access but can't.
If you find yourself opening java.base/java.lang to Guice then something is amiss. Is this because it is trying to hack into the non-public ClassLoader.defineClass method? Hopefully Guice will move to using Lookup.defineClass as that is the only supported way to inject a class into an existing run-time package.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.