2

Just out of curiosity, I'm trying to setup Eclipse to allow it to compile and run an application with a standard library class being patched.

I have 2 Java projects patch and consumer: one containing a string patch class (with a new method size(), identical to length()) and the other which should use said method size(). The setup is the following one:

- patch
  - bin (contains class files)
  - src
    - java
      - lang
        - String.java

- consumer
  - src
    - consumer
      - Main.java
      - module-info.java

Main.java:

package consumer;
public class Main {
    public static void main(String[] args) {
        String s  = new String("hello");
        System.out.println(s.size());
    }
}

After compiling patch (thereby getting String.class inside patch/bin/java/lang/) I know I can easily use:

java --patch-module java.base=patchpjt/bin/  consumer/src/consumer/Main.java

To correctly call the newly added method size(), obtainining the result of 5.

The problem is that in Eclipse method size is still not recognized (the error is Method Size() is underfined for type String):

enter image description here

So my question is:

  • How to configure Eclipse in order for the project consumer to correctly compile, run and show no errors?
  • How to configure Eclipse in order to show on the Content Assist the method size()?

I know I need to use the Build Path -> Module Path -> Edit Is Modular -> Details Tab -> Patched module but I don't know how to configure both projects.

Some info you might find useful:

  • Eclipse 2018-12 (4.10.0) Build id: 20181214-0600

Thanks for any kind reply.

howlger
  • 31,050
  • 11
  • 59
  • 99
Koldar
  • 1,317
  • 15
  • 35
  • How did you set up your Eclipse project containing the patch? It only works for me when trying to patch non-jdk modules by creating a non-modular project having the dependency to the module I want to patch defined on the classpath, not on the module path. For the JRE, however, Eclipse does not allow me to add it to the classpath... well.. it allows it but then somehow decides to move it by itself to the modulepath again. Hence I always get "The package java.lang conflicts with a package accessible from another module: java.base". Any hints? – Haasip Satang Jan 27 '19 at 19:21

1 Answers1

1

You are using --patch-module for which it is not intended (JEP 261: Module System):

The --patch-module option is intended only for testing and debugging. Its use in production settings is strongly discouraged.

In your scenario, you do not patch an existing method but add an additional method which breaks the API of the system library. Eclipse supports only patching without breaking the API of the system library. The fact that javac does not show any error (probably due to missing checks if the API will not be broken) is a bug in my opinion.

If you create your own JRE, add it in Window > Preferences: Java > Installed JREs and make sure when creating a new Java project to not select an execution environment JRE, but your specific JRE.

