Spring boot: resolve JSPs from jar dependencies - spring-boot

Unfortunately haven't found an answer in official documentation. Maybe what I'm trying to do is not supported event by Tomcat, but still. Is it possible to make spring-boot/Tomcat to resolve JSP pages from .jar file that is in the classpath?
I have a spring-boot (2) application that is packed as a war file. There are a numerous jsp pages in 'webapp/view' folder, and appropriate MVC configuration:
#Configuration
public class MVCConf implements WebMvcConfigurer {
// ...
#Bean
public ViewResolver internalResourceViewResolver() {
return new InternalResourceViewResolver(){{
setPrefix("/view/");
setSuffix(".jsp");
setRedirectHttp10Compatible(false);
}};
}
// ...
}
All these pages are being resolved. Okay.
But. The project is a multi-module maven one. It supports builds with different dependencies (my own modules) depending on maven profiles.
I need to make application to resolve JSPs from those optional dependencies that are included into runtime as jars in a classpath.
And I'm getting Whitelabel error that says that JSP files can not be found.
Is it even possible to make it work? And if it is, than how?
P.S.: I have already tried to make some magic with copying JSPs into "root" spring-boot application itself and it works, but this way is dirty and tricky.

I don't think it is worth to publish the whole pom.xml, but here is the maven-dependency-plugin section that works in my case:
<!-- ... -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<!-- this skip property is set by maven profile in same pom.xml - no magic here -->
<skip>${exclude.some_submodule}</skip>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>some_submodule</artifactId>
<version>${project.version}</version>
<type>jar</type>
<overWrite>true</overWrite>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
<!-- there are webapp/view/*.jsp files in some_module's structure -->
<includes>view/**</includes>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<!-- ... -->
The only problem here is that it works only when you launch the executable .war. Launching this application from IDE (IntelliJ IDEA) need some additional steps to be made. And that's another one disadvantage of this solution, to my mind. The first one is a dirty pom.xml.
TL;DR
Solution:
BUT!. I have found another solution that is more suitable, I think.
Actually I have moved from Tomcat to Jetty, and solution above works fine even there. There's no need in hacking the build anymore.
Now I put my .jsp files into src/main/webapp/META-INF/resources/view/some_module folder in some_module dependency and resolve it by path 'some_module/someJsp' via standard Spring's #Controllers.
I apologize that I haven't found this topic earlier. Now this is a duplicate. But who knows, maybe someone will apply solution with maven dependency plugin.

Related

Quarkus Endpoints in dependency jars

I have a quarkus application which has dependencies to another maven module within the same project
within that module are REST endpoints
For some strange reason i cannot access those endpoints tho.. It seems quarkus will only accept endpoints of java classes within the quarkus module, or am I mistaken?
I foudn a solution:
if yopu add the jandex, endpoiints of other modules are being scanned, and can thus be found :
<build>
<plugins>
<plugin>
<groupId>org.jboss.jandex</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<version>1.1.0</version>
<executions>
<execution>
<id>make-index</id>
<goals>
<goal>jandex</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
You can do this by creating a dummy extended class:
Lets assume your imported jar has this pattern, app\proto-gen\1.0-SNAPSHOT\proto-gen-1.0-SNAPSHOT.jar
Add the below to application.properties,
quarkus.index-dependency.mygrpc.group-id=app
quarkus.index-dependency.mygrpc.artifact-id=proto-gen
#Singleton
MyGrpc extends XImplBase{
//your implementation
}
beans you extended/implemented in your current project will be started.
Check https://quarkus.io/guides/cdi-reference. You need to add a beans.xml to external models, create an index or reference the dependency using quarkus.index-dependency in the application.properties.
Then it will work when running tests or using the runner. But not in dev, because there is a probably in the current version (1.1.1Final). This problem has been fixed in the master, though, and will be available in the next release next month.
Please check ClassCastException in Quarkus multi-module project for more details.

JRebel does not working with Spring-Boot app in Intellij IDEA

I have set up JRebel in Intellij IDEA for spring-boot project, I have followed all the steps to install it in the correct way, but it is still not working, I have the following pom.xml configuration:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<plugin>
<groupId>org.zeroturnaround</groupId>
<artifactId>jrebel-maven-plugin</artifactId>
<version>1.1.7</version>
<configuration>
<addResourcesDirToRebelXml>true</addResourcesDirToRebelXml>
</configuration>
<executions>
<execution>
<id>generate-rebel-xml</id>
<phase>process-resources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
Box is also checked:
The funny thing is when I do changes in the source code and then go to the bytecode or .properties I can see the changes that have been made, but nothing is changing on the front-end...
Every time I compile the changed file I can see the following message in the event log:
From which I can conclude that changes should be applied.
I'm using out of the box servlet container which provides spring-boot, if I'm not wrong it is - Tomcat.
If any one knows, can you help me where am I wrong?
how do you start your SpringBoot application? With start buttons in IntelliJ or using the command line? I think you can remove the JRebel plugin from Maven pom.xml if you make sure that checking boxes in the JRebel panel generates a file called "rebel.xml". If the rebel.xml pom is generated, just verify it's content if it points to the folder with your compiled classes. After that, run "mvn clean compile package" and rerun your application again. If you start your application using buttons in IntelliJ, use JRebel buttons instead of normal ones. If from the command line, follow instructions in the plugin menu.

How to compile JSPs via Maven, but without failing on errors?

I've just started working on a large project that has many JSPs, many of which were created long ago, and some of which were generated. I would like to use the jetty-jspc-maven-plugin from org.eclipse.jetty to compile our JSPs for use in Tomcat 8.5. Unfortunately, some of the JSPs do not compile cleanly, and when there is a compilation problem, the maven build fails and stops.
The JspcMojo class does most of the work. It has an embedded class, JspcMojo.JettyJspC that extends org.apache.jasper.JspC and has a failOnError property. The documentation for JettyJspC says, "JettyJspC Add some extra setters to standard JspC class to help configure it for running in maven." So, it seems like I ought to be able to set the failOnError property to false and be done. I have tried all of the following, without success. How can I pass the failOnError property from maven to the JSP compiler?
<jspc.failOnError>false</jspc.failOnError>
<org.apache.jasper.compiler.failOnError>false</org.apache.jasper.compiler.failOnError>
<org.apache.jasper.JspC.failOnError>false</org.apache.jasper.JspC.failOnError>
<maven.compiler.failOnError>false</maven.compiler.failOnError>
<JettyJspC.failOnError>false</JettyJspC.failOnError>
<JspcMojo.JettyJspC.failOnError>false</JspcMojo.JettyJspC.failOnError>
<org.eclipse.jetty.jspc.plugin.JspcMojo.JettyJspC.failOnError>false</org.eclipse.jetty.jspc.plugin.JspcMojo.JettyJspC.failOnError>
BTW, compiling JSPs using ant is fairly well documented. I want to do the equivalent using maven.
Under the configuration section, you can use a subelement of the jspc element, like so:
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jspc-maven-plugin</artifactId>
<version>9.4.7.v20170914</version>
<executions>
<execution>
<id>jspc</id>
<goals>
<goal>jspc</goal>
</goals>
<configuration>
<webAppSourceDirectory>${basedir}/target/overlaidjsps</webAppSourceDirectory>
<webXml>${basedir}/src/main/webapp/WEB-INF/web.xml</webXml>
<webXmlFragment>${basedir}/target/webfrag.xml</webXmlFragment>
<!-- The comma separated list of patterns for file extensions to be processed. -->
<includes>**/*.jsp</includes>
<jspc><failOnError>false</failOnError></jspc>
</configuration>
</execution>
</executions>
</plugin>

