2

I'm currently in the process of moving a small scale application to SpringBoot. We have legacy code and my hope is that by hooking the landing page into SpringBoot, I am able to let the JSP/JS files of the web application handle the rest.

Currently I've been successful rolling us up to Spring 5, and Using SpringBoot 2.1.8.

POM snippet: The application is using Tomcat defaults provided by Spring, and have pulled in the relevant Jasper/JSTL JARS so Tomcat can read

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-classic</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-classic</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

Per this DZone Article I have built a small controller to handle the login process

@Controller
public class LoginController {

    @Resource
    private UserSessionService userSessionService;

    @GetMapping(value = "/")
    public String index() {
        Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, "LoginController: Hit /, returning index.jsp");
        return "index";
    }

    @GetMapping(value = "/login")
    public String login() {
        Logger.getLogger(this.getClass().getName())
              .log(Level.SEVERE, "LoginController: Hit /login, returning login.jsp");
        return "login";
    }

    @GetMapping(value = "/loginFailed")
    public String loginError(Model model) {
        Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, "LoginController: Failed to login");
        model.addAttribute("error", "true");
        return "login";
    }

    @GetMapping(value = "/logout")
    public String logout(SessionStatus session) {
        Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, "LoginController: Logging out");
        SecurityContextHolder.getContext().setAuthentication(null);
        session.setComplete();
        return "redirect:/login";
    }

    @PostMapping(value = "/postLogin")
    public String postLogin(Model model, HttpSession session) {
        Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, "LoginController: postLogin");
        return "redirect:/";
    }

}

login.jsp : legacy code, modified to call the controller. I'm attempting a best effort to let Spring Security handle the process of calling my AuthenticaionProvider and my DB (for the sake of brevity I'm only showing the critical area stuff, the page is rather simple anyway

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>

<!DOCTYPE html>
<html>
    <head>
        <%@page contentType="text/html" pageEncoding="UTF-8" %>
        <link type="text/css" href="${contextPath}/css/login.css" rel="stylesheet"/>
        <link rel="stylesheet" href="${contextPath}/css/font-awesome/css/font-awesome.min.css">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title></title>
    </head>

    <body class="loginBody">
          .
          .
          <div class="formdiv">
              <form name='f' action='doLogin' method='POST'>
                  <div style="width: 215px; margin: 0 auto; ">
                      <label class="loginLabel">Login</label><br>
                      <input type="text" name="username".../>
                      <label class="loginLabel">Password</label><br>
                      <input type="password" size="25" name="password".../><br>
                      <input type="submit" class="header_btn_class" value="Login" id="loginButton" style="..."/>
                    </div>
              </form>
           </div>
        .
        .
    </body>
</html>

index.jsp: legacy code, handles the main landing page

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>
<%@include file="beforeHtml.jsp" %>

<html>
    <head>
        <%@include file="head.jsp" %>
        <script type="text/javascript" src="${contextPath}/js/home/home.js"></script>
        <title></title>
    </head>

    <body>
        <%@include file="top.jsp" %>


        <h2 style="margin-left: 60px;">Release Notes</h2>
        <div id="changeLog" style=...">
        </div>

        <jsp:include page="../html/Changelog.html"/>
    </body>
</html>

I have a file I've called SecurityConfig to let SpringBoot know to bypass the 'default' landing page:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests().antMatchers("/xxx").hasAnyRole("ADMIN", "USER")
                .and()
                .authorizeRequests().antMatchers("/login", "/resource/**").permitAll()
                .and()
                .formLogin().loginPage("/login").usernameParameter("username").passwordParameter("password").permitAll()
                .loginProcessingUrl("/doLogin")
                .successForwardUrl("/postLogin")
                .failureUrl("/loginFailed")
                .and()
                .logout().logoutUrl("/doLogout").logoutSuccessUrl("/logout").permitAll()
                .and()
                .csrf().disable();
    }

And I have these fields in my application.properties file

server.port=8888
server.servlet.context-path=/xxx
server.tomcat.additional-tld-skip-patterns=*mchange-commons-java*.jar

spring.mvc.view.prefix=/WEB-INF/view/jsp/
spring.mvc.view.suffix=.jsp
spring.mvc.servlet.load-on-startup=1

All of this put together allows me to login at my requested URL of localhost:8888/xxx/login just fine, and logging in, watching the logs Spring finds my set up and logs in fine. The problem is when it goes to fetch the landing page which is a JSP file named index.jsp. Per the logging by SpringBoot/Application

