简体   繁体   中英

Is it possible with Spring Boot to serve up JSPs with a JAR packaging?

I'm familiar with the Spring Boot JSP sample application

However that example uses the WAR packaging. Is it possible to do the same with <packaging>JAR</packaging> ?

My goal is to put JSP s under src/main/resources/jsp to simplify the structure of my app.

As @Andy Wilkinson said, there are limitations related to JSP. Please package your application as war and execute as jar . This is documented at spring site.

With Tomcat it should work if you use war packaging, ie an executable war will work (...). An executable jar will not work because of a hard coded file pattern in Tomcat .


Deprecated, old answer

Yes, this is possible with Spring Boot.

Take look at this example: https://github.com/mariuszs/spring-boot-web-jsp-example .

For doing this use spring-boot-maven-plugin or gradle equivalent.

With this plugin jar is executable and can serve JSP files.

$ mvn package
$ java -jar target/mymodule-0.0.1-SNAPSHOT.war 

(Note the extension of the artifact in the above command .war) or just

$ mvn spring-boot:run

If your springboot is building your project and running it in local server correctly then YES . What you need to do is to build the project using mvn -U clean package . Then in target folder you have your runnable xxxx.jar . Now what you have to do is put your xxxx.jar file in the server or where you want along with the src/main/webapp/WEB-INF/jsp/ *.jsp files in same hierarchy. then try java -jar xxxx.jar Your project will run with no issue.

`
.
├── src
│   └── main
│       └── webapp
│           └── WEB-INF
│               ├── jsp
│               │   ├── default.jsp
│               │   ├── help.jsp
│               │   ├── index.jsp
│               │   ├── insert.jsp
│               │   ├── login.jsp
│               │   ├── modify.jsp
│               │   ├── search.jsp
│               │   └── show.jsp
│               └── web.xml
├── xxx.jar
└── xxx.jar.original`

java -jar xxx.jar

<pre>    
$java -jar xxx.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.4.RELEASE)