How to hot redeploy non-active maven project via jetty-maven-plugin

I'm trying to evaluate jetty for rapid development or project which is currently running on tomcat. My configuration looks like
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.2.3.v20140905</version>
<configuration>
<scanIntervalSeconds>3</scanIntervalSeconds>
<webApp>
<descriptor>${project.build.directory}/${project.build.finalName}/WEB-INF/web.xml</descriptor>
<resourceBases>
<directory>${basedir}/src/main/webapp</directory>
<directory>${basedir}/../SharedWeb/src/main/webapp</directory>
</resourceBases>
<allowDuplicateFragmentNames>true</allowDuplicateFragmentNames>
<contextPath>/test</contextPath>
</webApp>
</configuration>
</plugin>
I have main war depending on SharedWeb war via war overlay mechanism. I specify resourceBases for both maven projects so changes in resources are scanned automatically and reloaded on the fly and all working fine. Also when I compile classes in main war, jetty restarts automatically, reloading the latest changes. But when I try to change any class in SharedWeb project and compile it, the class is not reloaded. I'm just wondering if there is a way to make embed jetty to reload classes from SharedWeb automatically? I understand that jetty-maven-plugin uses SharedWeb war from local maven repository, so I need to install SharedWeb artifact before I can see any changes. So I don't have high expectations, but maybe I'm missing something.
Ivan,
The plugin is using the classes and resources from your dependency
war, NOT from the that you have added. The
simply tells jetty to watch that location and redeploy if something in
it changes - it does NOT put it onto the classpath.
You need to tell jetty to use the classes and resources from your
dependency war's project, NOT the war artifact.
So do something like:
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.2.3.v20140905</version>
<configuration>
<webApp>
<!-- tell jetty to use the classes from the dependency
webapp project directly -->
<extraClassPath>${basedir}/../SharedWeb/target/classes</extraClassPath>
<!-- tell jetty to use both this project's static
resources, and those of the dependency webapp project -->
<resourceBases>
<directory>${basedir}/src/main/webapp</directory>
<directory>${basedir}/../SharedWeb/src/main/webapp</directory>
</resourceBases>
</webApp>
<scanIntervalSeconds>3</scanIntervalSeconds>
<!-- tell jetty to watch the dependency webapp project classes
dir for changes -->
<scanTargets>
<scanTarget>${basedir}/../SharedWeb/target/classes/</scanTarget>
</scanTargets>
</configuration>
</plugin>
Jan
Since there doesn't seem to be a good prior answer that is specific enough for this question (aka <scanTarget>) I'll just post this new one and tweak the title to make it easier to find in the future.
What you are looking for is <scanTarget>, as that will allow you to customize the scanning locations for changed content that will trigger a hot redeploy.
The jetty-maven-plugin intentionally does not set this up for custom <resourceBases> as there are far to many legitimate use cases where this can cause aggressive / too often / or infinite redeploys. It was decided that it was best to break from "convention over configuration" for <scanTarget> entries and allow the developers to decide what should be scanned for changes.
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.2.3.v20140905</version>
<configuration>
...
<scanIntervalSeconds>3</scanIntervalSeconds>
<scanTargets>
<scanTarget>${basedir}/../SharedWeb/src/main/webapp/</scanTarget>
<scanTarget>${basedir}/../SharedWeb/target/classes/</scanTarget>
</scanTargets>
</configuration>
</plugin>

