Spring Boot - Host JaCoCo coverage as static HTML - java

I am working with Spring Boot 2.2.5 and Java 8.
I have a Spring Boot webservice that I deploy and run as a linux service using the embedded Tomcat in Spring Boot.
During my Maven build, I generate code coverage metrics using the JaCoCo Maven plugin, and I would like to package and host these static HTML pages when I deploy to my server.
The output for these files is target/site/jacoco/index.html.
I know that you can deploy and host webpages through Spring Boot, but I have never accomplished it, and everything I lookup online seems to be more complicated than what im actually trying to do. The only thing i seem to have gathered so far, is that it need to get the html into my /resources directory.
Does anyone know how I can package all of the JaCoCo generated html pages into my .jar file, and host it on my webserver so that I can access it in a similar fashion to how I access any other API in the app?
I build and deploy the app with Jenkins. So if there is some nifty Jenkins way of doing it through my Jenkins.groovy script, that would be nice too.
I would like to be able to access something like: localhost:8080/my-app-context/coverage

Well, after some more digging and the right google questions, the solution was simpler than I thought. I stumbled across this article from Baeldung.
The goal:
get target/site/jacoco into the src/main/resources/static directory
get target/apidocs into the src/main/resources/static directory
The challenge:
Do it during a Maven/Jenkins build only.
The solution:
Use a Maven plugin to move the files after successful build
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>copy-javadocs</id>
<phase>install</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/src/main/resources/static/apidocs</outputDirectory> <!-- output directory -->
<resources>
<resource>
<directory>${basedir}/target/apidocs</directory> <!-- source directory -->
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-jacoco</id>
<phase>install</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/src/main/resources/static/jacoco</outputDirectory> <!-- output directory -->
<resources>
<resource>
<directory>${basedir}/target/site/jacoco</directory> <!-- source directory -->
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
After putting the above code in my pom.xml, once the app is deployed to my server, both my JaCoCo coverage and my JavaDoc static html pages can be accessed at:
<context-root>/apidocs/index.html
<context-root>/jacoco/index.html
I hope this simplifies it for others looking to do the same.

Related

What is the proper way to create .ebextensions for AWS Beanstalk through maven for Spring Boot app

We have to customize hosts file in our dynamically generated Elastic Beanstalk instance of our Spring Boot application during our GitLab CI/CD pipeline. To do this, we need to provide a .ebextensions directory with a config file that looks like this:
commands:
01_add_hosts:
command: echo 123.12.12.123 myhost.com >> /etc/hosts
Since we have a spring boot application, we have to package .ebextensions at the root level of our fat jar. So, basically we unzip the jar, add the ebextensions directory and zip it back. This way we successfully achieve the Beanstalk customization in our Gitlab pipelines.
However, to make this process we use maven-antrun-plugin like this:
<!-- Bundle .ebextensions inside jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>prepare</id>
<phase>package</phase>
<configuration>
<tasks>
<unzip src="${project.build.directory}/${project.build.finalName}.jar" dest="${project.build.directory}/${project.build.finalName}" />
<copy todir="${project.build.directory}/${project.build.finalName}/" overwrite="false">
<fileset dir="./" includes=".ebextensions/**"/>
</copy>
<zip compress="false" destfile="${project.build.directory}/${project.build.finalName}.jar" basedir="${project.build.directory}/${project.build.finalName}"/>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
The problem is that maven-antrun-plugin is and old plugin from 2014 and this looks like not the proper way to achieve this bundle jar.
Do you how or what it is the spring boot way to create this bundle jar? I mean add directories/files at the root level of the jar in spring boot? Bear in mind that we bundle this at our pipeline job time that deploys through AWS Beanstalk maven plugin.
The Build Helper Maven Plugin can be used for this. Create a folder .ebextensions in the project's root and add the plugin to the <build><plugins> section of the pom.xml:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-resource-ebextensions</id>
<phase>generate-resources</phase>
<goals>
<goal>add-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${basedir}/.ebextensions</directory>
<targetPath>.ebextensions</targetPath>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
Is it required that you upload only a jar to elastic beanstalk? The way we do it is that we upload a zipfile, containing our fat jar, a .ebextensions directory and a Procfile. This way the ebextensions get applied and the Procfile contains the command to start the java process.
So for example you could upload hello-world.zip containing:
.ebextensions:
hosts.config
hello-world.jar
Procfile
With Procfile being a text file containing:
web: java -jar hello-world.jar
If you do it like this, there's no need to embed your ebextension files in your application jar, which makes things a whole lot easier in my opinion.
Procfile documentation

