WEB-INF not getting added to SpringBoot jar - spring

I'm putting together a webapp using SpringBoot.
The REST part works fine, but the "JSP part" is borked. The endpoints are called, everything is fine until I return the basename of the JSP page, such as return "info"; The method returns a String.
When I invoke an endpoint, I get this message:
[...] [dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Could not resolve view with name 'info' in servlet with name 'dispatcherServlet'] with root cause
javax.servlet.ServletException: Could not resolve view with name 'info' in servlet with name 'dispatcherServlet'
I followed "the pattern" for adding JSP support to SpringBoot (I say "the pattern" because the dozen or so sources I found all seem to quote the same example).
I'm building a JAR with Maven, looking into that jar, I don't find the JSP files, or even any of the WEB-INF directory structure, so I believe the problem is somewhere between Maven and SpringBoot's plugin.
Here are the pieces of the POM, most of the dependencies removed:
<packaging>jar</packaging>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
...
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
The POM was built mostly from an archetype.
I'm rather ignorant about debugging Maven builds, assuming that this is a Maven problem, however, I suppose it could have something to do with the springboot plugin. I appreciate all assistance, include better resources to read than the ones that I found.
UPDATE
Per Qiu Zhou's comment about the , I added the following to the POM:
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
and moved the JSP directory to src/main/resources/WEB-INF/jsp. The JAR file now contains this:
BOOT-INF/classes/WEB-INF/jsp/info.jsp
which seems to be correct. I no longer get the dispatcherServlet message (see above), however, when I curl the site, this is what I get:
% curl localhost:8099/info
{"timestamp":"2020-09-01T17:22:28.413+00:00","status":404,"error":"Not Found","message":"","path":"/info"}
The controller code is this:
#Controller
public class SimpleController {
#GetMapping("/info")
public String info() {
return "info";
}
}
I remain perplexed -- Is the JAR built correctly??
Thanks

You are not specifying the version of the plugin you wish to use. Depending on the version you might need to configure the plugin to add the resources directory to the application classpath by adding the following to the plugin configuration
<configuration>
<addResources>true</addResources>
</configuration>
This enables hot reloading of resources, but what is actually recommended now for this and other development features is adding spring dev tools dependency.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.3.3.RELEASE</version>
<optional>true</optional>
</dependency>
</dependencies>
You can learn more about it by reading the plugin docs.
https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/html/#run

khmarbaise provided a solution in his comment - which was to package the JAR as a WAR. I waited for him to post an answer v. a comment, but its been quite a while, so I will answer on his behalf.

try following steps:
1、include jstl dependency:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
2、put your jsp files in src/main/webapp/WEB-INF/jsp directory. (manually create it if does not exist)
3、set view prefix in application.properties or application.yml:
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
4、specify where jsp files should be in output, put following config in pom.xml(based on the your config):
<packaging>jar</packaging>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
...
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/webapp</directory>
<targetPath>META-INF/resources</targetPath>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>

Related

Jmeter random function is not working from java application

I want to make use of Jmeter Random function (${__RandomString(10,0123456789,Value)}) in my java application for load testing.
Below is maven dependency
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_functions</artifactId>
<version>4.0</version>
</dependency>
It is working properly If I add ApacheJMeter_functions jar to class path but the same is not working if I use Maven dependency.
Note : Works fine if I add jar to classpath without version name.
pom :
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>performance-api</artifactId>
<version>0.1</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_core</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_http</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_functions</artifactId>
<version>4.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Response when added ApacheJMeter_functions jar to class path without version name
Response when added ApacheJMeter_functions dependency jar into pom
Below solution helped me in resolving issue
When Jmeter functions are used in the Java code, Jmeter tries to compare the function related classes from java class path with classes from the 'search_path' (Reads classes from the jars). So Jmeter function works only if required function class is present in both the path (Jmeter has seperate class for each function).
This is why we need to make sure the 'ApacheJMeter_functions' jar added in the pom (which will be added in class path ) and the path to jmeter functions jar is set to 'search_path'.Both should have same version.
But in case of spring boot application, along adding dependency to pom we also need to append the path to jmeter functions jar explicitly to the class path like below
System.setProperty("java.class.path", System.getProperty("java.class.path") + PathToJmeterFunctionJars );