Best way to hand a WAR file from one Maven project to another, to use with Embedded Jetty?

I thought this was simple, but having problems:
Project1 is of type war. It creates an entire webapp .war file, including some Apache modules (solr/lucene), and some of our custom code.
Project2 is an existing application. It needs to launch embedded Jetty to do queries against Project1's war file. (see code below)
Main Problem:
When Project2 instantiates Jetty, it needs to pass in the full path to the WAR file, but this changes each time. Maven adds version number stuff to Project1's war file.
Assemblies to the Rescue?
I'm able to get a custom assembly to work, but can't get rid of the versioning stamp from Project1.
But I always wind up with Project1-1.4.1-20120530.233546-2.war. It's in a more convenient place, but the name is still weird.
Jetty code in Project2:
// Context
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/");
String jettyHome = System.getProperty( "jetty.home", ".." );
String fullWarName = ...; // Project1's WAR file. This path always changes
webapp.setWar( fullWarName );
// Server
Server server = new Server( kPort ); // TODO: get from config
server.setHandler(webapp);
server.start();
server.join();
Other considerations:
I realize there is a maven-jetty plugin, but I don't believe that's appropriate here. It seems to be targeted at Unit tests, and also our application stack doesn't use maven at runtime to launch services.
I'm also aware that Solr has a fully embedded version that doesn't require a web container, but that's been deprecated for a while and not a good idea to use.
Is there a better way to refactor this project? Maybe this isn't "the maven way" ?
It turns out I didn't need an assembly (advice I had got internally), instead there's something easier in the main pom. Also, having the war unpacked here turned out to be a nice idea.
At the top of Project1's pom.xml I have:
<groupId>com.my.group</groupId>
<artifactId>project-one</artifactId>
<version>1.2.3-SNAPSHOT</version>
<packaging>war</packaging>
This goes near the bottom of Project2's pom.xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-webapp</id>
<phase>package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.my.group</groupId>
<artifactId>project-one</artifactId>
<version>1.2.3-SNAPSHOT</version>
<type>war</type>
<overWrite>true</overWrite>
<outputDirectory>${project.build.directory}/webapps/project-one</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
Then when launching Jetty I have:
webapp.setWar( "target/webapps/project-one" );
I still think there might be issues with some Jetty settings, but I think this is the right direction.

Resources