2

While trying to implement classpath locations for URL a URLStreamHandlerProvider implementation was developed and added to META-INF/services/java.net.spi.URLStreamHandlerProvider as per https://docs.oracle.com/javase/9/docs/api/java/net/spi/URLStreamHandlerProvider.html Everything works fine when application starts from IDE but when it is packed into single jar with spring-boot-maven-plugin ServiceLoader fails to load the URLStreamHandlerProvider with class not found exception. This is probably related to different classloaders... Any help how to fix or workaround? BTW it is not possible to use URL.setURLStreamHandlerFactory as embedded tomcat does this too. Spring boot 2.7.2 java 11

Exception: Exception in thread "main" java.lang.reflect.InvocationTargetException at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) Caused by: java.util.ServiceConfigurationError: java.net.spi.URLStreamHandlerProvider: Provider com.example.springlog.ClasspathURLStreamHandlerProvider not found at java.base/java.util.ServiceLoader.fail(ServiceLoader.java:589)

Jens
  • 67,715
  • 15
  • 98
  • 113
Alex
  • 51
  • 2
  • Does your jar file actually contain `com.example.springlog.ClasspathURLStreamHandlerProvider` and is it in the correct directory? – k314159 Aug 17 '22 at 14:13
  • They are loaded with the `ClassLoader.getSystemClassLoader()`, if that matters. See the static method java.net.URL#providers – slindenau Aug 17 '22 at 14:38
  • ClassLoader.getSystemClassLoader() - this is what I am warried about. For instance if I use Class.forname("c.e.s.C") in main it works normally. On the other hand it is hard to beleave it is not possible to pack any application into jar – Alex Aug 18 '22 at 05:26
  • I looked into the class loaders difference and it appears that locally jdk.internal.loader.ClassLoaders$AppClassLoader is in use while org.springframework.boot.loader.LaunchedURLClassLoader when starting from jar – Alex Aug 18 '22 at 05:52
  • 1
    “starting from jar” is not the same as “starting with a custom launcher”. And `Class.forName( "com.example.springlog.ClasspathURLStreamHandlerProvider")` is not the same as `ClassLoader.getSystemClassLoader() .loadClass("com.example.springlog.ClasspathURLStreamHandlerProvider")` as the former uses the caller’s class loader. – Holger Aug 23 '22 at 09:14
  • @Holger running a spring boot application with `java -jar myspringbootapp.jar` will load all the embedded dependencies with a custom classloader created via `org.springframework.boot.loader.JarLauncher`. – slindenau Aug 30 '22 at 13:13
  • @Alex what exactly is your custom `URLStreamHandlerProvider` doing, and when is it needed (during application boot, or only after boot on processing requests)? – slindenau Aug 30 '22 at 13:31
  • 1
    @slindenau …and that custom class loader is the problem, not the “starting from jar” which the title of this question wrongly suggests. – Holger Aug 30 '22 at 14:13
  • @Alex, did you manage to fix the issue? I am facing the same problem myself and it will be of great help if you could share the fix. – Sathish Nov 07 '22 at 11:42
  • 1
    @Sathish in my case the result jar contains directory "classes" where provider implementaions goes too, while system class loader wants to load classses from "root". So jar repackaging solved the problem for me. I placed classses supposed to be loaded by system class loader from classes folder into root one where some spring stuff exist. – Alex Nov 09 '22 at 12:34
  • 1
    @slindenau custom URLStreamHandlerProvider gets resources from class path it is needed when parsing application.yml on start. – Alex Nov 09 '22 at 12:40
  • 1
    Thanks for your reply @Alex. In my case Tomcat was registering its own `URLStreamHandlerProvider` on my application startup. Since only one stream provider can be registered in the JVM for a container, my provider is not used even if the service is loaded correctly by Service loader. The solution was to use the addUserFactory method of `TomcatURLStreamHandlerFactory` to register my custom protocol provider. – Sathish Nov 09 '22 at 17:05

0 Answers0