"Cannot access com.google.common.base.Predicate" in Selenide

I am new to Selenide and I tried to follow this video: https://vimeo.com/107647158 at 8:05 - part
$("#ires li.g").shouldHave
is underlined with "Cannot access com.google.common.base.Predicate" error.
I tried some Google and found this: What is the fix for class file for com.google.common.base.predicate not found? which pointed to https://github.com/google/guava page.
I added dependency to pom:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.company.myFirstSelenideTest</groupId>
<artifactId>myFirstSelenideTest</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<name>metadataTestonDPU</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.codeborne</groupId>
<artifactId>selenide</artifactId>
<version>4.12.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>25.1-jre</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<fork>true</fork>
<executable>C:\Program Files\Java\jdk1.8.0_131\bin\javac</executable>
</configuration>
</plugin>
</plugins>
</build>
</project>
But it didn't resolve my issue.
I also checked %JAVA_HOME% and path to JDK/JRE and seems to be fine. What I missed?
Oh, duck method...
I added import com.google.common.base.Predicate; on the top and IntelliJ IDEA posted on red Predicate part with a tip to add it to a classpath
Sorry for dumb question
I faced same issue for Predicate & Collect, then added below and it worked
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-base</artifactId>
<version>r03</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-collections</artifactId>
<version>r03</version>
</dependency>
Springfox 3.x removes dependencies on guava and other 3rd party libraries (not zero dep yet! depends on spring plugin and open api libraries for annotations and models) so if you used guava predicates/functions those will need to transition to java 8 function interfaces

Spring boot generated jar file just work inside root of project

I am new in Spring boot and I think this issue is very basic.
I created an application using Spring Boot and everything is good in dev environment. But when I copy the jar file from target directory to another machine and run "java -jar" it doesn't render jsp pages with following error:
There was an unexpected error (type=Not Found, status=404)
when I copy src folder from project root to same location it works fine.
It seems the jar file just work from project's root.
Here is my configurations:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot Section -->
<!-- Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/webapp</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
and my project structure:
and I run following at command line:
mvn clean package
I have following in my target directory:
I can run the application using
java -jar target/springboot-in-10-steps-0.0.1.jar
Application works as expected and it renders welcome page.
When I go to target directory and run same command:
java -jar springboot-in-10-steps-0.0.1.jar
Application gets launch but it doesn't render the welcome page.
When I copy src to target everything is fine and I can see welcome page. It seems Spring boot doesn't find WEB-INF/jsp directory in the jar file.
Did I miss something in spring boot configuration or application.properties?
I could fix it by changing packaging from jar to war and interestingly this works:
java -jar springboot-in-10-steps-0.0.1.war
I think there are ways to change the structure of files and folders within generated jar file using repackage features.

So how do I set a Thymeleaf template resolver for my springboot webapp then