Spring Boot alternative index page

My app uses Spring Boot on the back end and a SPA (Angular) site on the front end. Currently I am serving the index.html page from the webapp folder, which works automatically no configuration needed. Now I integrated a build process for the front end using gulp and all the created sources are "copied" into a build directory. Now I would like to serve the index.html file from the build directory as my main page.
I tried spring.application.index=build/index.htmland some other spring boot settings, but nothing worked. I believe no code is needed from my current code base but if anything is missing let me know.
Is there a way to configure this in the applications.properties file? Do I need to create a controller for the index page? Or is there any other way to change the default behavior?
thanks
Going by the common Spring Boot properties, you should be able to modify this property:
spring.application.index=
Admittedly, I do tend to create a minimal 'home' controller with a #RequestMapping("/") anyway. :)
It's worth noting that the build directory will only be on the classpath if it's under src/main/resources. It's also worth mentioning that the contents of src/main/webapp don't get bundled into the jar automatically. src/main/resources/static is where Spring Boot will look for your static web files. As such, there are a couple of alternatives for you.
Option 1: Configure your Grunt build to output to a directory under src/main/resources/static.
Option 2: Configure your Java build tool to take the Grunt output and put it in your resources directory, so that it's on the classpath. For example, using Maven, the following would move the contents of a directory called build into your src/main/rescources/static.
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/classes/static</outputDirectory>
<resources>
<resource>
<directory>build</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>

How to bind SASS plugin goal to tomcat7 deploy phase (maven)

I am developing a webapp running on Tomcat7, and using maven for dependencies/automated builds. Yesterday I started using the sass-maven-plugin, which is great. Its goal sass:update-stylesheets processes sass files and outputs css. Unfortunately, I can't have it executed during the webapp packaging. I am pretty new to maven too, so I might have missed something. Here's my understanding :
when I type mvn tomcat7:deploy, maven executes the deploy goal defined in the tomcat7 plugin
this plugin goes through some phases of the development lifecycle. More specifically, as mentioned in the doc, it "invokes the execution of the lifecycle phase package prior to executing itself."
if I map the goal sass:update-stylesheets to the package phase in <build><executions/></build>, it should be executed everytime I deploy/redeploy my app.
When I run mvn sass:update-stylesheets independently of tomcat7:deploy, everything is smooth. sass-maven-plugin gets the .scss files from src/main/resources, processes them and places the output in src/main/webapp/resources, where I want it to be to be deployed with my webapp. Unfortunately, if I don't run the command prior to tomcat7:deploy, I don't get any css for my pages. What did I get wrong? Also, is there any way I could map the sass:update-stylesheets to the phase process-resources, for instance, which would make more sense? Lastly, if this all works, will Eclipse's incremental build pick it up?
Here's my pom.xml (the relevant parts)
...
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
...
</configuration>
</plugin>
<!-- SASS processing -->
<plugin>
<groupId>org.jasig.maven</groupId>
<artifactId>sass-maven-plugin</artifactId>
<version>1.1.1</version>
<executions>
<execution>
<id>generate-css</id>
<phase>package</phase>
<goals>
<goal>update-stylesheets</goal>
</goals>
</execution>
</executions>
<configuration>
<useCompass>true</useCompass>
<resources>
<resource>
<source>
<directory>${basedir}/src/main/resources</directory>
</source>
<destination>${basedir}/src/main/webapp/resources</destination>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
Thanks in advance for your help.
You have configured the stuff in the plugin management section. Please move the execution and it's binding to the build section (that is, out of the pluginManagement's plugins section into build's plugins section).

Exclude application.properties when generating war using spring boot and spring-boot-maven-plugin

