Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Escaping The Jar Hell With Jigsaw Layers

Escaping The Jar Hell With Jigsaw Layers

Jigsaw modules are often criticized for the lack of versioning that exists in alternative module systems for Java, such as OSGi. One of the main purposes of versioning is to solve the so called Jar Hell problem that arises when your application depends on two different versions of the same library. While Jigsaw is able to detect this conflicting situation, it won’t allow you to load both versions that are placed on the module path. However, it is not always possible to eliminate all version conflicts from your application, because other versions of the same libraries can come to your application indirectly via dependencies that you do not control. Fortunately, there is a native solution for the problem in Jigsaw called Layers. In this session, I will show what problems Jigsaw could have introduced if it had explicit versions for modules, and how Jigsaw Layers in conjunction with Jigsaw Services help to solve the Jar Hell problem safely.

E071ddb92cfd60762d93c1a625c6d654?s=128

Nikita Lipsky

November 24, 2018
Tweet

Transcript

  1. Escaping The Jar Hell With Jigsaw Layers Nikita Lipsky Excelsior

    LLC
  2. Jigsaw and versioning 2

  3. Excelsior JET? •  AOT-centric Java SE implementation –  certified as

    Java Compatible since 2005 •  AOT compiler + Java Runtime –  mixed compilation: AOT + JIT –  AOT support for custom classloaders (Eclipse RCP, Tomcat, Spring Boot) •  Toolkit –  Startup Optimizer –  Deployment 3
  4. Nikita Lipsky •  20+ years in software development •  Excelsior

    JET project initiator –  18+ years of contributions –  compiler engineer –  product department lead –  etc. •  Twitter: @pjBooms •  Team blog: https://www.excelsiorjet.com/blog 4
  5. Agenda •  Very short introduction to Jigsaw •  Jar Hell

    problem •  Versioning as a solution to Jar Hell problem •  Problems with versioning •  Jigsaw Layers •  Jar Hell resolution using Jigsaw Layers •  Demo 5
  6. Модульная система OSGi 6

  7. Module example // src/java.sql/module-info.java module java.sql { requires transitive java.logging;

    requires transitive java.xml; exports java.sql; exports javax.sql; exports javax.transaction.xa; uses java.sql.Driver; } 7
  8. Java Platform Module System Jigsaw module •  imports: –  modules:

    requires –  services: uses •  exports: –  packages: exports –  servicies: provides SomeService with ServiceImpl 8
  9. Java Platform Module System Module A Class A Module B

    Class B Module C Class C exports packageC; requires: A exports packageA; I requires A; requires C; 9
  10. Jar Hell 10

  11. Jar/Classpath Hell App 1.0 Library Foo 2.0 Library Bar 3.0

    Library Baz 2.1 Library Baz 3.1 11
  12. Jar/Classpath Hell App 1.0 Library Foo 2.0 Library Bar 3.0

    Library Baz 2.1 Library Baz 3.1 12
  13. Jar/Classpath Hell App 1.0 Library Foo 2.0 Library Bar 3.0

    Library Baz 2.1 Library Baz 3.1 13
  14. Jar/Classpath Hell App 1.0 Library Foo 2.0 Library Bar 3.0

    Library Baz 2.1 Library Baz 3.1 14
  15. Jar/Classpath Hell App 1.0 Library Foo 2.0 Library Bar 3.0

    Library Baz 2.1 Library Baz 3.1 15
  16. Jar/Classpath Hell App 1.0 Library Foo 2.0 Library Bar 3.0

    Library Baz 2.1 Library Baz 3.1 16
  17. Versioning •  Jigsaw module might have a version in the

    first drafts of Jigsaw to solve Jar Hell problem – import/export was qualified by version – If two modules require a library module of two different versions, both versions will be loaded 17
  18. Versioning However versioning was removed later … 18

  19. Versioning However versioning was removed later … Why? 19

  20. Versioning Question: How to implement versioning? Task: For given A

    module importing Lib (v1) library, and B module importing Lib (v2), it is required that both versions of Lib working without conflicts. Solution: load the versions of the library by different classloaders. 20
  21. Versioning Question: How to implement versioning? Task: For a given

    module A importing library Lib (v1), and module B importing Lib (v2), it is required that both versions of Lib work without conflicts. Solution: load the versions of the library by different classloaders. 21
  22. Versioning Question: How to implement versioning? Task: For a given

    module A importing library Lib (v1), and module B importing Lib (v2), it is required that both versions of Lib work without conflicts. Solution: load versions of the library by different classloaders. 22
  23. Versioning •  ClassLoader forms a unique class namespace •  If

    each module is loaded by its own classloader then there will be no conflicts with classes of other modules 23
  24. Versioning com.foo App 1.0 com.foo parse-api 2.0 com.foo persist-api 3.0

    org.apache commons 2.1 org.apache commons 3.1 CL1 CL2 CL3 CL4 CL5 Different versions of apache commons may simultaneously present in the JVM 24
  25. Versioning problems 25

  26. Versioning Versioning immediately means: 1 module ßà 1 classloader 26

  27. Versioning Versioning immediately means: 1 module ßà 1 classloader It

    was exactly so in the first Jigsaw drafts! 27
  28. Jigsaw and classloaders Jigsaw is not just modules, but also

    the Java SE platform split into modules. 28
  29. Jigsaw and classloaders Backward compatibility problems: According to the specification

    getClassloader() == null for core platform classes.то противоречит That precludes splitting of the platform into modules, with each module loaded by its loader 29
  30. Jigsaw and classloaders Backward compatibility problems: According to the specification

    getClassloader() == null for core platform classes.то противоречит It precludes splitting of the platform into modules, with each module loaded by its loader 30
  31. Jigsaw and classloaders Backward compatibility problems: Many classloaders for an

    application may also cause problems: – classloaders delegation Ф 31
  32. Jigsaw and classloaders Backward compatibility problems: Even the fact that

    Application (system) Classloader does not extend URLClassloader anymore causes problems Ф 32
  33. 33

  34. Versioning? App 1.0 Foo 2.0 Bar 3.0 Baz 2.1 Baz

    3.1 34
  35. Versioning? App 1.0 Foo 2.0 Bar 3.0 Baz 2.1 Baz

    3.1 A 35
  36. Versioning? App 1.0 Foo 2.0 Bar 3.0 Baz 2.1 Baz

    3.1 A A 36
  37. Versioning? App 1.0 Foo 2.0 Bar 3.0 Baz 2.1 Baz

    3.1 A A 37
  38. Versioning? App 1.0 Foo 2.0 Bar 3.0 Baz 2.1 Baz

    3.1 A A 38
  39. Versioning? App 1.0 Foo 2.0 Bar 3.0 Baz 2.1 Baz

    3.1 A A 39
  40. Versioning? App 1.0 Foo 2.0 Bar 3.0 Baz 2.1 Baz

    3.1 A A 40
  41. Versioning? App 1.0 Foo 2.0 Bar 3.0 Baz 2.1 Baz

    3.1 A A I’m “A”! 41
  42. Versioning? App 1.0 Foo 2.0 Bar 3.0 Baz 2.1 Baz

    3.1 A No, I’m “A”! A I’m “A”! 42
  43. App 1.0 A Versioning? Foo 2.0 Bar 3.0 Baz 2.1

    Baz 3.1 A AAAAA! AAAAA! 43
  44. None
  45. Loading constraints •  Loading constraints prohibit two classes with the

    same fully qualified name to appear in the namespace of another class 45
  46. Loading constraints B.java: class B { T1 f1 = A.f;

    int f2 = A.m(t2); } A.java: class A { static T1 f; static int m(T2 t) If B is loaded by L1 classloader and A is loaded by L2 then the JVM will check that (T1, L1) = (T1, L2) and (T2, L1) = (T2, L2) == 46
  47. Versioning? •  JVM throws java.lang.LinkageError on loading constraints violation 47

  48. Versioning? Conclusion: versioning does not solve JAR Hell but raises

    it to a new, more sophisticated level. 48
  49. Versioning Another detail: import in early Jigsaw versions was qualified

    by not a single version but by а version range: –  A module may declare that it can work with a dependency of “from” version to “to” version 49
  50. Versioning // src/module-info.java module my.module@1.0 { //1.2.3.1 <= lib.module version

    < 4.0 requires lib.module@1.[2.3.1+];2*;3*; } 50
  51. Versioning Problem 3: Resolving dependencies (wiring modules) from version ranges

    is an NP- complete problem! –  Reduced to 3-SAT 51
  52. Versioning … after that versioning in JPMS breathed its last.

    52
  53. Versioning … after that versioning in JPMS breathed its last.

    No versioning – no classloaders for modules. 53
  54. Jar Hell But how to solve the Jar Hell now?

    App 1.0 Foo 2.0 Bar 3.0 Baz 2.1 Baz 3.1 54
  55. Jar Hell Jigsaw simply prohibits this situation! App 1.0 Foo

    2.0 Bar 3.0 Baz 2.1 Baz 3.1 55
  56. Jar Hell Stop! Jigsaw does not have versions! App 1.0

    Foo 2.0 Bar 3.0 Baz 2.1 Baz 3.1 56
  57. Jar Hell Stop! Jigsaw does not have versions! The most

    probably two different versions of the same library have same packages. 57
  58. Jar Hell Stop! Jigsaw does not have versions! The most

    probably two different versions of the same library have same packages. Jigsaw prohibits for two modules to have same packages (split packages) 58
  59. Jar Hell But how to solve the Jar Hell problem

    after all? App 1.0 Foo 2.0 Bar 3.0 Baz 2.1 Baz 3.1 59
  60. Jar Hell A possible solution: App 1.0 Foo 2.0 Bar

    3.0 Baz 3.1 60
  61. Jar Hell A possible solution: App 1.0 Foo 2.5 Bar

    3.0 Baz 3.1 61
  62. 62

  63. I always dreamed of solving NP-complete problems! 63

  64. Jar Hell Another solution: relocate classes of two versions of

    a library to different Java packages (Maven Shade plugin) 64
  65. Jar Hell Another solution: relocate classes of two versions of

    a library to different Java packages (Maven Shade plugin) It may not work because of reflection. 65
  66. Jar Hell Third solution: employ classloaders App 1.0 Foo 2.0

    Bar 3.0 Baz 2.1 Baz 3.1 66 CL1 CL2
  67. Jar Hell Third solution: employ classloaders Be ready to fight

    with classloaders constraints violation problem 67
  68. Jigsaw Layers *

  69. Jigsaw Layers * Fourth solution: employ Jigsaw Layers 69

  70. Jigsaw Layers * •  Local module system •  Split packages

    are prohibited inside one layer •  Two modules containing the same package have to belong to different layers 70
  71. Jigsaw Layers * * The picture is from Alex Buckley’s

    presentation: Project Jigsaw Under the hood 71
  72. Jigsaw Layers * •  Jigsaw layers are implemented …через загрузчики

    классов. •  У каждого слоя по крайней мере один свой загрузчик (может быть несколько) •  Но при этом невозможно по построению нарушение classloader constraints! 72
  73. Jigsaw Layers * •  Jigsaw layers are implemented via classloaders!ерез

    загрузчики классов. •  У каждого слоя по крайней мере один свой загрузчик (может быть несколько) •  Но при этом невозможно по построению нарушение classloader constraints! 73
  74. Jigsaw Layers * •  Jigsaw layers are implemented via classloaders!

    •  Each layer has at least one unique classloader (may have several classloaders) 74
  75. Jigsaw Layers * •  Jigsaw layers are implemented via classloaders!

    •  Each layer has at least one unique classloader (may have several classloaders) •  However, classloader constraints are impossible to violate by construction! 75
  76. Jigsaw Layers Jar Hell Resolution App 1.0 Library Foo 2.0

    Library Bar 3.0 Library Baz 2.1 Library Baz 3.1 76
  77. Jigsaw Layers Jar Hell Resolution App 1.0 Library Foo 2.0

    Library Bar 3.0 Library Baz 2.1 Library Baz 3.1 Boot Layer 77
  78. Jigsaw Layers Jar Hell Resolution App 1.0 Library Foo 2.0

    Library Bar 3.0 Library Baz 2.1 Library Baz 3.1 Layer 2 Boot Layer 78
  79. Layer 3 Jigsaw Layers Jar Hell Resolution App 1.0 Library

    Foo 2.0 Library Bar 3.0 Library Baz 2.1 Library Baz 3.1 Layer 2 Boot Layer 79
  80. Jigsaw Layers Jar Hell Resolution App 1.0 Library Foo 2.0

    Library Bar 3.0 Library Baz 2.1 Library Baz 3.1 80
  81. Layer 3 Jigsaw Layers Jar Hell Resolution App 1.0 Library

    Foo 2.0 Library Bar 3.0 Library Baz 2.1 Library Baz 3.1 Layer 2 Boot Layer 81
  82. Layer 3 Jigsaw Layers Jar Hell Resolution App 1.0 Library

    Foo 2.0 Library Bar 3.0 Library Baz 2.1 Library Baz 3.1 Layer 2 Boot Layer 82
  83. Jigsaw Layers •  Every layer has a parent layer • 

    Modules of a child layer can import modules of parent layers •  However parent layer modules cannot import child layers modules! •  That’s why it’s impossible to violate classloaders constraints! 83
  84. Jigsaw Layers •  Every layer has a parent layer • 

    Modules of a child layer can import modules of parent layers •  However parent layer modules cannot import child layers modules! •  That’s why it’s impossible to violate classloaders constraints! 84
  85. Jigsaw Layers •  Every layer has a parent layer • 

    Modules of a child layer can import modules of parent layers •  However, parent layer modules cannot import child layers modules! •  That’s why it’s impossible to violate classloaders constraints! 85
  86. Jigsaw Layers •  Every layer has a parent layer • 

    Modules of a child layer can import modules of parent layers •  However, parent layer modules cannot import child layers modules! •  That’s why it’s impossible to violate classloaders constraints! 86
  87. Layer 3 Jigsaw Layers Jar Hell Resolution App 1.0 Library

    Foo 2.0 Library Bar 3.0 Library Baz 2.1 Library Baz 3.1 Layer 2 Boot Layer A A 87
  88. Layer 3 Jigsaw Layers Jar Hell Resolution App 1.0 Library

    Foo 2.0 Library Bar 3.0 Library Baz 2.1 Library Baz 3.1 Layer 2 A A Boot Layer 88
  89. Layer 3 Jigsaw Layers Jar Hell Resolution App 1.0 Library

    Foo 2.0 Library Bar 3.0 Library Baz 2.1 Library Baz 3.1 Layer 2 A A Boot Layer 89
  90. Layer 3 Jigsaw Layers Jar Hell Resolution App 1.0 Library

    Foo 2.0 Library Bar 3.0 Library Baz 2.1 Library Baz 3.1 Layer 2 A A Boot Layer There is a space between us A place that I can’t go 90
  91. Jigsaw Layers Question: if application modules cannot import child layers

    modules then how they can employ functionality implemented in these child layers? 91
  92. 92

  93. Jigsaw Layers Question: if application modules cannot import child layers

    modules then how they can employ functionality implemented in these child layers? 93
  94. Jigsaw Layers Question: if application modules cannot import child layers

    modules then how they can employ functionality implemented in these child layers? Answer: employ IoC design pattern via Jigsaw Services: child layers can provide services to modules of parent layers 94
  95. Jigsaw Layers Question: if application modules cannot import child layers

    modules then how they can employ functionality implemented in these child layers? Answer: employ IoC design pattern via Jigsaw Services: child layers can provide services to modules of parent layers 95
  96. Layer 3 Jigsaw Layers Jar Hell Resolution App 1.0 Library

    Foo 2.0 Library Bar 3.0 Library Baz 2.1 Library Baz 3.1 Layer 2 Boot Layer 96
  97. Layer 3 Jigsaw Layers Jar Hell Resolution App 1.0 Foo

    2.0 Bar 3.0 Baz 2.1 Baz 3.1 Layer 2 App Service Provider 1 App Service Provider 2 Boot Layer 97
  98. 98

  99. How to use var finder = ModuleFinder.of(Paths.get(“MyLayer”)); var parent =

    ModuleLayer.boot(); Configuration cf = parent.configuration().resolve( finder, ModuleFinder.of(), Set.of(“myRootMod")); ModuleLayer myL = parent.defineModulesWithOneLoader( cf, ClassLoader.getSystemClassLoader()); 99
  100. How to use var finder = ModuleFinder.of(Paths.get(“MyLayer”)); var parent =

    ModuleLayer.boot(); Configuration cf = parent.configuration().resolve( finder, ModuleFinder.of(), Set.of(“myRootMod")); ModuleLayer myL = parent.defineModulesWithOneLoader( cf, ClassLoader.getSystemClassLoader()); 100
  101. How to use var finder = ModuleFinder.of(Paths.get(“MyLayer”)); var parent =

    ModuleLayer.boot(); Configuration cf = parent.configuration().resolve( finder, ModuleFinder.of(), Set.of(“myRootMod")); ModuleLayer myL = parent.defineModulesWithOneLoader( cf, ClassLoader.getSystemClassLoader()); 101
  102. How to use var finder = ModuleFinder.of(Paths.get(“MyLayer”)); var parent =

    ModuleLayer.boot(); Configuration cf = parent.configuration().resolve( finder, ModuleFinder.of(), Set.of(“myRootMod")); ModuleLayer myL = parent.defineModulesWithOneLoader( cf, ClassLoader.getSystemClassLoader()); 102
  103. How to use var finder = ModuleFinder.of(Paths.get(“MyLayer”)); var parent =

    ModuleLayer.boot(); Configuration cf = parent.configuration().resolve( finder, ModuleFinder.of(), Set.of(“myRootMod")); ModuleLayer myL = parent.defineModulesWithOneLoader( cf, ClassLoader.getSystemClassLoader()); var services = ServiceLoader.load(myL, MySrv.class); 103
  104. Demo https://github.com/pjBooms/Jigsaw-Layers- Example 104

  105. Where else we can use layers Problem: the servlet container

    standard knows nothing about modules so far –  Dependencies in war files are old plain classpath in fact! Solution: it’s possible to fork your favorite app server, adding a Jigsaw layer into its web application loader –  example: 105
  106. Jigsaw Mantra Reliable Configuration Strong Encapsulation 106

  107. Where else we can use layers Problem: the servlet container

    standard knows nothing about modules so far –  Dependencies in war files are old plain classpath in fact! Solution: it’s possible to fork your favorite app server, adding a Jigsaw layer into its web application loader –  example: 107
  108. Where else we can use layers Problem: the servlet container

    standard knows nothing about modules so far –  Dependencies in war files are old plain classpath in fact! Solution: it’s possible to fork your favorite app server, adding a Jigsaw layer into its web application loader –  example: https://github.com/pjBooms/tomcat 108
  109. Tomcat •  Tomcat classpath contains bootstrap.jar •  Tomcat is loaded

    by its own classloader (org.apache.catalina.startup.ClassLoaderFactory) •  Each web application is loaded by its own classloader (org.apache.catalina.loader.WebappClassLoaderBase) 109
  110. Tomcat •  Any classloader has its own Unnamed module • 

    Without special efforts from a classloader implementer, classes loaded by a classloader would belong to its Unnamed module – even if сlasses formally belong to a named module (to a jar with module-info.class) 110
  111. Modular War Example Example (https://github.com/pjBooms/modular-war-example): –  2 modules: helloworld-provider, helloworld-web-app

    –  helloworld-web-app outputs module names of loaded classes on a web page: "<H3>HelloServlet module: " + HelloServlet.class.getModule().getName() + "</H3>\n" + "<H3>HelloWorldProvider module: " + HelloWorldProvider.class.getModule().getName() + "</H3>\n" + 111
  112. Modular War Example Example (https://github.com/pjBooms/modular-war-example): –  2 modules: helloworld-provider, helloworld-web-app

    –  helloworld-web-app outputs module names of loaded classes on a web page: "<H3>HelloServlet module: " + HelloServlet.class.getModule().getName() + "</H3>\n" + "<H3>HelloWorldProvider module: " + HelloWorldProvider.class.getModule().getName() + "</H3>\n" + 112
  113. Modular War Example If the example is launched on a

    regular Tomcat: 113
  114. Modular War Example On Tomcat fork (https://github.com/pjBooms/tomcat): 114

  115. Tomcat fork ClassLoaderFactory.createClassLoader modification: instead of return new URLClassLoader(array); modular

    layer creation: ModuleFinder finder = ModuleFinder.of(array); … ModuleLayer moduleLayer = parentLayer.defineModulesWithOneLoader(cf, parent); return moduleLayer.findLoader(moduleNames.get(0)); 115
  116. Tomcat fork ClassLoaderFactory.createClassLoader modification: instead of return new URLClassLoader(array); modular

    layer creation: ModuleFinder finder = ModuleFinder.of(array); … ModuleLayer moduleLayer = parentLayer.defineModulesWithOneLoader(cf, parent); return moduleLayer.findLoader(moduleNames.get(0)); 116
  117. Tomcat fork ClassLoaderFactory.createClassLoader modification: instead of return new URLClassLoader(array); modular

    layer creation: ModuleFinder finder = ModuleFinder.of(array); … ModuleLayer moduleLayer = parentLayer.defineModulesWithOneLoader(cf, parent); return moduleLayer.findLoader(moduleNames.get(0)); 117
  118. Tomcat fork WebAppClassloaderBase.start() modification modular layer creation: WebResource modules =

    resources.getResource("/WEB-INF/modules"); if (modules.exists()) { ModuleFinder finder = ModuleFinder.of(Paths.get(modules)); ModuleLayer parent = Catalina.class.getModule().getLayer(); … ModuleLayer moduleLayer = parent.defineModulesWithOneLoader(cf, getParent()); 118
  119. Tomcat fork WebAppClassloaderBase.start() modification modular layer creation: WebResource modules =

    resources.getResource("/WEB-INF/modules"); if (modules.exists()) { ModuleFinder finder = ModuleFinder.of(Paths.get(modules)); ModuleLayer parent = Catalina.class.getModule().getLayer(); … ModuleLayer moduleLayer = parent.defineModulesWithOneLoader(cf, getParent()); 119
  120. Tomcat fork WebAppClassloaderBase.start() modification modular layer creation: WebResource modules =

    resources.getResource("/WEB-INF/modules"); if (modules.exists()) { ModuleFinder finder = ModuleFinder.of(Paths.get(modules)); ModuleLayer parent = Catalina.class.getModule().getLayer(); … ModuleLayer moduleLayer = parent.defineModulesWithOneLoader(cf, getParent()); 120
  121. Tomcat fork WebAppClassloaderBase.loadClass() modification The layer is used to load

    web application classes: if (moduleLayerLoader != null) { try { return moduleLayerLoader.loadClass(name); } catch (ClassNotFoundException e) { // Ignore } } 121
  122. Modular War Example Tomcat fork (https://github.com/pjBooms/tomcat): 122

  123. Jigsaw Mantra Reliable Configuration Strong Encapsulation 123

  124. What about Spring Boot? •  Classes of Spring Boot executable

    Jar are loaded by a Spring Boot classloader •  The classloader knows nothing about modules •  Hence there are no modules at runtime for Spring Boot applications 124
  125. What about Spring Boot? •  Classes of Spring Boot executable

    Jar are loaded by a Spring Boot classloader •  The classloader knows nothing about modules •  Hence there are no modules at runtime for Spring Boot applications 125
  126. What about Spring Boot? •  Classes of Spring Boot executable

    Jar are loaded by a Spring Boot classloader •  The classloader knows nothing about modules •  Hence there are no modules at runtime for Spring Boot applications 126
  127. What about Spring Boot? •  Classes of Spring Boot executable

    Jar are loaded by a Spring Boot classloader •  The classloader knows nothing about modules •  Hence there are no modules at runtime for Spring Boot applications 127
  128. What about Spring Boot? Good news: Spring Boot applications can

    run without Spring Boot classloader – with all their dependencies put on classpath It is possible to modularize Spring Boot applications using standard migration path! 128
  129. What about Spring Boot? Good news: Spring Boot applications can

    run without Spring Boot classloader – with all their dependencies put on classpath It is possible to modularize Spring Boot applications using standard migration path! 129
  130. What about Spring Boot? Exercise: add a modular layer to

    the Spring Boot classloader. 130
  131. Conclusion Versions for modules look like a good idea but

    there are many problems with them: – backward compatibility – unsafety (classloaders constraints) – NP-completeness on dependencies resolution 131
  132. Conclusion Jigsaw Layers allow to solve the Jar Hell problem

    safely: – no classloaders constraints violations by construction – Jigsaw services help employing functionality implemented in a child layer from a parent layer 132
  133. Conclusion Knowledge about the layers can help in modularizing of

    Java applications including server side applications 133
  134. Nikita Lipsky, Excelsior nlipsky@excelsior-usa.com twitter: @pjBooms Team blog: https://www.excelsiorjet.com/blog 134