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'?
Related
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
Hello guys am currently working with An api from swagger but am getting a whitelable error page
This is my Customers APIservice
package services;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import domains.Customers;
#Service
public class CustomersApiService {
// define a RestTemplate object
private final RestTemplate restTemplate;
// web service resources endpoints
private final String GET_ALL_USER =
"https://api.predic8.de/shop/customers/";
// define an argument constructor, then pass in the RestTemplate object
and
Autowire it
#Autowired
public CustomersApiService (RestTemplate restTemplate){
this.restTemplate = restTemplate;
}
// Get All Users
// #returns a list
#SuppressWarnings("unchecked")
public List<Customers> findAllCustomers(){
return Arrays.stream(restTemplate.getForObject
(GET_ALL_USER,Customers[].class)).collect(Collectors.toList());
}
}
This is my Customers Controller
package controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import services.CustomersApiService;
#Controller
public class CustomersController {
private final CustomersApiService service;
#Autowired
public CustomersController(CustomersApiService service) {
this.service = service;
}
#GetMapping("/customers")
public String getAll(Model model){
model.addAttribute("customers", service.findAllCustomers());
return "customers";
}
}
This is my Customers POJO
package domains;
import java.util.HashMap;
import java.util.Map;
public class Customers {
private String firstname;
private String lastname;
private String customerUrl;
private Map<String, Object> additionalProperties = new HashMap<String,
Object>();
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getCustomerUrl() {
return customerUrl;
}
public void setCustomerUrl(String customerUrl) {
this.customerUrl = customerUrl;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
This is the customers file
<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org/">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Users</title>
<!-- Bootstrap core CSS -->
<link href="..static/css/bootstrap.min.css"
th:href="#{/css/bootstrap.min.css}" rel="stylesheet">
<link href="..static/css/bootstrap-theme.min"
th:href="#{/css/bootstrap.min.css}" rel="stylesheet">
<link href="..static/css/carousel.css" th:href="#{/css/carousel.css}"
rel="stylesheet">
</head>
<body>
<div class="container">
<div class="jumbotron">
<h1>Java SringREST API </h1>
</div>
</div>
<div class="container">
<div th:if = "${not #lists, isEmpty (customers)}">
<h1>Users List</h1>
<table class="table table-hover">
<tr>
<th>FirstName</th>
<th>LastName</th>
<th>CustomerUrl</th>
</tr>
<tr th:each="Customer : $(customers)">
<td th:text = "${customers.firstname}"></td>
<td th:text = "${customers.lastname}"></td>
<td th:text = "${customers.customerUrl}"></td>
</tr>
</table>
</div>
</div>
<!-- FOOTER -->
<footer class="container">
<p class="float-right">Back to top</p>
<p>© 2017-2018 Company, Inc. · Privacy
· Terms</p>
</footer>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="..static/js/jquery.min.js" th:src="#{/js/jquery.min.js}">
</script>
<script src="..static/js/bootstrap.min.js" th:src="#{/js/bootstrap.min.js}">
After running this spring application am getting a white label error on
this url http://localhost:8080/customers,
this is the error on the browser
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing
this as a fallback.
Wed Sep 19 10:39:31 PDT 2018
There was an unexpected error (type=Not Found, status=404).
No message available
pls i dont know why i am having this whitelable error on the page, Am expecting it to return the customers details using the API. Anyhelp?
First All you need to create a Customer POJO and in my case, I have used Recipe as a POJO to play around restTemplate
#Configuration
public class RestTemplateConfig {
//Creating a restTemplate Bean
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder)
{
return builder.build();
}
}
Then create an Implementation class:
#Service
public class ImplementaionClass implements Service {
private final RestTemplate restTemplate;
public RecipeServiceImpl(RestTemplate restTemplate) {
// TODO Auto-generated constructor stub
this.restTemplate = restTemplate;
}
#Override
public List<Result> getResultsByRecipe(String search,String query,int pageno) {
// TODO Auto-generated method stub
final List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Arrays.asList(MediaType.ALL));
messageConverters.add(converter);
//
this.restTemplate.setMessageConverters(messageConverters);
//
// final Recipe recipe = this.restTemplate.getForObject("http://url.../api/?i=" + search,
// Recipe.class);
//
ResponseEntity<Recipe> rateResponse = null;
// ResponseEntity<Recipe> rateResponse = null;
// if(pageno==0)
// rateResponse = restTemplate.exchange("http://url.../api/?i="+search,
//
// HttpMethod.GET, null, new ParameterizedTypeReference<Recipe>() {
// });
if(query.isEmpty())
if(pageno==0)enter code here
rateResponse = restTemplate.exchange("http://url..../api/?i=" + search,
HttpMethod.GET, null, new ParameterizedTypeReference<Recipe>() {
});
else
rateResponse = restTemplate.exchange("http://url..../api/?i=" + search+"&q=&p="+pageno,
HttpMethod.GET, null, new ParameterizedTypeReference<Recipe>() {
});
else
if(pageno==0)
rateResponse = restTemplate.exchange("http://url.../api/?i=" + search+"&q="+query,
HttpMethod.GET, null, new ParameterizedTypeReference<Recipe>() {
});
else
rateResponse = restTemplate.exchange("http://url...../api/?i=" + search+"&q="+query+"&p="+pageno,
HttpMethod.GET, null, new ParameterizedTypeReference<Recipe>() {
});
final Recipe rates = rateResponse.getBody();
System.out.println(rateResponse.getHeaders());
return rates.getResults();
}
This is just a demo class how you can use restTemplate and I hope it will work for you.
This is my controller
#Controller
#RequestMapping("VIEW")
public class SearchController {
private static final Log LOGGER = LogFactoryUtil.getLog(SearchController.class);
#RenderMapping
public String render() {
return "view";
}
#ActionMapping(params = "action = getResults")
public void getResults(#ModelAttribute("search") Search search, ActionRequest actionRequest, ActionResponse actionResponse) {
String keyword = search.getKeyword();
LOGGER.info("Keyword: " + keyword);
}
}
and my bean,
public class Search {
private String keyword;
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
}
and my view.jsp
<%#page import="org.springframework.web.bind.annotation.RequestMethod"%>
<%# taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<portlet:defineObjects />
<portlet:actionURL var = "getResultsURL">
<portlet:param name="action" value="getResults"/>
</portlet:actionURL>
<form:form action="${getResultsURL}" commandName="search" method="POST">
<form:input path="keyword"/>
<input type="submit" value="Search">
</form:form>
and I am getting the following exception
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'search' available as request attribute
It is working fine if I place #ModelAttribute("search") as a parameter in render method, but I know it was absolutely wrong(correct me)
Any suggestions?
You get this exception when the JSP page is rendered, right?
Spring MVC tells you that it cannot find "search" attribute in the current request. And indeed, your controller doesn't put any instance of Search class to the Spring MVC model.
Two options:
Create getter for Search class instance with #ModelAttribute annotation:
#ModelAttribute
public Search getSearch() {
return new Search();
}
Put Search class instance to the Spring model in the render method:
#RenderMapping
public String render(Model model) {
model.addAttribute("search", new Search());
return "view";
}
This way the form tag will find the model under the given command name.
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>.
I'm using a multiple select HTML form input to allow a user to pick a collection of Extensions from a list of all possible Extensions.
The Extension class is quite simple -
public class Extension {
private String number;
private String firstName;
private String lastName;
... getters and setters ...
#Override
public String toString() {
return new StringBuilder(number).append(" - ")
.append(firstName).append(" ").append(lastName)
.toString();
}
}
Here's my form object -
public class BusinessUnitForm {
private String name;
private Collection<Extension> extensions;
public Collection<Extension> getExtensions() {
return extensions;
}
public void setExtensions(Collection<Extension> extensions) {
this.extensions = extensions;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And the controller -
#Controller
#RequestMapping("/businessunit")
public class BusinessUnitController {
... extension service & getters/setters ...
#RequestMapping(method = RequestMethod.GET)
public ModelAndView showForm(HttpServletRequest request, HttpServletResponse response) throws Exception {
Integer customerId = (Integer) request.getSession().getAttribute("customerId");
ModelAndView mav = new ModelAndView("bu");
// this is quite expensive...
Collection<Extension> allExtensions = extensionService.getAllExtensions(customerId);
BusinessUnitForm businessUnitForm = new BusinessUnitForm();
mav.addObject("allExtensions", allExtensions);
mav.addObject("businessUnitForm", businessUnitForm);
return mav;
}
#RequestMapping(value="/create", method = RequestMethod.POST)
public ModelAndView create(HttpServletRequest request, HttpServletResponse response, BusinessUnitForm businessUnitForm, BindingResult result) throws Exception {
// *** BREAKPOINT HERE *** to examine businessUnitForm
Integer tenantId = (Integer) request.getSession().getAttribute("tenantId");
// code to process submission
ModelAndView mav = new ModelAndView("bu");
return mav;
}
}
And finally the view -
<%# page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%# taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<%# taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<html>
...
<form:form action="businessunit/create" method="POST" commandName="businessUnitForm" >
<form:label path="name"></form:label><form:input path="name" />
<form:select path="extensions" cssClass="multiselect">
<form:options items="${allExtensions}" itemValue="number" />
</form:select>
<input type="submit" value="Create" />
</form:form>
...
</html>
At the breakpoint shown above in the create controller method, businessUnitForm.extensions is null. businessUnitForm.name is bound correctly.
I've tried, perhaps misguidedly, making businessUnitForm.extensions a LazyList but this doesn't help.
If I change BusinessUnitForm.extensions to be a Collection of unspecified type, it is populated successfully with a LinkedHashSet of Strings, containing the values selected.
Maybe I'm expecting too much of Spring, but I was hoping it would be able to use the values from the select, and also the reference data in allExtensions to automagically create a Collection<Extension> for me on the businessUnitForm. I understand the role of CustomCollectionEditors, but was under the impression that this might not be required in Spring 3.
Can Spring 3 populate my Collection<Extension> on the BusinessUnitForm without me writing a custom collection editor ? Some kind of trick with the view, perhaps ?
Thanks in advance ...
Dan
You need to implement a custom converter that is able to convert the String from the request into an Extension object.
And then you must use Collection (or List, or Set) with generic type information.
See this answer for an example of an converter: Submit Spring Form using Ajax where Entity has Foreign Entities