Cache expiration and versioning of resource file work correctly on all pages. But flows seem to ignore Spring MVC configuration.
A working example:
resource files have versioning
With Spring Web Flow:
resource files are missing versioning
In WebMvcConfig class:
#Configuration
#EnableCaching
#ConfigurationProperties("message")
public class WebMvcConfig implements WebMvcConfigurer, ServletContextAware {
...
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
.resourceChain(false)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
.addTransformer(new CssLinkResourceTransformer());
}
#Bean
public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
return new ResourceUrlEncodingFilter();
}
...
}
WebConfigClass:
#Configuration
#EnableWebMvc
public class WebFlowConfig extends AbstractFlowConfiguration {
#Autowired
private ViewResolver viewResolver;
#Autowired
private RequestDataInterceptor requestDataInterceptor;
#Autowired
private LocalValidatorFactoryBean validator;
// WEB FLOW
#Bean
public FlowExecutor flowExecutor() {
return getFlowExecutorBuilder(flowRegistry()).addFlowExecutionListener(new SecurityFlowExecutionListener(), "*").build();
}
#Bean
public FlowDefinitionRegistry flowRegistry() {
return getFlowDefinitionRegistryBuilder(flowBuilderServices()).setBasePath("/WEB-INF/flows/").addFlowLocationPattern("/**/*-flow.xml").build();
}
#Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder().setViewFactoryCreator(mvcViewFactoryCreator()).setValidator(validator).setDevelopmentMode(true).setConversionService(getDefaultConversionService()).build();
}
private DefaultConversionService getDefaultConversionService() {
final DefaultConversionService service = new DefaultConversionService();
final FormattingConversionService delegateConversionService = (FormattingConversionService) service.getDelegateConversionService();
delegateConversionService.removeConvertible(String.class, Number.class);
delegateConversionService.addConverterFactory(new StringToNumberConverterFactory());
delegateConversionService.addConverter(new TrimStringConverter());
return service;
}
// MVC
#Bean
public FlowHandlerMapping flowHandlerMapping() {
final FlowHandlerMapping mapping = new FlowHandlerMapping();
mapping.setOrder(0);
mapping.setFlowRegistry(this.flowRegistry());
mapping.setInterceptors(requestDataInterceptor);
return mapping;
}
#Bean
public FlowHandlerAdapter flowHandlerAdapter() {
final FlowHandlerAdapter adapter = new FlowHandlerAdapter();
adapter.setFlowExecutor(this.flowExecutor());
adapter.setSaveOutputToFlashScopeOnRedirect(true);
return adapter;
}
#Bean
public MvcViewFactoryCreator mvcViewFactoryCreator() {
final MvcViewFactoryCreator factoryCreator = new MvcViewFactoryCreator();
factoryCreator.setViewResolvers(Lists.newArrayList(this.viewResolver));
factoryCreator.setUseSpringBeanBinding(true);
return factoryCreator;
}
}
In security config:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
#Override
public void configure(final WebSecurity web) {
web.ignoring().antMatchers("/resources/**");
}
...
}
In jsp files (including flow):
<%# taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%# taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
...
<!-- jQuery -->
<script type="text/javascript" src="<c:url value="/resources/scripts/libs/jquery-3.3.1.js"/>"></script>
<script type="text/javascript" src="<c:url value="/resources/scripts/jquery-validation/jquery.validate.js"/>"></script>
<script type="text/javascript" src="<c:url value="/resources/scripts/jquery-numbers/jquery.number.min.js"/>"></script>
...
Any ideas on how to apply versioning of static assets on flows?
my way to deal with it:
<spring:eval expression="#applicationProperties.resourcesVersion" var="resourcesVersion"/>
<head>
<style type="text/css" media='screen,print'>
#import url("<c:url value="/resources/css-framework/css/tools.css?v=${resourcesVersion}" />");
</style>
<script type="text/javascript" src="<c:url value="/resources/spring/Spring.js?v=${resourcesVersion}" />"></script>
</head>
ApplicationProperties:
#Named
public class ApplicationProperties {
....
private Long resourcesVersion = System.currentTimeMillis();
public Long getResourcesVersion() {
return resourcesVersion;
}
}
I encountered the same issue in the CAS server and the solution was to add the ResourceUrlProviderExposingInterceptor as an interceptor of the FlowHandlerMapping.
See: https://github.com/apereo/cas/pull/5266/files#diff-5c92401d9bb5992a2273618310a30ff672ff45e00d691eb651e6187ceedb0869R197
Related
I have a controller
#Controller
public class FirstController {
#GetMapping("/hello")
public String helloPage(#RequestParam("name") String name,
Model model){
model.addAttribute("message", name);
return "first/hello";
}
}
And view hello.html
<!doctype html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>Hello world!</h1>
<p th:text="${message}"></p>
</body>
</html>
I enter the required parameters: http://localhost:8080/hello?name=Tom
But all that the page displays is "Hello world!"
P.S. I'm working with spring core and therefore I had to write the configs by hand, here's what is in the configs:
#Configuration
#ComponentScan("com.nosferat.springapp")
#EnableWebMvc
public class SpringConfig implements WebMvcConfigurer {
#Bean
public ViewResolver viewResolver() {
var viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".html");
return viewResolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
Maybe it has something to do with the config
Why do you have a return "first/hello"? you should use return "hello", by the way this may be the answer to your question
i also highly suggest you to use Spring boot so your life will be easier
When I go to http://localhost:8080/ for my spring boot form it just gives me a whitelabel error page. This is my Controller code
package net.codejava;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class MvcController {
#RequestMapping("/")
public String home() {
System.out.println("Going home...");
return "index";
}
}
and here's my index.jsp code
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Volunteer Management Form</title>
</head>
<body>
<h1>Volunteer Management Form</h1>
</body>
</html>
I cannot work out why it won't show I do however get "Going home..." printed in the console
i think that you have problem with the view resolver add this to your application.properties:
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
and move your index.jsp to /WEB-INF/jsp/
the second method is to set resolver by using WebConfig class like that :
add this class to your source package :
#EnableWebMvc
#Configuration
#ComponentScan("net.codejava")
public class WebConfig implements WebMvcConfigurer {
#Bean
public ViewResolver internalResourceViewResolver() {
InternalResourceViewResolver bean = new
InternalResourceViewResolver();
bean.setViewClass(JstlView.class);
bean.setPrefix("/WEB-INF/view/");
bean.setSuffix(".jsp");
return bean;
}
}
update 16/03/2022
there is some problems with the the newer versions of Spring boot so we should add this been also like the following :
#Configuration
#EnableWebMvc
#ComponentScan
public class MvcConfig extends WebMvcConfigurerAdapter {
#Bean
WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
enableDefaultServlet() {
return (factory) -> factory.setRegisterDefaultServlet(true);
}
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new
InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/view/");
resolver.setSuffix(".html");
return resolver;
}
#Override
public void
configureDefaultServletHandling(DefaultServletHandlerConfigurer
configurer) {
configurer.enable();
}
}
Not sure how did you setup JSP on spring boot because there's some specific dependencies that you need to have. Also, people nowadays use Thymeleaf or Freemarker for templating instead of JSP on spring boot. I was able to follow and run the github project from https://www.baeldung.com/spring-boot-jsp with these urls
http://localhost:8080/spring-boot-jsp/book/viewBooks
http://localhost:8080/spring-boot-jsp/book/addBook
I am trying to create a sample Spring MVC project but static file are not getting loaded.I am getting below mentioned error.
http://localhost:8080/BPMEI/static/css/bootstrap.css Failed to load resource: the server responded with a status of 404 (Not Found)
I will be grateful if someone can help me out to fix this issue.
Project Structure
Configuration Code
package com.dgsl.bpm.configuration;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.dgsl.bpm")
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/");
}
}
Initializer code
package com.dgsl.bpm.configuration;
public class WebInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(WebConfiguration.class);
ctx.setServletContext(container);
ServletRegistration.Dynamic servlet = container.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
JSP Code
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Student Enrollment Form</title>
<link href="<c:url value='/static/css/bootstrap.css' />" rel="stylesheet"></link>
</head>
<body>
To F2:if you let it download itself instead of me downloading css files and telling Spring where they are,you must let the js files or somethings publish to the web which is not controlled by yourself.The building-owner’s purpose is how to load the static file in a Spring MVC project.I think the problem is on the code of addResourceLocations,it should be addResourceLocations ("classpath:/static/");
may be you made a wrong mapping.
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
this code should works, i tested it on my local machine.
addResourceLocations("/static/") the last slash is mandatory
in sping-core-4.2.5-release.jar StringUtils.java has the following code
public static String applyRelativePath(String path, String relativePath) {
int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
if (separatorIndex != -1) {
String newPath = path.substring(0, separatorIndex);
if (!relativePath.startsWith(FOLDER_SEPARATOR)) {
newPath += FOLDER_SEPARATOR;
}
return newPath + relativePath;
}
else {
return relativePath;
}
}
if not end with a slash, this method will return relativePath
FYI, when i view your attached picture, i found "bootstrap.min.css" in your project. don't forget to use right file name. i wish my suggestion can help you
change addResourceHandlers like
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("/");
}
and also change css file name 'bootstrap.min.css'
Change your addResourceHandlers definition like below and try:-
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
Please use the context path in your url:
<c:url value='${pageContext.request.contextPath}/static/css/bootstrap.css' />
This will give you the url value:
BPMEI/static/css/bootstrap.css
Let me know if that works.
I'm getting started using Spring.
Following this tutorial when I reach WebDomainIntegrationTest the test fails because of an empty content response.
I'm thinking maybe that the problem could be the serverport. The application runs on port 8000 and the integration test ask for localhost port 80. (if this is the problem, how can I set a different port?)
CODE
SiteController:
#Controller
#RequestMapping("/")
public class SiteController {
private static final Logger LOG = LoggerFactory.getLogger(SiteController.class);
#Autowired
private MenuService menuService;
#Autowired
private Basket basket;
#RequestMapping(method = RequestMethod.GET)
public String getCurrentMenu(Model model) {
LOG.debug("Yummy MenuItemDetails to home view");
model.addAttribute("menuItems",getMenuItems(menuService.requestAllMenuItems(new RequestAllMenuItemsEvent())));
System.out.println("getCurrentMenu");
return "home";
}
private List<MenuItem> getMenuItems(AllMenuItemsEvent requestAllMenuItems) {
List<MenuItem> menuDetails = new ArrayList<MenuItem>();
for (MenuItemDetails menuItemDetails : requestAllMenuItems.getMenuItemDetails()) {
menuDetails.add(MenuItem.fromMenuDetails(menuItemDetails));
}
return menuDetails;
}
/*
Lastly, you need to put the Basket into the model for the view to be able to read from.
This method takes the auto injected Basket and annotates it so that it is automatically merged into the Model.
*/
#ModelAttribute("basket")
private Basket getBasket() {
return basket;
}
}
WebDomainIntegrationTest:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = { PersistenceConfig.class, CoreConfig.class, WebConfig.class })
public class WebDomainIntegrationTest {
private static final String STANDARD = "Yummy Noodles";
private static final String CHEF_SPECIAL = "Special Yummy Noodles";
private static final String LOW_CAL = "Low cal Yummy Noodles";
private MockMvc mockMvc;
#Autowired
WebApplicationContext webApplicationContext;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void thatTextReturned() throws Exception {
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andDo(print())
.andExpect(content().string(containsString(STANDARD)))
.andExpect(content().string(containsString(CHEF_SPECIAL)))
.andExpect(content().string(containsString(LOW_CAL)));
}
}
Home view:
<%# page contentType="text/html; charset=UTF-8" %>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%# taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<tiles:insertDefinition name="defaultTemplate">
<tiles:putAttribute name="body">
<div class="body">
<h1>Home page !</h1>
<div class="col-md-12 text-center">
<c:forEach var="item" items="${menuItems}">
<div class="name"><c:out value="${item.name}"/></div>
</c:forEach>
</div>
</div>
</tiles:putAttribute>
</tiles:insertDefinition>
Test on model attributes (successful!)
#ContextConfiguration(classes = {PersistenceConfig.class, CoreConfig.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class CoreDomainIntegrationTest {
#Autowired
MenuService menuService;
#Autowired
OrderService orderService;
#Test
public void thatAllMenuItemsReturned() {
AllMenuItemsEvent allMenuItems = menuService.requestAllMenuItems(new RequestAllMenuItemsEvent());
assertEquals(3, allMenuItems.getMenuItemDetails().size());
}
#Test
public void addANewOrderToTheSystem() {
CreateOrderEvent ev = new CreateOrderEvent(new OrderDetails());
orderService.createOrder(ev);
AllOrdersEvent allOrders = orderService.requestAllOrders(new RequestAllOrdersEvent());
assertEquals(1, allOrders.getOrdersDetails().size());
}
}
The problem is, if you are using jsp related technique, you cannot use content(), you need to use model(), try this:
#Test
public void thatTextReturned() throws Exception {
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andDo(print())
.andExpect(model().attribute("test", hasItem(hasProperty("name", value)))));
}
The Matcher I'm using is from Hamcrest.
Spring Integration test is based on Spring Container, It doesn't care which port or host you are running your application.
For your problem , the issue is on this part of code:
#RequestMapping(method = RequestMethod.GET)
public String getCurrentMenu(Model model) {
LOG.debug("Yummy MenuItemDetails to home view");
model.addAttribute("menuItems",getMenuItems(menuService.requestAllMenuItems(new RequestAllMenuItemsEvent())));
System.out.println("getCurrentMenu");
return "home";
}
the model didn't attach to your response. please check home page ,does it generate the model attribute 'menuItems'?
I'm trying out Spring MVC with java config and I'm getting 404 not found for static resources, the folder of which I can confirm is definitively getting published to the server. It seems like the resources are not being resolved at all. So even when calling localhost:8080/SpringMVC/resources/js/jquery-1.4.2.min.js I get 404 not found. I'm using Spring 4.0.1
Deployed structure
SpringMVC/
resources/
css/
js/
jquery-1.4.2.min.js
WEB-INF/
classes/
lib/
views/
services.jsp
web.xml
My Code:
com/me/config/WebApplicationConfig.java
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.me.services"})
public class WebApplicationConfig extends WebMvcConfigurerAdapter {
private static final String VIEW_RESOLVER_SUFFIX = ".jsp";
private static final String VIEW_RESOLVER_PREFIX = "/WEB-INF/views/";
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Bean
public ViewResolver setupViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(VIEW_RESOLVER_PREFIX);
resolver.setSuffix(VIEW_RESOLVER_SUFFIX );
resolver.setViewClass(JstlView.class);
return resolver;
}
}
com/me/config/Initializer.java
public class Initializer implements WebApplicationInitializer {
private static final String DISPATCHER_SERVLET_MAPPING = "/";
private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(WebApplicationConfig.class);
rootContext.setServletContext(servletContext);
Dynamic servlet = servletContext.addServlet(DISPATCHER_SERVLET_NAME, new DispatcherServlet(rootContext));
servlet.addMapping(DISPATCHER_SERVLET_MAPPING );
servlet.setLoadOnStartup(1);
}
}
com/me/controller/ServicesController.java
#Controller
public class ServicesController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String services(ModelMap model) {
return "services";
}
}
WEB-INF/views/services.jsp
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<script src="<c:url value="/resources/js/jquery-1.4.2.min.js" />"></script>
</head>
<body>
<h2>Services</h2>
</body>
</html>
GET Request Response when calling localhost:8080/SpringMVC/:
<html>
<head>
<script src="/SpringMVC/resources/js/jquery-1.4.2.min.js"></script>
</head>
<body>
<h2>Services</h2>
</body>
</html>
And then after that 404 Not Found on:
localhost:8080/SpringMVC/resources/js/jquery-1.4.2.min.js
I think in your services.jsp, could you try with single quotes:
<script src="<c:url value='/resources/js/jquery-1.4.2.min.js' />"></script>
It seems like a problem with 2 sets of double quotes in <script>.