Oct 09, 2019 2:44:37 PM org.springframework.core.log.LogFormatUtils traceDebug
FINE: GET "/xxx/login", parameters={}
Oct 09, 2019 2:44:37 PM org.springframework.web.servlet.handler.AbstractHandlerMapping getHandler
FINE: Mapped to public java.lang.String com.rhdjapan.xxx.controllers.LoginController.login()
Oct 09, 2019 2:44:37 PM com.xx.xxx.controllers.LoginController login
SEVERE: LoginController: Hit /login, returning login.jsp
Oct 09, 2019 2:44:37 PM org.springframework.web.servlet.view.ContentNegotiatingViewResolver getBestView
FINE: Selected 'text/html' given [text/html,...]
Oct 09, 2019 2:44:37 PM org.springframework.web.servlet.view.AbstractView render
FINE: View name 'login', model {}
Oct 09, 2019 2:44:37 PM org.springframework.web.servlet.view.InternalResourceView renderMergedOutputModel
FINE: Forwarding to [/WEB-INF/view/jsp/login.jsp]
Oct 09, 2019 2:44:38 PM org.springframework.web.servlet.FrameworkServlet logResult
FINE: Completed 200 OK
Oct 09, 2019 2:44:42 PM com.xx.xxx.services.common.security.service.UserDetailsServiceImpl loadUserByUsername
SEVERE: loadUserByUsername: looking up user [admin]
Oct 09, 2019 2:44:42 PM com.xx.xxx.services.common.security.service.UserDetailsServiceImpl loadUserByUsername
SEVERE: loadUserByUsername: userId found = [1111111]
Oct 09, 2019 2:44:42 PM org.springframework.core.log.LogFormatUtils traceDebug
FINE: "FORWARD" dispatch for POST "/xxx/postLogin", parameters={username:[admin], password:[admin]}
Oct 09, 2019 2:44:42 PM org.springframework.web.servlet.handler.AbstractHandlerMapping getHandler
FINE: Mapped to public java.lang.String com.xx.xxx.controllers.LoginController.postLogin(org.springframework.ui.Model,javax.servlet.http.HttpSession)
Oct 09, 2019 2:44:42 PM com.xx.xxx.controllers.LoginController postLogin
SEVERE: LoginController: postLogin
Oct 09, 2019 2:44:42 PM org.springframework.web.servlet.view.AbstractView render
FINE: View name 'redirect:', model {}
Oct 09, 2019 2:44:42 PM org.springframework.web.servlet.FrameworkServlet logResult
FINE: Exiting from "FORWARD" dispatch, status 302
Oct 09, 2019 2:44:42 PM org.springframework.core.log.LogFormatUtils traceDebug
FINE: GET "/xxx/", parameters={}
Oct 09, 2019 2:44:42 PM org.springframework.web.servlet.handler.AbstractHandlerMapping getHandler
FINE: Mapped to public java.lang.String com.xx.xxx.controllers.LoginController.index()
Oct 09, 2019 2:44:42 PM com.xx.xxx.controllers.LoginController index
SEVERE: LoginController: Hit /, returning index.jsp
Oct 09, 2019 2:44:42 PM org.springframework.web.servlet.view.ContentNegotiatingViewResolver getBestView
FINE: Selected 'text/html' given [text/html, ...]
Oct 09, 2019 2:44:42 PM org.springframework.web.servlet.view.AbstractView render
FINE: View name 'index', model {}
Oct 09, 2019 2:44:42 PM org.springframework.web.servlet.view.InternalResourceView renderMergedOutputModel
FINE: Forwarding to [/WEB-INF/view/jsp/index.jsp]
Oct 09, 2019 2:44:42 PM org.springframework.web.servlet.FrameworkServlet logResult
FINE: Completed 200 OK

This is where the issue lies: while successful in finding the JSP file, the page is not rendered. I am returned an empty HTML file, and using the web debugger (chrome) only shows a source of 15 blanks lines. I presume its the length of my index.jsp file;

My question is twofold

  1. What might be the cause of the HTML/JSP not rendering on my screen?
  2. If there is a fix, for all my other legacy JSP files I need to move to work under SpringBoot, will they all require a similar procedure?

Thanks!

Update For the sake of brevity, someone asked about the structure of my produced WAR, the structure is as such when I pull up the archive

WAR
|____css
|____dashboard_view
|____excel
|____images
|____jquery
|____js
|____jsp
|____META-INF
|____org
|____third-party
|____WEB-INF
|________classes
|________lib
|________view
|____________html
|____________jsp
|_________________index.jsp
|_________________login.jsp