2017-09-05 19:31:05.009  INFO 10325 --- [           main] com.myapp.app.DemoApplication         : Starting DemoApplication v0.0.1-SNAPSHOT on dipu-HP with PID 10325 (/home/dipu/Documents/workspace-sts/jspjartest/xxx.jar started by dipu in /home/dipu/Documents/workspace-sts/jspjartest)
2017-09-05 19:31:05.014  INFO 10325 --- [           main] com.myapp.app.DemoApplication         : No active profile set, falling back to default profiles: default
2017-09-05 19:31:05.138  INFO 10325 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@6e5e91e4: startup date [Tue Sep 05 19:31:05 IST 2017]; root of context hierarchy
2017-09-05 19:31:07.258  INFO 10325 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8090 (http)
2017-09-05 19:31:07.276  INFO 10325 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2017-09-05 19:31:07.278  INFO 10325 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.15
2017-09-05 19:31:08.094  INFO 10325 --- [ost-startStop-1] org.apache.jasper.servlet.TldScanner     : At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
2017-09-05 19:31:08.396  INFO 10325 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2017-09-05 19:31:08.401  INFO 10325 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3267 ms
2017-09-05 19:31:08.615  INFO 10325 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2017-09-05 19:31:08.617  INFO 10325 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'loginServlet' to [/loginServlet/]
2017-09-05 19:31:08.618  INFO 10325 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'uploadController' to [/uploadController/]
2017-09-05 19:31:08.622  INFO 10325 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2017-09-05 19:31:08.622  INFO 10325 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2017-09-05 19:31:08.623  INFO 10325 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2017-09-05 19:31:08.623  INFO 10325 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2017-09-05 19:31:09.137  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@6e5e91e4: startup date [Tue Sep 05 19:31:05 IST 2017]; root of context hierarchy
2017-09-05 19:31:09.286  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/user-management]}" onto java.lang.String com.myapp.app.DemoController.user()
2017-09-05 19:31:09.288  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto java.lang.String com.myapp.app.DemoController.reload()
2017-09-05 19:31:09.290  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/show]}" onto java.lang.String com.myapp.app.DemoController.show()
2017-09-05 19:31:09.292  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/modify]}" onto java.lang.String com.myapp.app.DemoController.modify()
2017-09-05 19:31:09.293  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/admin]}" onto java.lang.String com.myapp.app.DemoController.admin()
2017-09-05 19:31:09.294  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/login]}" onto java.lang.String com.myapp.app.DemoController.login()
2017-09-05 19:31:09.294  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/faq-management]}" onto java.lang.String com.myapp.app.DemoController.faq()
2017-09-05 19:31:09.294  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/subject-area]}" onto java.lang.String com.myapp.app.DemoController.subject()
2017-09-05 19:31:09.295  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/Chat]}" onto java.lang.String com.myapp.app.DemoController.index()
2017-09-05 19:31:09.295  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/delete]}" onto java.lang.String com.myapp.app.DemoController.delete()
2017-09-05 19:31:09.296  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/result]}" onto java.lang.String com.myapp.app.DemoController.result()
2017-09-05 19:31:09.296  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/insert]}" onto java.lang.String com.myapp.app.DemoController.insert()
2017-09-05 19:31:09.300  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/fileUpload]}" onto java.lang.String com.myapp.app.DemoController.file()
2017-09-05 19:31:09.301  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/search]}" onto java.lang.String com.myapp.app.DemoController.search()
2017-09-05 19:31:09.301  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/help]}" onto java.lang.String com.myapp.app.DemoController.help()
2017-09-05 19:31:09.312  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/LoginServlet]}" onto public void com.myapp.app.LoginServlet.LoginServlet.doPost(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws javax.servlet.ServletException,java.io.IOException
2017-09-05 19:31:09.313  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/image],methods=[GET],produces=[text/html;charset=UTF-8]}" onto public java.lang.String com.myapp.app.controller.ImageController.image()
2017-09-05 19:31:09.316  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/authenticate],methods=[POST]}" onto public java.lang.String com.myapp.app.controller.AjaxController.authenticate(java.lang.String,java.lang.String) throws java.io.IOException
2017-09-05 19:31:09.317  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/searchCompany],methods=[GET]}" onto public java.lang.String com.myapp.app.controller.AjaxController.getCompany(java.lang.String,java.lang.String) throws java.io.IOException
2017-09-05 19:31:09.318  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/executeQuery],methods=[GET]}" onto public java.lang.String com.myapp.app.controller.AjaxController.executeQuery(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String) throws java.io.IOException
2017-09-05 19:31:09.318  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/insertData],methods=[POST]}" onto public int com.myapp.app.controller.AjaxController.insertResources(java.lang.String,java.lang.String,java.lang.String) throws java.io.IOException
2017-09-05 19:31:09.319  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/showData],methods=[GET]}" onto public java.lang.String com.myapp.app.controller.AjaxController.showResources(java.lang.String) throws java.io.IOException
2017-09-05 19:31:09.319  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/searchData],methods=[POST]}" onto public java.lang.String com.myapp.app.controller.AjaxController.getSearchData(java.lang.String,java.lang.String) throws java.io.IOException
2017-09-05 19:31:09.319  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/deleteData],methods=[POST]}" onto public java.lang.String com.myapp.app.controller.AjaxController.deleteData(java.lang.String,java.lang.String) throws java.io.IOException
2017-09-05 19:31:09.320  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/modifyData],methods=[POST]}" onto public int com.myapp.app.controller.AjaxController.modifyData(java.lang.String,java.lang.String,java.lang.String) throws java.io.IOException
2017-09-05 19:31:09.322  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/suggestWords],methods=[POST]}" onto public java.lang.String com.myapp.app.controller.AjaxController.suggestWords(java.lang.String,java.lang.String) throws java.io.IOException
2017-09-05 19:31:09.322  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/authenticateUser],methods=[POST]}" onto public java.lang.String com.myapp.app.controller.AjaxController.authenticateUser(java.lang.String) throws java.io.IOException
2017-09-05 19:31:09.323  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/insertJson],methods=[POST]}" onto public int com.myapp.app.controller.AjaxController.insertJsonResources(java.lang.String,java.lang.String) throws java.io.IOException
2017-09-05 19:31:09.323  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/getvalue],methods=[GET]}" onto public java.lang.String com.myapp.app.controller.AjaxController.getResource(java.lang.String,java.lang.String) throws java.io.IOException
2017-09-05 19:31:09.324  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/searchQuery],methods=[GET]}" onto public java.lang.String com.myapp.app.controller.AjaxController.getResources(java.lang.String,java.lang.String) throws java.io.IOException
2017-09-05 19:31:09.330  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/upload],methods=[POST]}" onto public void com.myapp.app.controller.UploadController.doPost(org.springframework.web.multipart.MultipartFile,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws javax.servlet.ServletException,java.io.IOException
2017-09-05 19:31:09.333  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2017-09-05 19:31:09.334  INFO 10325 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2017-09-05 19:31:09.388  INFO 10325 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-09-05 19:31:09.388  INFO 10325 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-09-05 19:31:09.461  INFO 10325 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-09-05 19:31:09.752  INFO 10325 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-09-05 19:31:09.861  INFO 10325 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8090 (http)
2017-09-05 19:31:09.867  INFO 10325 --- [           main] com.myapp.DemoApplication         : Started DemoApplication in 5.349 seconds (JVM running for 5.866)

