How to schedule a JSP Page? - java

I have a JSP page as task.jsp. So I want to display this page only once in a year i.e. in the month of March every year.
I dont have any idea how to schedule it. Please help me. Following is my sample controller code:
#RequestMapping(
value = "task.htm",
method = RequestMethod.GET
)
public ModelAndView taskPost(#ModelAttribute("task") Task task, HttpServletRequest request) {
ModelAndView model = new ModelAndView("task");
//do something
return model;
}

You can throw a ResponseStatusException:
#RequestMapping(value = "task.htm", method = RequestMethod.GET)
public ModelAndView taskPost(#ModelAttribute("task") Task task,HttpServletRequest request {
if(LocalDate.now().getMonth() != Month.MARCH) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Unable to find resource");
}
ModelAndView model = new ModelAndView("task");
//do something
return model;
}
See more:
ResponseStatusException (javadoc)
https://www.baeldung.com/spring-response-status-exception

Related

How can I prevent duplicating data while refreshing the page in Spring

In my project I have two pages first one is for entering the data and the second one is for showing the data to the user. When I refresh the result page data is dublicated. I tried to fix this but I am not familiar with PRG pattern. I want to prevent the duplication if the user refreshes the result page.
#RequestMapping(value = { "/display-form", "mainPage.html" }, method = RequestMethod.GET)
public ModelAndView displayForm() {
ModelAndView mv = new ModelAndView("mainPage");
mv.addObject("formData", new SampleModel());
return mv;
}
#RequestMapping(value = "/send-form-data", method = RequestMethod.POST)
public ModelAndView processForm(#Valid #ModelAttribute("formData") SampleModel formData, BindingResult res) {
ModelAndView mv = new ModelAndView();
fmv.validate(formData, res);
mv.addObject("formData", formData);
service.delete(1);
if(res.hasErrors())
{
mv.setViewName("mainPage");
}
else
{
mv.setViewName("result");
service.create(formData);
mv.addObject("list", service.findAll());
}
return mv;
}
Try to create the ModelAndView instance with redirection, so that when actions completes, you get a 3xx redirection (to the initial form, for example):
ModelAndView mv = new ModelAndView("redirect:/display-form");
// .. as before ..

Redirect ModelAndView with message

My application has a method to update a conference. After doing so I have a modelandview with a redirect to the main conference list. This all works fine although the message which I add as an object to the modelandview does not display.
My method in my controller:
#PostMapping("/updateConference")
public ModelAndView updateConference(
#ModelAttribute("conference") #Valid ConferenceDto conferenceDto, BindingResult result) {
if(result.hasErrors()){
return new ModelAndView("updateConference","conferenceDto", conferenceDto);
}
try {
conferenceService.updateConference(conferenceDto);
} catch (ConferenceAlreadyExistException uaeEx) {
ModelAndView mav = new ModelAndView("updateConference","conferenceDto", conferenceDto);
mav.addObject("message", uaeEx.getMessage());
return mav;
}
ModelAndView mav = new ModelAndView("redirect:/teacher/configure"); // Problem is here
mav.addObject("message", "Successfully modified conference.");
return mav;
}
In my html I have the line:
<div th:if="${message != null}" th:align="center" class="alert alert-info" th:utext="${message}">message</div>
After updating the conference it goes back to configure.html although the message does not show. In the url I can see http://localhost:8080/teacher/configure?message=Successfully+modified+conference
I have looked at this thread although it did not help.
I tried to experiment by setting ModelAndView mav = new ModelAndView("configure") and the message displays but my conference list is empty and the url is http://localhost:8080/teacher/updateconference
Any tips is highly appreciated!
EDIT
I have tried to use RedirectAttributes as crizzis pointed out & this page and have this now:
#PostMapping("/updateConference")
public String updateConference(
#ModelAttribute("conference") #Valid ConferenceDto conferenceDto, BindingResult result, RedirectAttributes attributes) {
if(result.hasErrors()){
attributes.addFlashAttribute("org.springframework.validation.BindingResult.conferenceDto", result);
attributes.addFlashAttribute("conferenceDto", conferenceDto);
return "redirect:/teacher/updateConference";
}
try {
conferenceService.updateConference(conferenceDto);
} catch (ConferenceAlreadyExistException uaeEx) {
attributes.addFlashAttribute("conferenceDto", conferenceDto);
attributes.addFlashAttribute("message", uaeEx.getMessage());
return "redirect:/teacher/updateConference";
}
attributes.addFlashAttribute("message", "Successfully modified conference.");
return "redirect:/teacher/configure";
}
My get method:
#GetMapping(path = "/updateConference/{id}")
public String showUpdateConferenceForm(#PathVariable(name = "id") Long id, Model model){
Optional<Conference> conference = conferenceService.findById(id);
if (!model.containsAttribute("ConferenceDto")) {
model.addAttribute("conference", new ConferenceDto());
}
return "updateConference";
}
This works as intended and my message is shown on my configure.html . However, when I have an error in BindingResults the application goes to an error page and I get this in the console:
Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported]
Use RedirectAttributes which has addFlashAttribute method. You can set the success or failure message like you did and access that message through the key in the redirected page as you need.
when the error occurs you are redirecting to the same method instead of this you can just render the template in case there is error. I do this way.