I am developing a web application using Spring Boot, and want to generate war instead of jar.
It works very fine using the conversion from jar to war described here : http://spring.io/guides/gs/convert-jar-to-war/
But I want to exclude the application.properties from the war, because I use #PropertySource(value = "file:${OPENSHIFT_DATA_DIR}/application.properties") to get the file path on production environment.
This method works when generating my war, but in eclipse I can't run my application because application.properties not copied at all to target/classes :
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>application.properties</exclude>
</excludes>
</resource>
</resources>
</build>
This method doesn't work at all, I think that spring-boot-maven-plugin doesn't support packagingExcludes :
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<packagingExcludes>WEB-INF/classes/application.properties</packagingExcludes>
</configuration>
</plugin>
</plugins>
</build>
Have you another suggestion?
Thanks
Try using the solution below. This will work:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/*.properties</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
If you are using the above solution , while running the project in Eclipse IDE you may get error that the properties file is not found. To get rid of this you need to add the resources folder in Run as configuration.(Run configurations... -> Classpath -> User Entries -> Advanced... -> Add Folders)
When running in Eclipse, at your Run Configuration, you need to specify the path of the propeties to Spring Boot:
--spring.config.location=${workspace_loc:/YOURPROYECTNAME}/src/main/resources/
The solution I added is to unzip my packaged war, delete the file application.properties and create a new war named ROOT.war using maven-antrun-plugin and run some ant tasks.
this is what i added to my plugins in pom.xml :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>package</id>
<phase>package</phase>
<configuration>
<target>
<unzip src="target/${artifactId}-${version}.${packaging}" dest="target/ROOT/" />
<delete file="target/ROOT/WEB-INF/classes/application.properties"/>
<zip destfile="target/ROOT.war" basedir="target/ROOT" encoding="UTF-8"/>
<delete dir="target/ROOT"/>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
I named my target war as ROOT.war because I am using tomcat on openshift PaaS, so I just copy my ROOT.war and push to my openshift repo. That's it
What I understand from your question is, you want to use application.properties for your development. But you dont want to use it for production.
I would suggest using Spring profiles to achieve this.
In your properties file
Create a profile for development. Put all your development properties under it.
Do not create a profile for production in your properties file.
When you are developing, set active profile to development, so that the properties are loaded from your application.properties file.
When you run it in production, set active profile to Production. Though application.properties will be loaded into your WAR, since there is no profile for production, none of the properties will be loaded.
I have done something similar using YML. I am sure there must be a way to do the same thing using .properties file too.
spring:
profiles.active: development
--
spring:
profiles: development
something:
location: USA
unit1: Test1
unit2: Test2
You could change the profile in run time using
-Dspring.profiles.active=production
Try to using this solution:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.version}</version>
<configuration>
<addResources>false</addResources>
</configuration>
</plugin>
<addResources>false</addResources> will keep properties when you run mvn spring-boot:run

Using Maven to run a WAR dynamically in Tomcat, how does one add classpath entries so only Tomcat sees them?

Scenario is such: I have a webapp that I'd like to run dynamically with the tomcat-maven-plugin's tomcat:run goal. The wrinkle is that I have numerous classpath resources that need to differ between the packaged artifact and the one run off a local workstation.
Failed Attempts:
1.) My first attempt was to use the builder-helper-maven-plugin, but it won't work because the target configuration files will (inconsistently!) work their way into the packaged WAR archive.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<id>add-resource</id>
<phase>generate-resources</phase>
<goals>
<goal>add-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${basedir}/src/main/resources-env/${testEnv}</directory>
<targetPath>${basedir}/target/classes</targetPath>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
2.) My second attempt was to add the folder (since the files-to-be-deployed aren't present in Tomcat's classpath yet either) to -Djava.ext.dirs, but it has no effect (I actually suspect that this systemProperties element is misconfigured or otherwise not working at all). See:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<version>1.0-beta-1</version>
<configuration>
<tomcatWebXml>${basedir}/src/main/mock/web.xml</tomcatWebXml>
<systemProperties>
<property>
<name>java.ext.dirs</name>
<value>${basedir}/src/main/resources-env/${testEnv}</value>
</property>
</systemProperties>
<path>/licensing</path>
</configuration>
</plugin>
I'm not sure what to attempt next. The heart of the problem seems to be that missing in this plugin is something like Surefire's <additionalClasspathElement> element.
Would the next step be to create a custom catalina.properties and add it to a <configurationDir>? If so, what would catalina.properties need to look like?
Edit: More thorough explanation follows
I understand this question reads somewhat vaguely, so I'll try to elaborate a bit.
My POM uses the webResources functionality of the WAR plugin to copy some environment-specific config files and without using a profile to do it, by copying in a resource named /src/main/resources-env/${env} like so:
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
...
<configuration>
...
<webResources>
<!-- Copy Environment-specific resources to classes -->
<resource>
<directory>${basedir}/src/main/resources-env/${env}</directory>
<targetPath>WEB-INF/classes</targetPath>
</resource>
</webResources>
</configuration>
</plugin>
This will copy the (default, DEV) environment resources into the package and currently works fine. Note also that b/c these occur as part of packaging, the tomcat:run goal is never privy to them (which is desired, as the environments differ).
So the problem is this: when the dynamic tomcat:run is executed, the application will fail because its classpath (it looks at target/classes) will lack the needed local workstation environmental config files. All I need to do is get those on the path for tomcat, but would like to do so without adding anything to the command line and definitely without breaking the build's integrity if someone follows up with a mvn package and doesn't clean first.
I hope this is more clear.
I may be missing something but why don't you declare the required dependencies in a profile and use this profile when running Tomcat? I don't get why you would need to put these resources at Tomcat's classpath level.
UPDATE: I'm editing my answer to cover the comment from the OP itself answering my question above.
You're correct, the files really need to be in the webapp classpath, not tomcat's. So how could I make a profile that activate automatically for tomcat:run but without additional cmd line args?
I don't know how to do this without declaring the profile as <activeByDefault> or listing it under the <activeProfiles> (but this is not what I had in mind, I would rather use property activation and call something like mvn tomcat:run -Denv=test, not sure to understand why this is a problem).
And how should I "declare the dependencies" in the profile while ensuring that subsequent invocations never let them into the package WAR via a vanilla mvn package
If the previously mentioned profile is active by default, then you'll need to exclude it if you don't want it, by calling something like mvn package -P !profile-1. A profile can't be magically deactivated for one particular goal (at least, not to my knowledge).
Actually, my understanding is that you really have two different context here: the "testing" context (where you want to include more things in the WAR) and the "normal" context (where you don't want these things to be included). To be honest, I don't know how you could distinguish these two situations without specifying any additional parameter (either to activate a profile or to deactivate it depending on the context). You must have valid reasons but, as I said, I don't really understand why this is a problem. So maybe profiles are not a solution for your situation. But I'd really like to understand why because this seems to be a typical use case for profiles :)
UPDATE2: Having read your comment to the other answer and your update, I realize that my initial understanding was wrong (I though you were talking about dependencies in the maven sense). But, I still think that profiles could help you, for example to customize the <resources> as in this blog post (this is just one way to do, using a property like src/main/resources/${env} in the path is another way to go). But this won't solve all your concerns (like not specifying additional command line params or automagically cleaning the target directory). I don't have any solutions for that.
Add the dependencies element directly to the plugin element.
Here is an example of doing the same with the Jetty plugin from the (still in development) Maven Handbook: http://www.sonatype.com/books/mhandbook-stage/reference/ch06s03.html
Vote for http://jira.codehaus.org/browse/MTOMCAT-77 which addresses this need.
Here's the solution I have in place at the moment.
Special thanks to Pascal's diligent conversation here, but I ultimately decided to make a change to how I was loading my environment-specific config files throughout the goals and now I believe I'm getting most of what I initially wanted.
I removed the config files from <webResources> from the WAR plugin and the test config from <testResources> and am now manually managing the resource-copying with the the maven-resources-plugin to copy them directly into target/classes at the goal they're needed. This way Tomcat can see the config, but the tests aren't broken by having duplicate or differing config files on the path.
It's definitely a mess, but it works. Listing:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.4.1</version>
<executions>
<execution>
<id>copy-env-resources</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${basedir}/src/main/resources-env/${env}</directory>
<filtering>true</filtering>
</resource>
</resources>
<outputDirectory>${basedir}/target/classes</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-testEnv-resources</id>
<phase>process-test-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${basedir}/src/main/resources-env/${testEnv}</directory>
<filtering>true</filtering>
</resource>
</resources>
<outputDirectory>${basedir}/target/classes</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-env-resources-again</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${basedir}/src/main/resources-env/${env}</directory>
<filtering>true</filtering>
</resource>
</resources>
<outputDirectory>${basedir}/target/classes</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
So a mvn clean install will build and test with ${env} and ${testEnv} appropriately. A mvn -Denv=someLocalConfig tomcat:run (which in my case is identical to my default ${testEnv} will make sure the src/main/resources-env/someLocalConfig gets loaded for Tomcat's dynamic execution, but without requiring that I do a clean before successfully rebuilding.
Like I said, messy that I'm rewriting the same cluster of files to the same target location at each phase, but it accomplishes what I'd meant to.

Categories