</pre>

here the server is running

$ curl 127.0.0.1:8090/login Welcome to Login page

My POM.xml

<code>
    
  
 
  
  
  
        <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>
        <groupId>com.myapp</groupId>
        	<artifactId>app</artifactId>
        	<version>0.0.1-SNAPSHOT</version>
        	<packaging>jar</packaging> 
        	<name>Jsp Springboot</name>
        	<description>Jsp Springboot</description> 
        	<parent>
        		<groupId>org.springframework.boot</groupId>
        		<artifactId>spring-boot-starter-parent</artifactId>
        		<version>1.5.4.RELEASE</version>
        		<relativePath/> 
        	</parent> 
        	<properties>
        		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        		<java.version>1.8</java.version>
        	</properties> 
        	<dependencies>
        		<dependency>
        			<groupId>org.springframework.boot</groupId>
        			<artifactId>spring-boot-starter</artifactId>
        		</dependency> 
        		<dependency>
        			<groupId>org.springframework.boot</groupId>
        			<artifactId>spring-boot-starter-test</artifactId>
        			<scope>test</scope>
        		</dependency>
        		<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>
        			<scope>provided</scope>
        		</dependency> 
        		
        		
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            </dependency> 
        		
        		<dependency>
        			<groupId>org.apache.tomcat.embed</groupId>
        			<artifactId>tomcat-embed-jasper</artifactId>
        			<scope>provided</scope>
        		</dependency> 
        		<!-- Need this to compile JSP,
        			tomcat-embed-jasper version is not working, no idea why -->
        		<dependency>
        			<groupId>org.eclipse.jdt.core.compiler</groupId>
        			<artifactId>ecj</artifactId>
        			<version>4.6.1</version>
        			<scope>provided</scope>
        		</dependency>
        		 
        		
        		<dependency>
        			<groupId>org.webjars</groupId>
        			<artifactId>bootstrap</artifactId>
        			<version>3.3.7</version>
        		</dependency>
        		
        		
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
        </dependency>
        		
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>rest</artifactId>
            <version>5.5.1</version>
        </dependency>
        		
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.0</version>
        </dependency>
            
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi</artifactId>
                <version>3.16</version>
            </dependency>
            
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>3.16</version>
            </dependency>
            
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml-schemas</artifactId>
                <version>3.16</version>
            </dependency>
    
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency> 
        	</dependencies> 
        	<build>
        		<plugins>
        			<plugin>
        				<groupId>org.springframework.boot</groupId>
        				<artifactId>spring-boot-maven-plugin</artifactId>
        			</plugin>
        <plugin>
        				<groupId>org.apache.maven.plugins</groupId>
        				<artifactId>maven-surefire-plugin</artifactId>
        				<configuration>
        					<useSystemClassLoader>false</useSystemClassLoader>
        				</configuration>
        			</plugin> 
        		
        		</plugins>
        	</build>
        </project>
    
</code>

my application.properties

<code>
    spring.mvc.view.prefix: /WEB-INF/jsp/
    spring.mvc.view.suffix: .jsp
    server.port=8090
</code>

My Project Structure

<code>
.
├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── myapp
│       │           └── app
│       │               ├── mynewapp
│       │               ├── controller
│       ├── resources
│       │   └── static
│       │       ├── assets
│       │       │   ├── css
│       │       │   │   ├── fonts
│       │       │   │   └── lib
│       │       │   ├── img
│       │       │   └── js
│       │       │       └── lib
│       │       ├── css
│       │       │   └── img
│       │       ├── fonts
│       │       ├── images
│       │       └── js
│       │           └── img
│       └── webapp
│           └── WEB-INF
│               ├── jsp
│               └── lib
</code>

Thank You.