I built a spring-boot webapp with a single home.html file under src/main/resources/template and when I return "home" from my spring controller it works perfectly. I'm packaging it as a war and it runs wonderfully with spring-boot's embedded tomcat server. If I run it with mvn spring-boot:run, that is.
But later if I start my application with java -jar myjar.war it claims the server has started up and all is well, but when I try to access it at localhost:8080 it fails with:
Error resolving template "home", template might not exist or might not be accessible by any of the configured Template Resolvers
So what do I have to do next then?
This is my pom.xml, by the way:
<groupId>com.ciber</groupId>
<artifactId>energyworx-conversion-tool</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
</parent>
<properties>
<java.version>1.7</java.version>
<start-class>com.ciber.ewct.WebApp</start-class>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<!-- <jvmArguments>-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000</jvmArguments> -->
</configuration>
</plugin>
</plugins>
</build>
After turning debug logging level on, I see this about thymeleaf configuration, which is the same for when I run it with mvn spring-boot:run and java -jar:
[THYMELEAF] * Cache Factory implementation: org.thymeleaf.cache.StandardCacheManager
[THYMELEAF] * Template modes:
[THYMELEAF] * LEGACYHTML5
[THYMELEAF] * XHTML
[THYMELEAF] * HTML5
[THYMELEAF] * VALIDXML
[THYMELEAF] * VALIDXHTML
[THYMELEAF] * XML
[THYMELEAF] * Template resolvers (in order):
[THYMELEAF] * org.thymeleaf.templateresolver.TemplateResolver
[THYMELEAF] * Message resolvers (in order):
[THYMELEAF] * org.thymeleaf.spring4.messageresolver.SpringMessageResolver
[THYMELEAF] * Dialect [1 of 2]: org.thymeleaf.spring4.dialect.SpringStandardDialect
[THYMELEAF] * Prefix: "th"
[THYMELEAF] * Dialect [2 of 2]: nz.net.ultraq.thymeleaf.LayoutDialect
[THYMELEAF] * Prefix: "layout"
[THYMELEAF] TEMPLATE ENGINE CONFIGURED OK
I'm sad to say that you are actually doing it wrong.
You configured your build to exclude the webapp server from your package.
<packaking>war</packaging>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
With this setup, you have a war that can be deployed inside an existing Servlet container. You won't be able to start your app with a java -jar command.
If you want to run you webapp with a java command line. Replace your packaging to jar and remove the provided instruction from your pom.
<packaking>jar</packaging>
...
<!--
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
-->
Then you will have an embedded tomcat inside your jar and the command java -jar myjar.jar will properly.
Edit : Finally, I would raise the log level on Spring Web package to see how the Template Resolver tries to lookup your template.
Regards,
Daniel
I seem to have fixed it, with #DanielLavoie's help, by reverting all the changes I made to make the application a deployable war.
Those were:
remove the <packaking>war</packaging> so that a jar is generated instead;
remove <scope>provided</scope> from the dependency spring-boot-starter-tomcat;
remove extends SpringBootServletInitializer from my Application class;
I don't like the solution because according to the documentation I followed you'd be able to make the war deployable and also standalone enabled. But it'll have to do for now.

Grizzly 2.3.11 - CLStaticHttpHandler - cannot read index.html under resource folder when packed in a jar

I want to host static web pages in a jar. So I used Maven to pack the java project containing a folder having a index.html web page. My code:
server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig, start);
server.getServerConfiguration().addHttpHandler();new CLStaticHttpHandler(Server.class.getClassLoader(), myfolder/), /mysite)
When I access http://localhost:8080/mysite/ in IDE, the handler is able to read index.html. But if I use mvn package and run the jar file, http://localhost:8080/mysite/ doesn't work, unless I specify http://localhost:8080/mysite/index.html in a browser to make it work. The web page folder is under src/main/resources, and it is under the root path when opening the jar.
Thank you so much!
Added: To reproduce this, you can create a Maven project by writing a pom.xml and put something like
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>simple-service</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>simple-service</name>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>com.wordnik</groupId>
<artifactId>swagger-jersey2-jaxrs_2.10</artifactId>
<scope>compile</scope>
<version>1.3.4</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>${jersey.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<inherited>true</inherited>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<jersey.version>2.6</jersey.version>
<grizzly.version>2.3.11</grizzly.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
and create a server like:
final ResourceConfig rc = new ResourceConfig();
rc.register(new LoggingFilter(Logger.getLogger(Main.class.getName()), true));
return GrizzlyHttpServerFactory.createHttpServer(new URI(BASE_URI), rc);
server.getServerConfiguration()
.addHttpHandler(new CLStaticHttpHandler(ServletSimple.class.getClassLoader(), "statichtmlfolder/"), "/ui/" );
System.out.println(String.format("Jersey app started with WADL available at " + "%sapplication.wadl\nHit enter to stop it...", BASE_URI));
System.in.read();
server.shutdown();
statichtmlfolder is a folder containing all the index.html file under /src/main/resources/. we are using Jersey2 here. And use mvn package to package the code to a jar file, go to target folder, then run java -cp dependency/*:api-server-1.0.26-SNAPSHOT.jar com.example.Main. We can see the statichtmlfolder is under the root directory in the jar file.
The bug is fixed in Grizzly 2.3.13
https://java.net/jira/browse/GRIZZLY-1687

Resources