Calling RequestMapping "twice"

I couldn't figure out what to put to the title, but I have the following code:
#Controller
public class WorkdayAddController {
#Autowired
private WorkdayRepository workdayRepository;
#Autowired
private VehicleRepository vehicleRepository;
#RequestMapping(value = "addworkday")
public String addWorkday(Model model){
model.addAttribute("workdayaddform", new WorkdayAddForm());
model.addAttribute("vehicles", vehicleRepository.findAll());
return "addworkday";
}
#RequestMapping(value = "saveworkday", method = RequestMethod.POST)
public String save(#Valid #ModelAttribute("workdayaddform") WorkdayAddForm workdayaddform, BindingResult bindingResult) {
if (!bindingResult.hasErrors()) { // validation errors
Date workdayBegin = workdayaddform.getBeginDate();
Date workdayEnd = workdayaddform.getEndDate();
if (!UtilityClass.dateIsAfterDate(workdayBegin, workdayEnd)) {
bindingResult.rejectValue("beginDate", "err.beginDate", "Aloitusaika ei voi olla lopetusajan jälkeen.");
return "addworkday";
}
Workday workday = new Workday();
Vehicle vehicle = new Vehicle();
workdayRepository.save(workday);
}
else {
return "addworkday";
}
return "redirect:/workdaylist";
}
}
After the 'dateIsAfterDate' check, it should direct one to 'addworkday' again, which it does, but it doesn't add the 'vehicles' model. Is there a way around this? I thought it would somehow just direct it to the above #RequestMapping(value= "addworkday") but this seems to not be the case.
Update:
#RequestMapping(value = "addworkday")
public String addWorkday(Model model, RedirectAttributes redirectAttributes){
System.out.println(redirectAttributes); // {}
System.out.println(model); // output in comment
model.addAttribute("workdayaddform", new WorkdayAddForm()); //I guess I need to add the old workdayform here?
model.addAttribute("vehicles", vehicleRepository.findAll());
return "addworkday";
}
#RequestMapping(value = "saveworkday", method = RequestMethod.POST)
public String save(#Valid #ModelAttribute("workdayaddform") WorkdayAddForm workdayaddform,
BindingResult bindingResult,
final RedirectAttributes redirectAttributes) {
if (!bindingResult.hasErrors()) { // validation errors
Date workdayBegin = workdayaddform.getBeginDate();
Date workdayEnd = workdayaddform.getEndDate();
if (!UtilityClass.dateIsAfterDate(workdayBegin, workdayEnd)) {
// Add the vehicle you want to send to the other method.
redirectAttributes.addFlashAttribute("workdayaddform", workdayaddform);
redirectAttributes.addFlashAttribute("vehicle", vehicleRepository.findAll());
redirectAttributes.addFlashAttribute("binding", bindingResult);
return "redirect:/addworkday";
}
You need to use the #RedirectedAttributes annotation in order to send attributes to another method in a controller. Also, you will need to add "redirect:/" to your returned url.
#RequestMapping(value = "saveworkday", method = RequestMethod.POST)
public String save(#Valid #ModelAttribute("workdayaddform") WorkdayAddForm workdayaddform,
BindingResult bindingResult,
final RedirectAttributes redirectAttributes) {
if (!bindingResult.hasErrors()) { // validation errors
Date workdayBegin = workdayaddform.getBeginDate();
Date workdayEnd = workdayaddform.getEndDate();
if (!UtilityClass.dateIsAfterDate(workdayBegin, workdayEnd)) {
// Add the vehicle you want to send to the other method.
redirectAttributes.addFlashAttribute("vehicle", vehicle);
redirectAttributes.addFlashAttribute("binding", bindingResult);
return "redirect:/addworkday";
}
// More code.
else {
redirectAttributes.addFlashAttribute("vehicle", new Vehicle());
return "redirect:/addworkday";
}
}
I wasn't sure if you meant, after the in the else or inside the if, so I add them in both places, just to make sure.

is there any java/spring way to store session information for the current logged user using the HttpServletRequest?

I am wondering if I can set attributes on the HttpServletRequest object.
What I want to do is to store some information for the current logged user that I can later get back (on the same session).
I am using spring mvc.
So far I tried this
#RequestMapping(value = "/url1", method = RequestMethod.GET)
public void test1(final HttpServletRequest req, final ModelMap model) {
List<String> myList = (List<String>)req.getAttribute("myList");
}
#RequestMapping(value = "/url2", method = RequestMethod.GET)
public void test2(final HttpServletRequest req, final ModelMap model) {
String message = "hello world";
List<String> messages = new ArrayList<String>();
messages.add(messages);
req.setAttribute("myList", messages);
}
So far, when I make the req.getAttribute I get a null... Any idea?
To setAttribute in session should be used like this:
request.getSession().setAttribute("myList", messages);
And you can get it like this :
request.getSession().getAttribute("myList");

spring mvc #ExceptionHandler method get same view

My problem is that I want to create an #ExceptionHandler method that will capture all un-handled exceptions. Once captured I would like to redirect to the current page instead of specifying a separate page just to display error.
Basically how do I get the value of someview returned by somemethod and set it dynamically in the method unhandledExceptionHandler below.
#ExceptionHandler(Exception.class)
protected ModelAndView unhandledExceptionHandler(Exception ex){
System.out.println("unhandle exception here!!!");
ModelAndView mv = new ModelAndView();
mv.setViewName("currentview");
mv.addObject("UNHANDLED_ERROR", "UNHANDLED ERROR. PLEASE CONTACT SUPPORT. "+ex.getMessage());
return mv;
}
#RequestMapping(value = "/somepage", method = RequestMethod.GET)
public String somemethod(HttpSession session) throws Exception {
String abc = null;
abc.length();
return "someview";
}
So in JSP I can render this error message back into the current page something like that.
<c:if test="${not empty UNHANDLED_ERROR}">
<div class="messageError"> ${UNHANDLED_ERROR}</div>
</c:if>
I don't think there is way to do what you are asking for, because in the exception handler method unhandledExceptionHandler there is no way to find out what the name of the view that the handler method somemethod would have returned.
The only way is for you to introduce some sort of meta data scheme so that when you end up in the exception handler you can figure out what view to map it to. But I think this meta data scheme would be fairly complex. You can implement such a scheme by finding out what was the original url being accessed when the exception was thrown, this can be done with the code snippet below.
(ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()
Once you know what the original request URL you can redirect to it, maybe using flash attribute to store the fact that there was an exception and what the error is.
The main problem wit the metadata will occur when you have a handler method that select between different views something like.
#RequestMapping(value = "/somepage", method = RequestMethod.GET)
public String somemethod(HttpSession session) throws Exception {
String abc = null;
if(someCondition) {
abc.length();
return "someview";
} else {
// do some stuff here.
return "someOtherView";
}
}
Even knowing that somemethod was the source of the error leaves you not knowing which branch in the if statement caused the exception.
I dont think you can do this without modifying all of your handler methods. However you can try to do this in a "pretty" way:
1) You can define your own annotation which will accept target view name as a parameter (e.g. #ExceptionView)
2) Next thing to do is marking your handler methods with it, e.g.:
#ExceptionView("someview")
#RequestMapping(value = "/somepage", method = RequestMethod.GET)
public String somemethod(HttpSession session) throws Exception {
String abc = null;
abc.length();
return "someview";
}
3) After that you can do something like this in exception handler:
#ExceptionHandler(Exception.class)
protected ModelAndView unhandledExceptionHandler(Exception ex, HandlerMethod hm) {
String targetView;
if (hm != null && hm.hasMethodAnnotation(ExceptionView.class)) {
targetView = hm.getMethodAnnotation(ExceptionView.class).getValue();
} else {
targetView = "someRedirectView"; // kind of a fallback
}
ModelAndView mv = new ModelAndView();
mv.setViewName(targetView);
mv.addObject("UNHANDLED_ERROR", "UNHANDLED ERROR. PLEASE CONTACT SUPPORT. "+ex.getMessage());
return mv;
}
Rather than sending the error on a separate page, you can you just put the error in the ModelAndView object. In your case you can just put the try/catch in your controller method and return the same view like so:
#RequestMapping(value = "/somepage", method = RequestMethod.GET)
public String somemethod(ModelAndView mv,HttpSession session) throws Exception {
mv.setViewName("someview");
try{
String abc = null;
abc.length();
} catch(Exception e) {
mv.addObject("UNHANDLED_ERROR", "UNHANDLED ERROR. PLEASE CONTACT SUPPORT. "+ex.getMessage());
}
return mv;
}
So add the ModelAndView to your method and return it.
I have not tried this out, but based on the documentation here, we can get the request object in the exception handler. We may not be able to get the view linked to the URL. Getting the view from the URL, and the state/model of the view will be the tricky part.
#ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception ex) {
logger.error("Request: " + req.getRequestURL() + " raised " + ex);
ModelAndView mav = new ModelAndView();
mav.addObject("exception", ex);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error");
return mav;
}
Create a controller method annotated with #RequestMethod("/server-error")
Create a controller method annotated with #ExceptionHandler which will return "forward:/server-error";

Categories