Your best bet is to change the packaging type into war, and it should work without further changes.

As mentioned in on of the comments above, there are some limitations:

http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html#boot-features-jsp-limitations

27.3.5 JSP limitations

When running a Spring Boot application that uses an embedded servlet container (and is packaged as an executable archive), there are some limitations in the JSP support.

With Tomcat it should work if you use war packaging, ie an executable war will work, and will also be deployable to a standard container (not limited to, but including Tomcat).

An executable jar will not work because of a hard coded file pattern in Tomcat.

With Jetty it should work if you use war packaging, ie an executable war will work, and will also be deployable to any standard container.

Undertow does not support JSPs.

Creating a custom error.jsp page won't override the default view for error handling, custom error pages should be used instead.

source

If for whatever reason, you can't deal with a war packaging, there's a hack. Full credit to this guy for doing it for an older version of spring-boot.

One way to do this is to personalize tomcat and add BOOT-INF/classes to tomcat's ResourceSet. In tomcat, all scanned resources are put into something called a ResourceSet. For example, the META-INF/resources of the application jar package in the servlet 3.0 specification is scanned and put into the ResourceSet.

Now we need to find a way to add the BOOT-INF/classes directory of the fat jar to the ResourceSet. We can do this through the tomcat LifecycleListener interface, in the Lifecycle.CONFIGURE_START_EVENT event, get the BOOT-INF/classes URL, and then add this URL to the WebResourceSet. A complete example is here , but you can do this like so:

import org.apache.catalina.Context;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnProperty(name = "tomcat.staticResourceCustomizer.enabled", matchIfMissing = true)
public class TomcatConfiguration {
    @Bean
    public WebServerFactoryCustomizer<WebServerFactory> staticResourceCustomizer() {
        return new WebServerFactoryCustomizer<WebServerFactory>() {

        @Override
        public void customize(WebServerFactory factory) {
            if (factory instanceof TomcatServletWebServerFactory) {
                ((TomcatServletWebServerFactory) factory)
                        .addContextCustomizers(new org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer() {
                            @Override
                            public void customize(Context context) {
                                context.addLifecycleListener(new StaticResourceConfigurer(context));
                            }
                        });
                }
            }
        };
    }
}

And then use the LifecycleListener like so:

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.catalina.Context;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.WebResourceRoot.ResourceSetType;
import org.springframework.util.ResourceUtils;

public class StaticResourceConfigurer implements LifecycleListener {

    private final Context context;

    StaticResourceConfigurer(Context context) {
        this.context = context;
    }

    @Override
    public void lifecycleEvent(LifecycleEvent event) {
        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
            URL location = this.getClass().getProtectionDomain().getCodeSource().getLocation();

            if (ResourceUtils.isFileURL(location)) {
                // when run as exploded directory
                String rootFile = location.getFile();
                if (rootFile.endsWith("/BOOT-INF/classes/")) {
                    rootFile = rootFile.substring(0, rootFile.length() - "/BOOT-INF/classes/".length() + 1);
                }
                if (!new File(rootFile, "META-INF" + File.separator + "resources").isDirectory()) {
                    return;
                }

                try {
                    location = new File(rootFile).toURI().toURL();
                } catch (MalformedURLException e) {
                    throw new IllegalStateException("Can not add tomcat resources", e);
                }
            }

            String locationStr = location.toString();
            if (locationStr.endsWith("/BOOT-INF/classes!/")) {
                // when run as fat jar
                locationStr = locationStr.substring(0, locationStr.length() - "/BOOT-INF/classes!/".length() + 1);
                try {
                    location = new URL(locationStr);
                } catch (MalformedURLException e) {
                    throw new IllegalStateException("Can not add tomcat resources", e);
                }
            }
            this.context.getResources().createWebResourceSet(ResourceSetType.RESOURCE_JAR, "/", location,
                    "/META-INF/resources");

        }
    }
}

Spring Boot is pretty good with JSP and it's little easy also just you need below configuration

1 - tomcat-embad-jasper dependency

<dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-jasper</artifactId>
      <scope>provided</scope>
</dependency>

2 - Add below configuration is application.properties

spring.mvc.view.prefix: /
spring.mvc.view.suffix: .jsp

That's it still have some doubt then check it out below link

Spring Boot and JSP integration

I'm using JS P successfully on Spring Boot Application with jar packaging , putting my JSP files on src/main/webapp/WEB-INF/JSP directory and configuring my Application as bellow