Taking into account the login and index jsp files I provided, I wonder if something is failing silently somewhere during the rendering?

  • Check [this answer](https://stackoverflow.com/a/54451482/2970947); particularly part 3: "Edit dispatcher-servlet.xml to ensure that it's looking in the views directory for files ending in .jsp." (but change "views" to "view" to match your usage). – Elliott Frisch Oct 09 '19 at 06:35
  • thank you for the answer. However, is this not handled in application.properties via the [spring.mvc.view.prefix, spring.mvc.view.suffix] fields ? – Joseph Orme Oct 09 '19 at 06:41

2 Answers2

1

It appears to be a case of that the servlet was unable to find some of the files in question and therefore, wasn't rendering anything because it couldn't find anything

I'll need to verify further. But upon moving home.js under WEB-INF/view/js/home per index.jsp, I was starting to get some stack traces from my webpage

Exception: org.apache.jasper.JasperException: java.lang.NullPointerException

Stack Trace
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:516) org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385) org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329) javax.servlet.http.HttpServlet.service(HttpServlet.java:741) 
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) 
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 
org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:712) 
org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:580) 
org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:516) 
org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequestDispatcher.include(HeaderWriterFilter.java:149) 
org.apache.jasper.runtime.JspRuntimeLibrary.include(JspRuntimeLibrary.java:955) 
org.apache.jsp.WEB_002dINF.view.jsp.index_jsp._jspService(index_jsp.java:150)

And for my log in SpringBoot

SEVERE: Servlet.service() for servlet [dispatcherServlet] in context with path [/xxx] threw exception [java.lang.NullPointerException] with root cause
java.lang.NullPointerException
    at java.util.Properties$LineReader.readLine(Properties.java:434)
    at java.util.Properties.load0(Properties.java:353)
    at java.util.Properties.load(Properties.java:341)
    at com.xx.xxx.common.utils.common.GenericPropertiesUtil.loadProperties(GenericPropertiesUtil.java:16)
    at com.xx.xxx.common.utils.common.EnvPropertiesUtil.getEnvironmentProperties(EnvPropertiesUtil.java:43)
    at com.xx.xxx.common.utils.common.EnvPropertiesUtil.getParameterValue(EnvPropertiesUtil.java:23)
    at com.xx.xxx.common.utils.common.EnvPropertiesUtil.getFlagParameterValue(EnvPropertiesUtil.java:37)
    at com.xx.xxx.common.utils.Config.getFlagValue(Config.java:131)
    at com.xx.xxx.common.utils.Config.isProd(Config.java:59)
    at com.xx.xxx.common.utils.IO.debug(IO.java:514)

This seems to suggest that there's activity on the page now

I'll verify and reopen if I need to. I will keep this post in case anyone else has a similar issue

0

Since you are using an external tomcat for deployment by setting the spring-boot-starter-tomcat as provided scope. Try adding the below scope to the jasper:

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

The jasper dependency is not included in the embedded tomcat but since the deployment is to an external one , the tomcat will already have this. So you could mark it as provided. The jasper dependency is the one which transpile the jsp.Also ensure that a war packaging is used as JSP won't work properly with jar as packaging in spring boot.Read

Ananthapadmanabhan
  • 5,706
  • 6
  • 22
  • 39
  • thank you for the answer. Unfortunately I had tried this from previous answers on the site, and it doesn't seem to be working regardless of having the scope on either the jasper or the jstl JARs – Joseph Orme Oct 09 '19 at 06:42
  • @JosephOrme can you verify that the created war contains all the jsp files correctly ? Since you are deploying in an external tomcat set the jasper dependency as provided and verify this.Also , check the stacktrace during run time to verify that when the jsp files are transpiled there are no errors generated. – Ananthapadmanabhan Oct 09 '19 at 06:45
  • pulling the WAR and opening it up in an archive viewer, the WAR has tomcat-embed-jasper located at **/WEB-INF/lib-provided** and jstl is under **WEB-INF/lib** – Joseph Orme Oct 09 '19 at 06:57
  • @JosephOrme are the jsp's present in the war ? Could you also check the tomcat folder after deployment.Also are there any errors in stacktrace when the jsp is rendered ? – Ananthapadmanabhan Oct 09 '19 at 07:18
  • the .jsp files are under `WEB-INF/view/jsp` as expected. I am working on seeing if one of the other .js/jsp/html files the index relies on is not being found. There are no stack traces, I think its failing silently. apologies but I can't seem to tag you either – Joseph Orme Oct 09 '19 at 07:21
  • Also it's preferable to use jsp includes like : instead of the static include using <%@ – Ananthapadmanabhan Oct 09 '19 at 07:47
  • @JosephOrme Yup been there :) – Ananthapadmanabhan Oct 09 '19 at 09:24