howlger
  • 31,050
  • 11
  • 59
  • 99
  • thanks for your reply. I was following something like [this](http://www.zhuwu.me/blog/posts/use-aspectj-to-modify-java-standard-library) to add methods to the library (mainly useful methods in the collections like `List.firstOrFail()`). Since Java11 I wondered with the module system how to achieve this since `rt.jar` does not exist anymore. Care to throw some ideas (or it is simply dumb to do it)? – Koldar Jan 15 '19 at 15:20
  • 1
    I guess the maintenance cost to extend the standard library is too high. Instead, I would place the additional functionality in separate packages whose classes and interfaces extend the standard classes. Similar to what [Eclipse Collections](https://www.eclipse.org/collections/) does, for example. – howlger Jan 15 '19 at 15:45
  • @howlger: While the JEP says it is discouraged, I could not find anything regarding not supported because it breaks the API. Matter of fact I do not even consider adding a new method as breaking the API. Dropping one certainly would be and for me also modifying an existing method and changing the behaviour. So I still do not understand the reason why the above should not be supported for intenteded testing and debugging purposes. Exactly there (when testing) I might want to try out how a new method performs. Or am I missing something? – Haasip Satang Jan 27 '19 at 18:26
  • @HaasipSatang If you add a method to a class of the system library and use it in your application, the application can no longer run with an unmodified JRE. From the system library point of view, the API has been extended, but if the application uses the extension, it no longer sticks to the given API (that's what I meant by _it breaks the API_). If you want to change the system library, you should not mix that with application code. The idea of `--patch-module`, as the name says, is to patch, not to extend the system library. – howlger Jan 27 '19 at 23:15
  • @HaasipSatang [Here is a case where I used `--patch-module` and which works in Eclipse without problems](https://github.com/eclipse/openj9/issues/4113#issuecomment-449937230). Technically, Eclipse simply uses the output folder in `--patch-module` when running the application. – howlger Jan 27 '19 at 23:24
  • @howlger Ok, I see where you are coming from. But let's say you want to achieve what could have easily been done with `-Xbootclasspath/p` in pre JDK 9 releases. So in case you need to migrate a project which _extended_ core classes and relied on thatfunctionality I believe it's ok. In my case the application itself does not know the modified system classes (so no risk of breaking anything here), but the agent delivered together with the modified classes does depend on those modifications. The other alternative would be BCI, more complicated though. – Haasip Satang Jan 27 '19 at 23:54
  • How did you create the the [patch jar](https://github.com/eclipse/openj9/files/2709875/OpenJ9_Issue_4113_Patch.zip) though? While I manage to create projects to patch non-jdk modules (by defining the dependency to the module to be patched on classpath, not on modulepath) I'm having trouble to get Eclipse to allow me to override classes from java.base without complaining that the packge conflicts with a package defined there already. So how did you setup your project to allow overriding `java.net.URLClassLoader`? Note: the problem is not to use the jar, it's to compile it. – Haasip Satang Jan 27 '19 at 23:57
  • 1
    @HaasipSatang In [the dialog that is shown here as second (sub)dialog](https://stackoverflow.com/a/54071487/6505250) I checked _Patches an existing module_ and entered `java.base` to test the fix and to create the patch JAR. IMHO extensions should be developed as separate frameworks outside the system library and not break the encapsulation of an existing module. – howlger Jan 28 '19 at 00:25
  • @howlger: that worked, thanks. Now it is just as in the original question that Eclipse is complaining that it can't resolve the some additional dependencies that I am adding in the patched module. So an error is displayed, code completion can't be used, but resulting code compiles and can be used. Rather strange in my opinion. I would not expect Eclipse more restrictive than the spec. So whatever works with `java` and `javac` should work with Eclipse IMHO. Thanks again. – Haasip Satang Jan 28 '19 at 16:16
  • @HaasipSatang What is right and what is wrong is specified by the [Java Language and Virtual Machine Specifications](https://docs.oracle.com/javase/specs/) and not by the implementation of `java` and `javac` (see [e. g. here](https://stackoverflow.com/a/45801092)). Eclipse itself is based on OSGi and supports in modularization the things that have worked well for more than a decade (e. g. [this "restriction" because of this](https://stackoverflow.com/a/52019047)). Since JPMS cannot be combined with OSGi and since JPMS does not support why there is OSGi, Eclipse itself will remain OSGi-based. – howlger Jan 28 '19 at 17:12
  • @howlger Sorry, now you lost me. Yes, of course I meant the spec, not just the implementation. But coming back to our concrete problem here I could find nowhere in the spec that things that you consider as _breaking API_ (like adding new methods in a patch) are disallowed. We can argue about whether or not that is nice, but to my knowledge it is at least supported, isn't it? So in my opinion Eclipse should **not** mark those patches as "_cannot resolve_", etc, although it actually works and is not prohibited. Or did I miss something in spec or JEP saying this would be a bug? – Haasip Satang Jan 29 '19 at 16:55
  • @HaasipSatang JEP 261 says _"intended only for testing and debugging"_. From my point of view, _testing and debugging_ exclude changing the [Java API](https://docs.oracle.com/en/java/javase/11/docs/api/index.html). Adding a method via `--patch-module` and using it looks like a hack to me. What if the patch contains a class where the inner class has been removed? If you allow adding stuff, removing should also be possible. But I bet this will not work. – howlger Jan 29 '19 at 17:42
  • @howlger yes, you cannot remove things like inner classes. But this was also not possible in the past, meaning pre Java 9. Using the `-Xbootclasspath` option you could prepend or append your own classes but you could not remove what is there already. Why would you need to remove things though? In the end most of such changes are to introduce new behaviour, so if you don't want a certain class to be used anymore you would need to override / patch all it's uses. The _removed_ class would still be available on the classpath though, just unused. So I see no problem. – Haasip Satang Jan 31 '19 at 18:15
  • In the end I am just looking for a way to achieve something similar as I could have done with `-Xbootclasspath/p`. Eg. I want to add a field to a certain class (let's say `java.lang.ClassLoader`) and use it in all of it's subclasses and potentially elsewhere. Did manage to get this patch compiled yet, as the subclasses complain that they do not the the field. Without dependencies of my patched classes between each other everything works (except Eclipse highlighting everything in red). Any idea what needs to be added / configured so the subclasses can see the new fields? – Haasip Satang Jan 31 '19 at 18:20
  • @HaasipSatang Nothing was removed before Java 9, so it was not required, but that has changed. But of course, you can create your customized JRE and add it in _Window > Preferences: Java > Installed JREs_. When creating a new Java project, make sure to choose your customized JRE as _specific JRE_ (instead choosing an _execution environment JRE_). I don't know how to create a custom JRE. [`jlink` does not support `--patch-module`](https://docs.oracle.com/en/java/javase/11/tools/jlink.html), which implies that this is definitely not officially supported. – howlger Feb 01 '19 at 07:30
  • @howlger I don't know how to how to create a custom JRE either other than compiling my own version of the JDK (which seems like a big overhead compared to patching and what was possible in the past).I had some more problems patching java.base classes but this was related to compiler changes done with [JEP280](https://stackoverflow.com/questions/54635667/patching-java-base-results-in-java-lang-linkageerror). The current behaviour in Eclipse for me definitely is a bug, sometimes it compiles (after clean), sometimes not, etc. Opened one [here](https://bugs.eclipse.org/bugs/show_bug.cgi?id=544424) – Haasip Satang Feb 13 '19 at 20:17