@Bean
public InternalResourceViewResolver viewResolver() {
    InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
    viewResolver.setViewClass(JstlView.class);
    viewResolver.setPrefix("/WEB-INF/JSP/");
    viewResolver.setSuffix(".jsp");
    return viewResolver;
}

My Controller is:

@Controller
public class HelloController {

    @RequestMapping(value = { "/", "/hello**" }, method = RequestMethod.GET)
    public ModelAndView welcomePage() {
        ModelAndView model = new ModelAndView();
        model.addObject("title", "Spring Security Example");
        model.addObject("message", "This is Hello World!");
        model.setViewName("hello");
        System.out.println("° ° ° ° welcomePage running. model = " + model);
        return model;
    }
    . . . 

And my hello.jsp is

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page session="true"%>
<html>
<body>
    <h1>Title : ${title}</h1>   
    <h1>${message}</h1>
    <h2>Options: 
        <a href="<c:url value="/hello" />" >Home</a> | 
        <a href="<c:url value="/admin" />" >Administration</a> | 
        <a href="<c:url value="/super" />" >Super user</a>
        <c:if test="${pageContext.request.userPrincipal.name != null}"> |
            <a href="<c:url value="/logout" />" >Logout</a> 
        </c:if>
    </h2>   
</body>
</html>

My pom.xml file is

<groupId>br.com.joao-parana</groupId>
<artifactId>my-spring-boot-app</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<name>my-spring-boot-app Maven Webapp</name>
<url>http://maven.apache.org</url>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.3.4.RELEASE</version>
</parent>
<properties>
    <java.version>1.8</java.version>
</properties>
<dependencies>
    . . . 
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>
<build>
    <finalName>my-spring-boot-app</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

I hope this help someone !

here is my solution:

<build>
    <resources>
        <resource>
            <directory>src/main/webapp</directory>
            <targetPath>META-INF/resources</targetPath>
            <includes>
                <include>**/**</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/**</include>
            </includes>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <!-- must use 1.4.2 version -->
            <version>1.4.2.RELEASE</version>
        </plugin>
    </plugins>
</build>

Hi I figer out how to copy jsp's from jar to embedded tomcat folder

package com.demosoft.stlb.loadbalancer;


import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.ServletContextAware;

import javax.annotation.PostConstruct;
import javax.servlet.ServletContext;
import java.io.*;
import java.net.MalformedURLException;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

@Component
public class JarFileResourcesExtractor implements ServletContextAware {

private String resourcePathPattern = "WEB-INF/**";
private String resourcePathPrefix = "WEB-INF/";
private String destination = "/WEB-INF/";
private ServletContext servletContext;
private AntPathMatcher pathMatcher = new AntPathMatcher();



/**
 * Extracts the resource files found in the specified jar file into the destination path
 *
 * @throws IOException           If an IO error occurs when reading the jar file
 * @throws FileNotFoundException If the jar file cannot be found
 */
@PostConstruct
public void extractFiles() throws IOException {
    try {
        JarFile jarFile = (JarFile) getClass().getProtectionDomain().getCodeSource().getLocation().getContent();
        Enumeration<JarEntry> entries = jarFile.entries();
        System.out.println("Tomcat destination : " + servletContext.getRealPath(destination));
        while (entries.hasMoreElements()) {
            processJarEntry(jarFile, entries);
        }

    } catch (MalformedURLException e) {
        throw new FileNotFoundException("Cannot find jar file in libs: ");
    } catch (IOException e) {
        throw new IOException("IOException while moving resources.", e);
    }
}

private void processJarEntry(JarFile jarFile, Enumeration<JarEntry> entries) throws IOException {
    JarEntry entry = entries.nextElement();
    if (pathMatcher.match(resourcePathPattern, entry.getName())) {
        String fileName = getFileName(entry);
        if (checkFileName(fileName)) return;
        String sourcePath = getSourcePath(entry, fileName);
        if (checkSourcePath(sourcePath)) return;
        copyAndClose(jarFile.getInputStream(entry), getOutputStream(fileName, sourcePath));

    }
}

private FileOutputStream getOutputStream(String fileName, String sourcePath) throws IOException {
    File destinationFolder = new File(servletContext.getRealPath(destination)+sourcePath);
    destinationFolder.mkdirs();
    File materializedFile = new File(destinationFolder, fileName);
    materializedFile.createNewFile();
    return new FileOutputStream(materializedFile);
}

private boolean checkSourcePath(String sourcePath) {
    return sourcePath.startsWith(".idea");
}

private String getSourcePath(JarEntry entry, String fileName) {
    String sourcePath = entry.getName().replaceFirst(fileName, "");
    sourcePath = sourcePath.replaceFirst(resourcePathPrefix, "").trim();
    return sourcePath;
}

private boolean checkFileName(String fileName) {
    return fileName.trim().equals("");
}

private String getFileName(JarEntry entry) {
    return entry.getName().replaceFirst(".*\\/", "");
}

@Override
public void setServletContext(ServletContext servletContext) {
    this.servletContext = servletContext;
}

public static int IO_BUFFER_SIZE = 8192;

private static void copyAndClose(InputStream in, OutputStream out) throws IOException {
    try {
        byte[] b = new byte[IO_BUFFER_SIZE];
        int read;
        while ((read = in.read(b)) != -1) {
            out.write(b, 0, read);
        }
    } finally {
        in.close();
        out.close();
    }
}
}

