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 com.google.guice@4.2.2/com.google.inject.internal.SingleFieldInjector.<init>(SingleFieldInjector.java:38)
    at com.google.guice@4.2.2/com.google.inject.internal.MembersInjectorStore.getInjectors(MembersInjectorStore.java:126)
    at com.google.guice@4.2.2/com.google.inject.internal.MembersInjectorStore.createWithListeners(MembersInjectorStore.java:93)
    at com.google.guice@4.2.2/com.google.inject.internal.MembersInjectorStore.access$000(MembersInjectorStore.java:36)
    at com.google.guice@4.2.2/com.google.inject.internal.MembersInjectorStore$1.create(MembersInjectorStore.java:45)
    at com.google.guice@4.2.2/com.google.inject.internal.MembersInjectorStore$1.create(MembersInjectorStore.java:41)
    at com.google.guice@4.2.2/com.google.inject.internal.FailableCache$1.load(FailableCache.java:40)
    at com.google.common@25.1-android/com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3444)
    at com.google.common@25.1-android/com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2193)
    at com.google.common@25.1-android/com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2152)
    at com.google.common@25.1-android/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...

Naman
  • 27,789
  • 26
  • 218
  • 353
0x1C1B
  • 1,204
  • 11
  • 40
  • *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](https://github.com/google/guice/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc). (you might find one already reported) – Naman Nov 06 '19 at 15:31
  • Refer to [this comment](https://github.com/google/guice/issues/1133#issuecomment-461104026) on GitHub, which seems quite related to your issue. – Naman Nov 06 '19 at 15:44
  • @Naman Means it's an internal error right? I can't do very much... – 0x1C1B Nov 06 '19 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. – Naman Nov 06 '19 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;`) – fabian Nov 06 '19 at 17:13

1 Answers1

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.

mipa
  • 10,369
  • 2
  • 16
  • 35
  • Okay seems to be logical... But if I do this, I receive similar error `module java.base does not "opens java.lang"` – 0x1C1B Nov 06 '19 at 15:18
  • Opening the complete module is slightly risk bearing in terms of proving access to the complete module packages and its member in my opinion. – Naman Nov 06 '19 at 16:23
  • @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. – Slaw Nov 06 '19 at 16:36
  • 2
    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. – Alan Bateman Nov 07 '19 at 08:14