Closed penberg closed 8 years ago
I have made some progress on trying to build and run an image with Java 8 compact profile more specifically compact1. As I was testing it I realized that some code in RunJava related java code relies on classes that are NOT part of any compact 1, 2, or 3 profile. More specifically there is dependency on java.beans.
Here is an output from running simple hello world example in Quemu on Mac:
reated instance: capstan-java8-compact-example
WARNING: aio=native was specified for '/Users/wkozaczuk/.capstan/instances/qemu/capstan-java8-compact-example/disk.qcow2', but is not supported in this build. Falling back to aio=threads.
This will become an error condition in future QEMU versions.
WARNING: aio=native was specified for '/Users/wkozaczuk/.capstan/repository/capstan-java8-compact-example/capstan-java8-compact-example.qemu', but is not supported in this build. Falling back to aio=threads.
This will become an error condition in future QEMU versions.
OSv v0.24
eth0: 192.168.122.15
java.so: Failed to load io/osv/RunJava
Exception in thread "main" java.lang.NoClassDefFoundError: java/beans/IntrospectionException
at io.osv.shade.net.sf.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:166)
at io.osv.shade.net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at io.osv.shade.net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
at io.osv.shade.net.sf.cglib.core.KeyFactory$Generator.create(KeyFactory.java:144)
at io.osv.shade.net.sf.cglib.core.KeyFactory.create(KeyFactory.java:116)
at io.osv.shade.net.sf.cglib.core.KeyFactory.create(KeyFactory.java:108)
at io.osv.shade.net.sf.cglib.core.KeyFactory.create(KeyFactory.java:104)
at io.osv.shade.net.sf.cglib.proxy.Enhancer.
Can somebody shed light or point to some wiki page to why there is a need for class loader isolation and dependency on cglib? Maybe all that can be reworked so that RunJava does not depend on java.beans.* and cglib.
Here is some info from the compact profile wiki: "In a few rare cases it may be necessary to subset classes by removing methods, e.g., the addPropertyChangeListener and removePropertyChangeListener methods defined by java.util.logging.LogManager, to avoid spliting packages or referencing types that do not exist.
JMX and the JMX Remote API are proposed for compact3. It may be necessary to update the JMX API to avoid referencing types in java.beans that do not exist. In the case of the JMX Remote API then it may be necessary to downgrade support for the RMI-IIOP protocol to avoiding needing to include CORBA."
@tgrabiec is more familiar with the details here, but to make a long story short: We wanted to be able to run several different Java programs on the same JVM and give those different programs some semblance of running on a different JVM - different class loaders, different loggers, and so on.
This isolation feature was especially important when we had a shell and ssh written in Java running on every OSv instance, which is no longer the case, so if you only plan to run one Java application on OSv, I think your best bet would be to remove this isolation feature - and its depedency on cglib. We could have an alternative version of the RunJava code without isolation (which would more-or-less reverse @tgrabiec 's patches adding isolation), or perhaps it might be possible to have one code which supports both options.
I managed to build custom compact profile1 capstan image and run a "hello world" app. Essentially I created an app directory under apps modeled after apps/openjdk8-fedora and adjusted Makefile to make it pull JRE custom compact profile1 tarballs from my personal S3 bucket. As far as custom compact profile goes I ended up building openjdk-1.8.0.92.b14 custom profile1 and doing following surgery on it:
All that I based on following advise from stackoverflow.com - http://stackoverflow.com/questions/23907728/how-to-use-log4j-in-embedded-jdk-compact-1-2-3
As you can see this hack is not ideal and makes me think what is the right path to move forward with support of compact profiles specifically and JVM (not only Java) apps on OSv platform in general given following:
Here are my questions regarding support of JVM apps on OSv going forward:
It seems there is a lot of complexity that may not be necessary anymore. I am very interested in simplifying that but I need some guidance as far as what to keep under /java and what can be removed. And there are possibly many reasons (optimizations like balooning) which may require some of this complexity going forward. Can @tgrabiec offer some help here?
Also I have found an issue https://github.com/cloudius-systems/osv/issues/623 which may get addressed if we simplify JVM support.
On Wed, Jun 1, 2016 at 11:32 PM, wkozaczuk notifications@github.com wrote:
I managed to build custom compact profile1 capstan image and run a "hello world" app. Essentially I created an app directory under apps modeled after apps/openjdk8-fedora and adjusted Makefile to make it pull JRE custom compact profile1 tarballs from my personal S3 bucket. As far as custom compact profile goes I ended up building openjdk-1.8.0.92.b14 custom profile1 and doing following surgery on it:
- add java.beans.* and com/sun/beans/* from full JRE to lib/rt.jar
- add /com/sun/beans/ to lib/meta-index file
- replace java/util/logging/LogManager.class with version from full JRE so it has required addPropertyChangeListener() method.
All that I based on following advise from stackoverflow.com - http://stackoverflow.com/questions/23907728/how-to-use-log4j-in-embedded-jdk-compact-1-2-3
As you can see this hack is not ideal and makes me think what is the right path to move forward with support of compact profiles specifically and JVM (not only Java) apps on OSv platform in general given following:
- Java 9 is going to make JVM apps modular and possibly more compact
- LogManager.addPropertyChangeListener() is deprecated in Java 8 and will be gone in Java 9
- most modern JVM app (Java, Scala, Clojure, Kotlin) tend to be microservices or at least "one-thing focused apps" which do not required running multiple isolated apps on single JVM
I agree with this sentiment.
Here are my questions regarding support of JVM apps on OSv going forward:
- Should it support the classloader isolation?
I think that if this causes problems, it would be really nice to make this optional, either as a java.so option or a separate java.so binary. I don't know how easy it is to take out the isolation stuff from java.cc and/or RunJava.java. I guess it should be probably be easy (you can use "git blame" and similar to find in which commits these were added, and think how to revert them).
- Is there any reason for any *.java source code to support JVM apps besides classloader isolation?
I didn't understand this question?
- Is there still any reason to treat JRE in special way by having special JVM launcher (java/jvm/java.cc) and other C++ code under /java folder?
No, it should probably be in modules/java (we already have such a directory).
- Could JVM apps be built as simply as other native apps (say apps/redis as example) or there is something special about JVM that requires special support in OSv and that is a reason for /java folder?
No, the main reason is historic reasons: the JVM was our main focus and we always wanted to compile it and test it. This is no longer the case.
It seems there is a lot of complexity that may not be necessary anymore.
I don't know if the "isolation" thing is not necessary anymore: I was never a big fan of it, but some people were. If you could make this part optional, perhaps by choosing between two versions of RunJava.java, that would be great. You can see the git history of what RunJava.java looked like before it acquired the isolation cruft^H^H^H^Hfeature.
I am very interested in simplifying that but I need some guidance as far as
what to keep under /java and what can be removed. And there are possibly many reasons (optimizations like balooning) which may require some of this complexity going forward. Can @tgrabiec https://github.com/tgrabiec offer some help here?
I don't think Ballooning is related to the issues we're discussing here. We should leave that be (and leave its files in java/), it's an orthogonal feature.
Indeed most of the stuff should be moved from java/ to modules/java. There's even a comment about it in the Makefile.
Also I have found an issue #623
https://github.com/cloudius-systems/osv/issues/623 which may get addressed if we simplify JVM support.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/cloudius-systems/osv/issues/497#issuecomment-223115723, or mute the thread https://github.com/notifications/unsubscribe/AAjqIxDTb0o-mv_wcKOyzidmtQF94ZYpks5qHexfgaJpZM4Chz0Z .
I created pull request to address this issue. Please note that I have not had chance yet to find some official compact profile 1,2 or 3 JRE to write a unit test verifying that indeed one can run java app on any of these compact profile.
I built JDK8 compact profiles myself and used for my one manual tests and things look good. Also please note that jolokia plugin/agent relies on JMX which is only part of compact profile 3 and up so probably this part will not work with compact profiles 1 and 2 and I did not verify that.
I am also planning to work on reorganizing java folder and moving it to modules/java and modules/java-tests but that would be part of a different issue which maybe already exists.
Java 8 introduced "compact profiles" which are stripped down versions of the JDK:
http://openjdk.java.net/jeps/161
We should provide an OSv image with one of the compact profiles to reduce the size of JDK-based VM images which currently weight at around 120 MiB. That's insanely large when compared to something like Node.js which has base images just under 20 MiB.