{"id":22295772,"url":"https://github.com/danielfernandez/test-spring-boot-tomcat","last_synced_at":"2025-07-27T07:07:07.523Z","repository":{"id":146695463,"uuid":"74404632","full_name":"danielfernandez/test-spring-boot-tomcat","owner":"danielfernandez","description":"Test repository for https://jira.spring.io/browse/SPR-14932","archived":false,"fork":false,"pushed_at":"2016-11-21T22:27:57.000Z","size":68,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-25T22:35:39.763Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielfernandez.png","metadata":{"files":{"readme":"README.markdown","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-11-21T20:50:32.000Z","updated_at":"2016-11-22T08:44:02.000Z","dependencies_parsed_at":null,"dependency_job_id":"866e05d9-b7ab-42e8-88f0-834b6075231a","html_url":"https://github.com/danielfernandez/test-spring-boot-tomcat","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/danielfernandez/test-spring-boot-tomcat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielfernandez%2Ftest-spring-boot-tomcat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielfernandez%2Ftest-spring-boot-tomcat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielfernandez%2Ftest-spring-boot-tomcat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielfernandez%2Ftest-spring-boot-tomcat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielfernandez","download_url":"https://codeload.github.com/danielfernandez/test-spring-boot-tomcat/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielfernandez%2Ftest-spring-boot-tomcat/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267320254,"owners_count":24068527,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-27T02:00:11.917Z","response_time":82,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-03T17:43:08.389Z","updated_at":"2025-07-27T07:07:07.505Z","avatar_url":"https://github.com/danielfernandez.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"Test repository for SPR-14932\n-----------------------------\n\n**Link**: [SPR-14932](https://jira.spring.io/browse/SPR-14932)  \n\n**Title**: Class loading issues due to thread context classloader hierarchy (Spring Web Reactive + Tomcat)\n\n**Tag**: Use tag `SPR-14932`\n\n## Scenario\n\nThe scenario tested in this repository is the following:\n\n   * A web application uses a library.\n   * That library needs to dynamically load a class that implements a specific interface, for example, in \n     order to select the best implementation for the specific environment in which it is being run.\n\n## Repository Contents\n\nThis repository consists of:\n\n   * A library (`-lib`) including two classes called `SomeIntefaceClassClassLoaderUtils` and\n     `SomeInterfaceThreadContextClassLoaderUtils`, which upon initialization (static block) try to\n     dynamically load and instance an implementation of an interface called `SomeInterface`. The former\n     utility class uses `SomeInterface.class.getClassLoader()` to obtain the class loader to be used \n     for this, and the latter uses `Thread.currentThread().getContextClassLoader()`.\n   * Four example web applications that provide two URLs: `/class` and `/threadcontext`, which respectively\n     call the two `*Utils` classes in the library.\n       * **[142mvc]** A Spring Boot `1.4.2.RELEASE` webapp using Spring MVC `4.3.3.RELEASE` on Tomcat.\n       * **[200mvc]** A Spring Boot `2.0.0.BUILD-SNAPSHOT` webapp using Spring Web MVC `5.0.0.BUILD-SNAPSHOT` on Tomcat.\n       * **[200reactive-tomcat]** A Spring Boot `2.0.0.BUILD-SNAPSHOT` webapp using Spring Web Reactive `5.0.0.BUILD-SNAPSHOT` on Tomcat.\n       * **[200reactive-netty]** A Spring Boot `2.0.0.BUILD-SNAPSHOT` webapp using Spring Web Reactive `5.0.0.BUILD-SNAPSHOT` on Netty.\n\nAll Spring Boot applications were generated using [http://start.spring.io](http://start.spring.io), with minor changes to\ntheir `pom.xml` to add the library dependency and/or enable or disable Tomcat/Netty.\n\n## Observed Results\n\nThe observed results are:\n\n   * Web applications [142mvc], [200mvc] and [200reactive-netty] work OK, and are able to call the library and\n     let it load the required interface implementation without issues.\n   * **Web application [200reactive-tomcat] fails** when using the *thread context* class loader, throwing a\n     `ClassNotFoundException` on the desired interface implementation class. When using the *class* class loader it works OK.\n\nThis is the detail of what is happening at the [200reactive-tomcat] application:\n\n   * The *thread-context* class loader is an `org.apache.catalina.loader.ParallelWebappClassLoader` instance \n     with no class path.\n   * The *class* class loader is an `org.springframework.boot.loader.LaunchedURLClassLoader` instance with a\n     class path including all the inner `.jar` files inside the Spring Boot *über jar*.\n   * The `ParallelWebappClassLoader` **does not delegate** to the `LaunchedURLClassLoader`, but instead\n     directly delegates to the `sun.misc.Launcher$AppClassLoader` that the `LaunchedURLClassLoader` delegates too.\n\nThe above situation is what causes the `ClassNotFoundException` when executing [200reactive-tomcat] and the\n*thread-context* class loader is used to load the desired class at the library.\n\n## Class Loader Hierarchy Comparison\n\nThis is the *thread-context* class loader hierarchy for [200mvc] — **OK**:\n\n```\n+-\u003e sun.misc.Launcher$ExtClassLoader - [sun.misc.Launcher$ExtClassLoader@198ff037]\n    +-\u003e sun.misc.Launcher$AppClassLoader - [sun.misc.Launcher$AppClassLoader@330bedb4]\n        +-\u003e org.springframework.boot.loader.LaunchedURLClassLoader - [org.springframework.boot.loader.LaunchedURLClassLoader@65ab7765]\n            +-\u003e org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader - [TomcatEmbeddedWebappClassLoader\\n\\n  context: ROOT\\n\\n  delegate: true\\n\\n----------\u003e Parent Classloader:\\n\\norg.springframework.boot.loader.LaunchedURLClassLoader@65ab7765\\n\\n]\n```\n\nThis is the *thread-context* class loader hierarchy for [200reactive-netty] — **OK**:\n\n```\n+-\u003e sun.misc.Launcher$ExtClassLoader - [sun.misc.Launcher$ExtClassLoader@67449f2]\n    +-\u003e sun.misc.Launcher$AppClassLoader - [sun.misc.Launcher$AppClassLoader@330bedb4]\n        +-\u003e org.springframework.boot.loader.LaunchedURLClassLoader - [org.springframework.boot.loader.LaunchedURLClassLoader@65ab7765]\n```\n\nThis is the *thread-context* class loader hierarchy for [200reactive-tomcat] — **FAIL**:\n\n```\n+-\u003e sun.misc.Launcher$ExtClassLoader - [sun.misc.Launcher$ExtClassLoader@32e33c96]\n    +-\u003e sun.misc.Launcher$AppClassLoader - [sun.misc.Launcher$AppClassLoader@330bedb4]\n        +-\u003e org.apache.catalina.loader.ParallelWebappClassLoader - [ParallelWebappClassLoader\\n\\n  context: ROOT\\n\\n  delegate: false\\n\\n----------\u003e Parent Classloader:\\n\\nsun.misc.Launcher$AppClassLoader@330bedb4\\n\\n]\n```\n\n\n## Possible Diagnosis\n\nThe `org.apache.catalina.loader.ParallelWebappClassLoader` being used as thread context class loader in [reactive200-tomcat]\n**should delegate to `org.springframework.boot.loader.LaunchedURLClassLoader`, but it doesn't**.\n\n\n## How to test\n\nFirst, the library has to be compiled, packaged and put into the local maven repository:\n\n```\ncd test-spring-boot-tomcat-lib\nmvn clean compile package install\n```\n\nThen, in order to run any of the web applications, the Spring Boot applications have to be packaged and then\nexecuted. **Running from an IDE such as IntelliJ or Eclipse does not correctly test the scenario** given the\ndifferent class path organizations applied by the IDEs.\n\n```\nmvn -U clean compile package\ncd target/\njava -jar test-spring-boot-[app].jar\n```\n\nThen a browser can be used to access `/threadcontext` or `/class` and see the results. The web applications\nwill output debug information about the class loaders being used through the log.\n\nBy default, when accessing `/threadcontext` or `/class` only the hierarchy of class loaders will be output through\nthe log. In order to see the full class path for each class loader in the hierarchy (very verbose), use:\n\n```\nmvn -U clean compile package\ncd target/\njava -Dlogging.level.classloaderlog=TRACE -jar test-spring-boot-[app].jar\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielfernandez%2Ftest-spring-boot-tomcat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielfernandez%2Ftest-spring-boot-tomcat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielfernandez%2Ftest-spring-boot-tomcat/lists"}