package
package copied to clipboard
Cannot Run Containerized Spring Boot Application
I tried using metaparticle to containerize a simple Spring Boot application but it is not running. I am not sure if maybe I am not doing something correctly or if it is a mismatch between what metaparticle expects and the conventions of Spring Boot. Here is my basic Boot app
package com.example.demo;
import io.metaparticle.annotations.Package;
import io.metaparticle.annotations.Runtime;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static io.metaparticle.Metaparticle.Containerize;
@SpringBootApplication
@RestController
public class DemoApplication {
private static final int port = 8080;
@Runtime(ports = {port})
@Package(repository="docker.io/ryanjbaxter",
jarFile="target/demo-0.0.1-SNAPSHOT.jar")
public static void main(String[] args) {
Containerize(() -> {
SpringApplication.run(DemoApplication.class, args);
});
}
@RequestMapping
public String index() {
return "Hello Metaparticle!";
}
}
I then run ./mvnw clean package
and it all works fine. But then I run the app using the traditional java -jar target/demo-0.0.1-SNAPSHOT.jar
, (This is how you would normally run a Boot app) but it fails with
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
Caused by: java.lang.NoClassDefFoundError: io/metaparticle/Util$1
at io.metaparticle.Util.once(Util.java:45)
at io.metaparticle.Metaparticle.Containerize(Metaparticle.java:115)
at com.example.demo.DemoApplication.main(DemoApplication.java:23)
... 8 more
Caused by: java.lang.ClassNotFoundException: io.metaparticle.Util$1
at java.net.URLClassLoader$1.run(URLClassLoader.java:370)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:94)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 11 more
Caused by: java.io.EOFException: Unexpected end of ZLIB input stream
at java.util.zip.InflaterInputStream.fill(InflaterInputStream.java:240)
at org.springframework.boot.loader.jar.ZipInflaterInputStream.fill(ZipInflaterInputStream.java:62)
at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:158)
at org.springframework.boot.loader.jar.ZipInflaterInputStream.read(ZipInflaterInputStream.java:52)
at sun.misc.Resource.getBytes(Resource.java:124)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:462)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
... 17 more
I noticed that the tutorial example creates a jar with all the class files for all of the dependencies, why is that necessary?
Right now the Java app assumes everything is in one jar.
So you need to build in all of your dependencies.
We could also change the app so that it could allow alternate FROM images that had the spring libraries already in there...
@brendandburns when you say "java app" are you referring to the metaparticle code? The metaparticle jars should be on the classpath, why do the class files need to be packaged in the jar itself?
Yes the metaparticle code itself.
The thing is that once the application is packaged as a container, the local classpath is gone, the only classes are the classes in the image.
It would be interesting to introspect the ${CLASSPATH}
in the Java code and move any and all of the classses in the $CLASSPATH
into the image somewhere...
Probably worth doing, but in the meantime if you want to get a Spring app working, you'll need to package of of the classes into a single .jar.
So could we create an image that contains the metaparticle code within it so its not necessary for each app to do this?
Spring Boot bundles the libraries by default, but the reason it won't start is that the plugin it uses to create a fat jar injects a wrapper that calls the real main method and moves all of the non loader classes to BOOT-INF/classes
internally so when the command in the Dockerfile tries to start the class directly it isn't found. I tried with an empty new Boot project and got the same error. It does add some metadata to MANIFEST.mf that could be used for special handling, but that would probably only work on the class that Spring thinks is the main method.
META-INF/MANIFEST.mf
from my test Boot project for reference:
Manifest-Version: 1.0
Implementation-Title: metaparticle-demo
Implementation-Version: 0.0.1-SNAPSHOT
Archiver-Version: Plexus Archiver
Built-By: will
Implementation-Vendor-Id: com.willpenington
Spring-Boot-Version: 1.5.9.RELEASE
Implementation-Vendor: Pivotal Software, Inc.
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.willpenington.metaparticledemo.MetaparticleDemoApplic
ation
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Created-By: Apache Maven 3.5.2
Build-Jdk: 1.8.0_144
Implementation-URL: http://projects.spring.io/spring-boot/metaparticle
-demo/
Curious if anyone has found a good workaround for this. I am definitely interested in giving Metaparticle a go, but this is a blocker.
I'll try to repro and dig into this a little deeper...
One thing to try would be to switch the bare bones JDK FROM image to a Spring enabled one.
--brendan
From: Frank Reno [email protected] Sent: Friday, January 5, 2018 12:35 PM To: metaparticle-io/package Cc: Brendan Burns; Mention Subject: Re: [metaparticle-io/package] Cannot Run Containerized Spring Boot Application (#6)
Curious if anyone has found a good workaround for this. I am definitely interested in giving Metaparticle a go, but this is a blocker.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fmetaparticle-io%2Fpackage%2Fissues%2F6%23issuecomment-355658680&data=02%7C01%7Cbburns%40microsoft.com%7C4149512dc0384a17c9dd08d5547bce92%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636507813076159747&sdata=sIOKvwY2%2F5ZBAV2AuRoBkdYGrKTncC9V4pKurNjrgyQ%3D&reserved=0, or mute the threadhttps://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAFfDgnXGAppuHOgN0XV2ZCseJ4PbQblfks5tHod4gaJpZM4Q4yLc&data=02%7C01%7Cbburns%40microsoft.com%7C4149512dc0384a17c9dd08d5547bce92%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636507813076159747&sdata=Y4PQXB9h3ZDON1VTvPzR%2BRa5%2BV0AdYG2NDtn2q4phGs%3D&reserved=0.
The cause of the problem described in the original post is the mvn package
step carried out in the Containerize()
. That repackaging overwrites the Spring Boot Jar file that is currently being executed and doing that makes the Spring Jar launcher's index of file locations inside the Jar (created at startup) worthless. Consequently the next attempted load of a class from the Jar fails with the ZLIB problem.
I've tried a couple of fixes locally but so far not fully convinced by either of them. The first approach was to have the mvn package
create a new jar in the target folder that had a different name to that specified in the @Package
annotation's jarFile
value and hence avoid the previously mentioned overwriting. That worked (i.e. java -jar target/mySpringBootjar.jar
ran successfully) but that package step feels unnecessary in this case. Skip it completely if it could be detected that we're running inside an executable Jar? I've tried inferring that from some of the runtime information available to the Metaparticle package library (e.g. check if the class containing the Containerize()
'd method has the @SpringBootApplication
annotation; or examine the current stack and see if things started from a main
method) but would that catch all cases? An alternative to all that might be to introduce support for a simple system property that could be used to disable the mvn package
step however the runs?
Incidentally, there is another problem lurking here which concerns the generated Dockerfile. It's current final line (CMD java -classpath /main.jar %s
) will not succeed in launching an executable Spring Boot Jar (or any executable Jar I think). One simple solution would be to add a new boolean
field to the @Package
annotation that could be set to true
if the assembled Jar (identified by the jarFile
field) is an executable Jar or false
(the default) otherwise. I'll open an issue and submit a PR for consideration.
Update: the above assertion that the generated Dockerfile won't work for any executable Jar is incorrect. Regular executable Jars work OK with CMD java -classpath /main.jar %s
but if it is a Spring Boot Jar things barf ("Error: Could not find or load main class..."). Maybe make the last line of the generated Dockerfile conditional on a check on the class containing the Containerize()
'd method: if it does not have the @SpringBootApplication
annotation then proceed as normal, otherwise if the annotation is detected then make the generated last line be CMD java -jar /main.jar
?
@brendanburns For your consideration I've submitted a PR for this issue that consists of the changes to the library I made locally to containerize a Spring Boot application. Works for running with Maven (mvn spring-boot:run
where the POM includes the Spring Boot plugin) and also for running using java -jar target/demo-0.0.1-SNAPSHOT.jar
on the built Spring Boot executable Jar. Pretty much a straw man proposal - would be interested in your thoughts on this approach.
I still get the same issue and also have the latest version of Spring Boot. Can somebody help
@faiz0019 I've just tried running a Boot application using the information in package samples and it all works OK. I'm using the latest level of package source and Spring Boot 2.0.3. What kind of failure do you see?
@georgeharley Im getting the same issue as reported in this thread. I tried to use different methods and none of them worked. The main problem I could see is that we are using the artifact that is from our own company and cannot use some of the dependencies mentioned in the package samples(Like I cannot use parent and metaparticle-package). I see that is the issue.
We moved from our company base pom and it solved the issue. Thank You for your support