how to read the already existing bean in jstl|form? - spring

I have simple form with SpringMVC, I want retrieve my already had bean by pbid. the problem is the server side I can get the already setting bean, but the jsp side it always get new bean. Can I use the #ModelAttribute("productbean") to received some parameters to get the bean store in my server-side? how to do it? The jstl|form seem always get new form
<form:form method="post" modelAttribute="productbean" action="">
<table>
</tr>
<tr>
<td width="118"><form:label for="name" path="name" > Production Name:</form:label></td>
<td colspan="2"><form:hidden path="pbid"/><form:input path="name" type="text" size="50" /></td>
</tr>
...
My controler is like:
#RequestMapping(value="/createproduct", method=RequestMethod.GET)
public String getProduct(HttpServletRequest req, Model model,
#RequestParam(value = "pbid", required = false, defaultValue = "") String spbid) throws MalformedURLException {
UUID pbid;
if(spbid.isEmpty())pbid=UUID.randomUUID();
else pbid=UUID.fromString(spbid);
ProductBean tmp;
if(!products.containsKey(pbid)){
tmp=createbean();
pbid=tmp.getPbid();
System.err.println("============new productbean===============\n");
}else{
tmp=products.get(pbid);
System.err.println(tmp.getMpf().size());
System.err.println(tmp.getMpf().printFileNameList());
}
.....
#ModelAttribute("productbean")
public ProductBean createbean(){
ProductBean productbean=new ProductBean(context.getRealPath(filepath));
products.put(productbean.assignID(), productbean);
return productbean;
}

Add session attribute annotation over class
#SessionAttributes("productbean")
#controller
public class test() {
}

Related

Mapping to List Object in Spring MVC when Page is not displayed by a Controller

I am trying to map the value from Front end to Back Bean which is a List. Now the problem is that the page/view is not displayed by a Spring controller it is a jsp which is being displayed by CMS(content management system). So I cant initialse List and add elements to it and pass as a mode attribute
This is how my bean looks
public class LodgingAvailabilityRequest implements Serializable
{
private List<GuestCount> guestCounts = new ArrayList<GuestCount>();
public List<GuestCount> getGuestCounts() {
return guestCounts;
}
public void setGuestCounts(List<GuestCount> guestCounts) {
this.guestCounts = guestCounts;
}
and the GuestCount has the count field
public class GuestCount implements Serializable
{
private Double count;
this is how my JSP looks like
<jsp:useBean id="lodgingAvailability" class="com.pegasus.gen.LodgingAvailabilityRequest" scope="request"/>
<jsp:useBean id="guestCount" class="com.pegasus.gen.GuestCount" scope="request"/>
<body>
<form:form method="POST" action="/lodgingbyroom" modelAttribute="lodgingAvailability">
<table>
<tr>
<td>Count: <form:input path=guestCounts[0].count /></td>
</tr>
<td><input type="submit" value="Submit lodgingAvailability request by ROOM type"/></td>
</tr>
</table>
</form:form>
Now using above code gives me Array Out of Bounds exception so I tried couple of things as mentioned in some other posts
Changed to this <td>Count: <form:input path="${guestCounts[0].count}" /></td> I dont get any compilation error but the vaue in controller is not getting mapped. The list is empty.
Added a for loop but this didnt work as expected since the list is empty and it wont enter the loop.
<c:forEach items="${lodgingAvailability.guestCounts}" varStatus="counter">
<tr>
<td>Count: <form:input path="guestCounts[${counter.index}].count" /></td>
</tr>
</c:forEach>
This is how my controller looks, where the form gets submitted
#RequestMapping(value = "/lodgingbyroom", method = RequestMethod.POST)
public String lodgingAvailabilityByRoom(ModelMap model, LodgingAvailabilityRequest request,
RedirectAttributes redirectAttributes) {
final RestTemplate restTemplate = new RestTemplate();
final Map<String, List<DXARoom>> groupByRoom = restTemplate
.postForEntity(pegasusPath + "/lodgingbyroom", request, Map.class).getBody();
redirectAttributes.addFlashAttribute("pegasusResponse", groupByRoom);
}
I was able to Fix the above problem. Since the list was not having any elements in it the only way to make it work is add a dummy element to list. So you can traverse the list and then map the values
<% lodgingAvailability.getGuestCounts().add(guestCount) ;%>
<% lodgingAvailability.getGuestCounts().add(guestCount) ;%>
<c:forEach items="${lodgingAvailability.guestCounts}" varStatus="counter">
<tr>
<td>Count: <form:input path="guestCounts[${counter.index}].count" /></td>
</tr>
</c:forEach>
for this piece of code to work make sure that you declare the beans
<jsp:useBean id="lodgingAvailability" class="com.pegasus.gen.LodgingAvailabilityRequest" scope="request"/>
<jsp:useBean id="guestCount" class="com.pegasus.gen.GuestCount" scope="request"/>

How to proceed with complex object in POST request

Hello SO I had 2 entities
Main entity
#Entity
#Table(name = "Events")
public class Event {
//Some fields ...
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "event_eventtypes" ,
joinColumns = #JoinColumn(name = "event_id"),
inverseJoinColumns = #JoinColumn(name = "event_type_id"))
private Set<EventType> eventTypes;
//Getters and setters...
Now I have a form that is created using spring form-taglib
<form:form modelAttribute="event" method="post" action="/event/registerEvent" commandName="event">
<form:label path="name">Event display name</form:label>
<form:input path="name" type="text" cssClass="form-control" placeholder="Display name"/>
<form:label path="description" >Description</form:label>
<form:textarea path="description" cssClass="form-control"/>
<form:label path="priv" cssClass="">Make private? <span style="font-family:'Open Sans', sans-serif; font-size:11px; color: dodgerblue;">(It will be seen by your friends and people who you send invitation.)</span></form:label>
<form:checkbox path="priv" cssClass="form-check-input"/>
<form:label path="age">Age limit</form:label>
<form:select path="age">
<form:options items="${age}"/>
</form:select>
<form:hidden path="lng" id="formLang" />
<form:hidden path="lat" id="formLat"/>
<%--Question appear here--%>
<form:select path="eventTypes" items="${eventTypes}" multiple="true"/>
<input type="submit" value="Submit">
Controller
#Controller
public class EventController {
private static final Logger logger = LoggerFactory.getLogger(EventController.class);
#Autowired
private EventService eventService;
#Autowired
private UserService userService;
#Autowired
private EventTypeService eventTypeService;
#RequestMapping(path = "/event/create", method = RequestMethod.GET)
public String init(ModelMap modelMap) {
List<String> tags = eventTypeService.listEventTypes().stream().map(EventType::getName).collect(Collectors.toList());
ArrayList<Integer> ageArr = new ArrayList();
ageArr.add(0);
ageArr.add(6);
ageArr.add(12);
ageArr.add(16);
ageArr.add(18);
modelMap.addAttribute("event", new Event());
modelMap.addAttribute("age", ageArr);
modelMap.addAttribute("eventTypes", tags);
return "/event/create";
}
#RequestMapping(path = "/event/registerEvent", method = RequestMethod.POST)
public String createEvent(#ModelAttribute("event") Event event, #ModelAttribute("eventTypes1") List<String> eventTypes){
event.setDate(new Date());
event.setUser(userService.getUserByUsername(
AuthenticationService.getLoggedInUser())
);
eventService.addEvent(event);
return "redirect:/";
}
When complete form with values, click submit, get error
400 The request sent by the client was syntactically incorrect.
It's because eventTypes property is of String type. How can I send that list as another parameter in controller or what should I do it?
Maybe you need a DTO to include event and eventType list both.
When I have done this I send a DTO with a list of IDs/Strings for event types to the form and then create a new event from the DTO.
For the select statements loop round each option returned in the DTO and use a service findById to retrieve each eventType and then add this to the event model.
for (String tag : eventDTO.getEventTags()) {
EventType eventTag = eventTypeService.findById(Long.valueOf(tag));
if (eventTag != null) {
event.getEventTypes().add(eventTag );
}
}
Found soulution.
Just implement jsp-based form as :
<form:form modelAttribute="event" method="post" action="/event/registerEvent" commandName="event">
...
<%--There I'm not using more jsp tabs.--%>
<select multiple name="et" id="tags">
<c:forEach items="${eventTypes}" var="e">
<option value=${e}>${e}</option>
</c:forEach>
</select>
<input type="submit" value="Submit">
</form:form>
In Controller I get parameters as request.getParameterValues("et")) - that return String[] from HttpServletRequest

How to handle repository exception in the controller - spring mvc

i'm using spring boot and spring MVC. I'm creating a simple form(CRUD)
here is the code:
#Document
public class User
{
#Id
private ObjectId id;
#Indexed(unique=true)
#NotNull
#Size(min=2, max=30)
private String username;
#NotNull
#Size(min=2, max=30)
private String password;
private List<String> roles;
...
Controller:
#Controller
#RequestMapping("/admin")
public class AdminController {
...
/**
* NEW USER (POST)
*/
#RequestMapping(value = "new", method = RequestMethod.POST)
public ModelAndView newUser(#Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return new ModelAndView("/admin/new");
}
user.setRoles(Arrays.asList(Constants.ROLE_ADMIN));
ur.save(user);
return new ModelAndView("redirect:/admin");
}
/**
* NEW USER (VIEW)
*/
#RequestMapping(value = "new", method = RequestMethod.GET)
public ModelAndView newUser(User user) {
ModelAndView mv = new ModelAndView("admin/new");
return mv;
}
...
}
and the View:
<form name="new" th:action="#{/admin/new}" th:object="${user}" method="post">
<table>
<tr th:if="${error != null}">
<td colspan="4">
<span th:text="${error}"></span>
</td>
</tr>
<tr>
<td>Name:</td>
<td><input type="text" name="username" autocomplete="off"/></td>
<td width="10"/>
<td th:if="${#fields.hasErrors('username')}" th:errors="*{username}"></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" name="password" autocomplete="off"/></td>
<td width="10"/>
<td th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></td>
</tr>
<tr>
<td><button type="submit">Create</button></td>
</tr>
</table>
</form>
and it works, if I put an username bigger than 30 character.
But if I got and exception from the repository, for example:
DuplicateKey from the mongodb repository, didn't work.
So i tried to put this code in the controller:
#ExceptionHandler(Exception.class)
public ModelAndView handleCustomException(Exception ex) {
ModelAndView model = new ModelAndView("admin/new");
model.addObject("error", ex.getMessage());
return model;
}
It handle all the exceptions, but in this moment I don't have the "User" or "BindingResult" and when it try to render gets this error:
2015-09-22 13:36:55.498 ERROR 6208 --- [nio-8080-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "#fields.hasErrors('username')" (admin/new:21)] with root cause
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'user' available as request attribute
What I'm doing wrong?
How should I handle this kind of exception?
There is a way to send the USER to ExceptionHandler?
Thanks.
Because you don't have to return admin/new from the error handler. The view is expecting the User object that you are correctly populating in normal controllers. In case of an error this object simply isn't there. So, return another view from the error controller: ModelAndView model = new ModelAndView("admin/error"); for example.
Anyhow, this is not a solution. You need to catch exceptions. In this situation it is always better to have an intermediate layer called Service. In service you can catch any repository exceptions and decide what to do about them.

Replace only selected fields using binding

I'm building simple twitter clone in Spring MVC. I want to provide edit functionality to posted messages.
Message domain object looks like this (simplified)
public class Message {
long id;
String text;
Date date;
User user;
}
I created jps form
<form:form action="edit" method="post" modelAttribute="message">
<table>
<tr>
<td><label for="text">Message: </label></td>
<td><form:textarea path="text" id="text"/></td>
</tr>
<tr>
<td><input type="submit" name="commit" value="Save" /></td>
</tr>
</table>
</form:form>
and added those method in controller class
#RequestMapping(value = "/edit", method = RequestMethod.GET)
public String showEditMessage(#RequestParam long id, Model model) {
Message message = messageService.findMessage(id);
if (message == null) {
return "404";
}
model.addAttribute("message", message);
return "users/editMessage";
}
#RequestMapping(value = "/edit", method = RequestMethod.POST)
public String editMessage(#Valid #ModelAttribute Message message, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "/users/editMessage";
}
messageService.updateMessage(message);
return "/users/editMessage";
}
The problem is that the Message received in editMessage() contains only text field. I assume that this is expected behaviour. Can it be configured to replace fields that are only in jsp form?
I know this is only one field and I could just use #RequestParam String message, but sooner or later I will face similar problem with more than just one field.
I also have side question.
Are attributes added in showEditMessage() are passed to editMessage() method? I tried to add "id" attribute in first method, but I couldn't retrive it using "#RequestParam long id" in second.
#SessionAttributes("message")
On top of controller class solved it.

Spring, modelmap, getting attributes from the jsp

I develop an MVC application with spring framework and some other frameworks (and I'm a beginner). I have a controller to manage jsp handling, for example when I want add new person to my 'person list' I call a instantiate a person object and I pass it to the jsp view corresponding to the add method. And I do that by this a method like this:
#RequestMapping(value = "/persons/add", method = RequestMethod.GET)
public String getAdd(Model model) {
logger.debug("Received request to show add page");
// Create new UserDomain and add to model
// This is the formBackingOBject
model.addAttribute("personAttribute", new UserDomain());
// This will resolve to /WEB-INF/jsp/addpage.jsp
return "addpage-tiles";
}
My problem is that now, I want to pass to add to the model two different Objects, for example, I want to pass the 'new UserDomain()' and also an other object which is from an other table in my database, for example a 'new UserSecurity()'.
I think I should use a 'modelMap' instead of the 'model.addAttribute...', but I can't do this, so if someone could help me.
I get my model from the jsp by a code like :
<form:form modelAttribute="personAttribute" method="POST" action="${saveUrl}">
<table>
<tr>
<td><form:label path="firstName">First Name:</form:label></td>
<td><form:input path="firstName"/></td>
</tr>
<tr>
<td><form:label path="lastName">Last Name</form:label></td>
<td><form:input path="lastName"/></td>
</tr>
<tr>
<td><form:label path="userName">User name</form:label></td>
<td><form:input path="userName"/></td>
</tr>
<tr>
<td><form:label path="email">E-mail</form:label></td>
<td><form:input path="email"/></td>
</tr>
</table>
<input type="submit" value="Save" />
thank you a lot for helping me.
Simply passing more than one object to the view is not a problem -- just use model.addAttribute multiple times, and then you can access both objects.
However, if you want to edit more than one model in <form:form> you'll need to create a class that contains both of the objects:
public class UserDomainSecurity {
private UserDomain userDomain;
private UserSecurity userSecurity;
// getters and setters for both
}
Then pass an instance of this into the view:
model.addAttribute("userDomainSecurity", new UserDomainSecurity());
And use it in the form:
<form:form commandName="userDomainSecurity" method="POST" action="${saveUrl}">
...
<form:input path="userDomain.firstName"/>
....
<form:input path="userSecurity.someSecurityProperty"/>
It's sometimes annoying to have to create all these additional classes, but it is somewhat logical. The wrapper class creates kind of namespaces in the form and thus separates the individual objects that you want to edit.
I wanted to add on this as it's really the present problem I have encountered minutes ago.
In this case, I assumed that you're UserSecurity and UserDomain are relative to each other.
Let say you have,
public class UserDomain {
public UserSecurity userSecurity
public String firstName;
public String lastName;
// getters and setters...
}
and you have your UserSecurity something like,
public class UserSecurity {
public String someSecurityProperty;
// getters and setters...
}
Since userSecurity property can be accessed publicly, then you can just do what you have did in your Controller,
#RequestMapping(value = "/persons/add", method = RequestMethod.GET)
public String getAdd(Model model) {
logger.debug("Received request to show add page");
// Create new UserDomain and add to model
// This is the formBackingOBject
model.addAttribute("userDomainSecurity", new UserDomain());
// This will resolve to /WEB-INF/jsp/addpage.jsp
return "addpage-tiles";
}
then just access it in your addpage.jsp like it's an object property like below,
<form:form commandName="userDomainSecurity" method="POST" action="${saveUrl}">
...
<form:input path="firstName />
<form:input path="lastname />
....
<form:input path="userSecurity.someSecurityProperty"/>
as you notice, I access the someSecurityProperty by the property declared in UserDomain class.

Resources