I tried out all possible solution finally this helped me out.
Add the following dependencies in pom.xml

<!-- Need this to compile JSP -->
<dependency>
  <groupId>org.apache.tomcat.embed</groupId>
  <artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!-- Need this to compile JSP -->
<dependency>
  <groupId>org.eclipse.jdt.core.compiler</groupId>
  <artifactId>ecj</artifactId>
  <version>4.6.1</version>
</dependency>

Add the view resolver to application.properties file.

spring.mvc.view.prefix: /WEB-INF/jsp/
spring.mvc.view.suffix: .jsp

Once, we are done with above code, we will build our project, using maven commands and then execute as standalone jar using standard java's jar execution command.

cd <TO_YOUR_PROJECT_DIR>
mvn clean package spring-boot:repackage
java -jar target/<YOUR_PROJECT_CREATED_JAR>

If for some reason anybody still needs this in SB2+ we had to slightly adjust lifecycleEvent method

@Override
public void lifecycleEvent(LifecycleEvent event) {
    if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
        URL location = this.getClass().getProtectionDomain().getCodeSource().getLocation();
        if (new LocalDevelDetection().isDocker()){
            try {
                //This is necessary to make JSP work in docker --> not sure which of those mounts actually does the trick but I think /YOUR_SB_APP_NAME one works for docker-compose and the other one for "vanilla docker" (non-compose)
                context.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.RESOURCE_JAR, "/<<YOUR_SB_APP_NAME>>", new URL("jar:file:/<<PATH_TO_YOUR_SB_APP>>.jar!/"), "/META-INF/resources");
                context.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.RESOURCE_JAR, "/", new URL("jar:file:/<<PATH_TO_YOUR_SB_APP>>.jar!/"), "/META-INF/resources");
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
            }
        }
        else {
            if (ResourceUtils.isFileURL(location)) {
                // when run as exploded directory
                String rootFile = location.getFile();
                if (rootFile.endsWith("/BOOT-INF/classes/")) {
                    rootFile = rootFile.substring(0, rootFile.length() - "/BOOT-INF/classes/".length() + 1);
                }
                if (!new File(rootFile, "META-INF" + File.separator + "resources").isDirectory()) {
                    return;
                }

                try {
                    location = new File(rootFile).toURI()
                                                 .toURL();
                } catch (MalformedURLException e) {
                    throw new IllegalStateException("Can not add tomcat resources", e);
                }
            }

            String locationStr = location.toString();
            if (locationStr.endsWith("/BOOT-INF/classes!/")) {
                // when run as fat jar
                locationStr = locationStr.substring(0, locationStr.length() - "/BOOT-INF/classes!/".length() + 1);
                try {
                    location = new URL(locationStr);
                } catch (MalformedURLException e) {
                    throw new IllegalStateException("Can not add tomcat resources", e);
                }
            }
            this.context.getResources()
                        .createWebResourceSet(WebResourceRoot.ResourceSetType.RESOURCE_JAR, "/", location, "/META-INF/resources");
        }
    }
}

Oh and our LocalDevelDetection.isDocker() method:

public boolean isDocker(){
    try (Stream< String > stream =
            Files.lines(Paths.get("/proc/1/cgroup"))) {
        return stream.anyMatch(line -> line.contains("/docker"));
    } catch (IOException e) {
        return false;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM