I'm assuming this is very easy to do, but I've been searching for a couple hours on Google and StackOverflow with no luck.
For starters, I have a jsp page using Dojo. I use Dojo's request module to make AJAX calls to Spring controllers for all sorts of things. The controllers call services, the services call DAOs, etc etc.
Originally, I had a properties file that contained several configuration settings for my site. These were injected into various Spring components using #Value. I wanted to make these dynamically configurable from my admin page, so I added a domain object called "SiteSettings" that holds the values from my properties file. When my webapp starts up, if it can't fine a SiteSettings object in persistence, it creates a new one, populates it with the settings from my properties file, and persists it. If one is already in persistence, it just uses it.
All of the site properties are used on the back-end except for one, a boolean called "authenticationRequired". I need to access this value in a javascript value on the front-end. This would be very easy to obtain via AJAX after the page loads, but I thought it's kind of silly to have to make an additional request, when I'm sure I should just be able to get the value from my SiteSettingsService (which uses the DAO to get the domain object).
Right now, I'm getting the value directly from the properties file:
<fmt:bundle basename="swtc">
<fmt:message key="swtc.authenticationRequired" var="authenticationRequired"/>
</fmt:bundle>
<script type="text/javascript">
window.authenticationRequired = <c:out value="${authenticationRequired}"/>;
</script>
How can I modify this get the value from my service/dao/domain object instead of directly from my properties file? Here are my controller and service files... nothing fancy:
Controller:
#RequestMapping(value = "/getSiteSettings", method = RequestMethod.GET)
#ResponseBody
public ModelMap getSiteSettings(ModelMap model) {
try {
SiteSettings siteSettings = siteSettingsService.getSiteSettings();
model.addAttribute("siteSettings", siteSettings);
model.addAttribute("success", true);
} catch (Exception ex) {
logger.error("There was an error getting the site settings data. ", ex);
model.addAttribute("exceptionMessage", ex.getLocalizedMessage());
model.addAttribute("success", false);
}
return model;
}
Service:
#Override
public SiteSettings getSiteSettings(){
List<SiteSettings> siteSettings = siteSettingsDao.findAll();
if (siteSettings != null && !siteSettings.isEmpty()){
return siteSettingsDao.findAll().get(0);
}
return null;
}
In the controller method that is responsible for rendering the overall jsp that requires the property, you should just be able to put the authenticationRequired attribute on the Model so it becomes available in the page for output into the Javascript. You'd need to autowire an instance of the SiteSettingsService into that controller:
#Controller
public class MainPageController { // or whatever it happens to be called
#Autowired
private SiteSettingsService siteSettingsService;
#RequestMapping(value="/mainPage", method=RequestMethod.GET)
public String displayMainPage(Model model) {
SiteSettings siteSettings = siteSettingsService.getSiteSettings();
// Make the property available to the view
model.addAttribute("authenticationRequired", siteSettings.isAuthenticationRequired());
return "mainPage";
}
}
Related
I am trying to pass data from DAO to JSP using ModelMap. It works but, when I refresh that page, same data comes repeatedly on every refresh. I want data not to come again and again on refreshing the page. Help me for this issue.
#Autowired
private SelectInfo selectInfo; /* Instance of SelectInfo DAO class injected here, here the method of fetching data from databse is defined and fetched data is passed to GetInfo bean*/
#Autowired
private GetDetail getDetails; /* GetDetail is the bean where the values are stored which are coming from database */
#RequestMapping(value="/selectInfo", method=RequestMethod.GET)
public String registerComplete(ModelMap model,HttpSession session,HttpServletResponse res) {
if(session.getAttribute("user")==null) {
return "redirect:/";
}else {
selectInfo.getInfo(); /*getInfo is the method defined in SelectInfo class which fetch data from database*/
/* the values are adding in modelmap using getter method from GetInfo bean */
model.put("cities", getDetails.getCities());
model.put("theaters", getDetails.getTheaters());
model.put("movies", getDetails.getMovies());
model.put("dates", getDetails.getDates());
model.put("timings", getDetails.getTimings());
return "cities";
}
So you do not want call your db each time when page refreshes? I think you can try caching in that case. Please take a look at example here: https://spring.io/guides/gs/caching/
You can just add annotation on your controller method.
#RequestMapping(value="/selectInfo", method=RequestMethod.GET)
#Cacheable
public String registerComplete(ModelMap model,HttpSession session,HttpServletResponse res) {
//your code goes here
}
If you are passing the data using modelMap means, data will be transferred each time a page load or refreshed. In order to load the data once use ajax and sessionStorage
Create new method in controller class to return data like this
#RequestMapping(value="/cities", method=RequestMethod.GET)
public #ResponseBody Object registerComplete(){
return getDetails.getCities()
}
In javascript check the sessionStorage if its null then load the data using ajax.
$(document).ready(function(){
if(sessionStorage.cities ==null){
$.ajax({
type : 'GET',
url : "/cities",
contentType:'application/json',
data:JSON.stringify(data),
success : function(response) {
sessionStorage.cities =response;
}
});
}
}) ;
By this you can restrict data load on each refresh
Caching is the natural way to avoid unnecessary trips to database. Spring provides support for this using #Cacheable annotation.
If you are using Spring Boot, things are easier for you. Ensure that you have got spring-boot-starter-cache in your dependencies or one of your spring starters depends on spring-boot-starter-cache. Then you can annotate your DAO method like so:
#Component
public class SelectInfo {
...
#Cacheable("dbInfo")
public <returnType> getInfo() {
// Implementation of the method
}
...
}
Putting the Cacheable annotation over the method in your DAO is advised since it is responsible for fetching the actual data that you want to cache.
If you are not using Spring Boot, you'll have to include a Cache Provider and configure a bean for that provider. The details of how to do this can be found in the Spring Caching Docs https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-caching.html#boot-features-caching-provider
Before asking question first i describe the scenario. I have multiple servlet contexts. suppose /**book**/review url is corresponding to book-servlet.xml and /**report**/update corresponds to report-servlet.xml.
Here are two controllers
#Controller
#SessionAttributes(BookController.COMMAND_NAME)
public class BookController {
public static final String COMMAND_NAME = "login";
#GetMapping("/book/review")
public String show(ModelMap modelMap) {
modelMap.put(COMMAND_NAME, getBook());
return "redirects:" + "/report/update"; //it redirects to second controller method
}
}
#Controller
#SessionAttributes(ReportController.COMMAND_NAME)
public class ReportController {
public static final String COMMAND_NAME = "report";
#GetMapping("/report/update")
public String show(ModelMap modelMap) {
modelMap.put(COMMAND_NAME, getReport());
return "redirects:" + "/report/done";
}
}
Observe this two controller. When i put getBook() in model it stores this object in session after method excution since #SessionAttributes(BookController.COMMAND_NAME) is added above class definition. But, after redirection to /report/update (different servlet context) we are also trying to put getReport() in model. And after handler method excution spring should put the object in session as it does for first case.Unfortunately for second controller it doesn't store it in session.
My question is if the first controller method can successfully save it in session why the second controller can't? Or is it for servlet context switching / multiple get requests or something like this. Please i need to know the reason it behaves weirdly. I am now totally confused about #SessionAttributes. It should work same for both controllers.
After studying on this i found that it is wise to use RedirectAttributes for this type of redirect scenario. Cause ModelMap is intended for scenario where you use this ModelMap attributes to rendering a view. And FlashAttribute will survive after immediate redirection to another handler method and then be erased. So, i did my solution using RedirectAttrinute support from spring.
I want to return a model from my Spring MVC controller, which will be visible to all pages in my application.
So, I need a variable which return some user details.
This is my code:
#RequestMapping(value = "/*", method = RequestMethod.GET)
public void status(Model model){
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
String username = auth.getName();
Manager mgr = managerService.findOne(username);
model.addAttribute("buget", mgr.getTeam().getTeamBuget());
}
And in my jsp page, i write something like this:
<li> <c:out value="${buget}" /> <span class="glyphicon glyphicon-euro"></span></li>
I want to be able to print the ${buget} in every page from my app.
So, my method don't work, nothing is appear in my pages and the biggest problem
for me is that I don't get any exceptions or errors which could help me. Who can help me with some advices?
I'm not entirely sure what it is that you're trying to do. Do you mean that you want buget to be part of every page that you hit? If so, you have to insert it into the model. Based on your code, I'm assuming that you have the mistaken impression that status is going to be called regardless of whatever page you hit. Spring will resolve to the most-specific handler and so if you have another handler method in another controller that is more specific, Spring will use that. But even if the one you have was called, how would Spring know that it has to call the most-specific one next? So how would you add page-dependent model attributes to the model?
If you want buget to be part of every response, you can use #ControllerAdvice (see here for a blog post with more details):
#ControllerAdvice
public class BugetControllerAdvice {
#ModelAttribute
public void addBugetToModel(Model model) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();
Manager mgr = managerService.findOne(username);
model.addAttribute("buget", mgr.getTeam().getTeamBuget());
}
}
Spring will now call this method before every handler-method in every controller. You can also take a look at the Spring Reference for more information.
I've read a couple of ways to download a file from a Spring Controller methods, either returning a FileSystemResource or void and using the outputStream.
What I want is to return a String because I want to validate the user input first. Something like...
public String downloadFile(BindingResult result, RedirectAttributes attr, ...) {
if(result.hasErrors()) {
....
return "redirect:/showForm";
}
...
return "success";
}
is it possible? What alternatives do I have to do this? Thanks
If I understand you correctly, what you want to do is either send the user to a page, like a "It didn't work/validate" page OR send the file.
If this is the case, what we did to solve it was to return a ModelAndView from our handler. You can include it as a parameter to your handler as well, and Spring will create one for you and you can just return it. If the form input failed validation, we set the View on the ModelAndView to a custom view that would set the Content-Disposition and related headers, then got the OutputStream from the HttpServletResponse and wrote the file out as bytes.
Something like this (pseudo code, so may need tweaking):
#RequestMapping("/myHandler")
public ModelAndView myHandler(#ModelAttribute("myBean") MyBean myBean, BindingResults results, ModelAndView mav) {
if (results.hasErrors()) {
mav.setViewName("myErrorView"); // Uses the default view you have setup, passing the name as if you returned a String
} else {
mav.setView(new FileOutputView(dataToOutputOrWhatever); // Custom View class to output file as needed
}
return mav
}
However, if you want to return a String AND do the file download, then you have to jump through some hoops. The easiest way I have found to do this is to set a model attribute that you can read on the JSP-side that either tells the JSP to call the file download or not. Within the JSP code you would have a combination of JSTL and JavaScript to trigger the download, either using AJAX or, in the our case, JavaScript to do the download in an IFRAME.
I am refactoring a legacy application to use Spring MVC. All of my controllers (legacy) return an object of type Model and my legacy dispatcher write the output of model.getContent(), the method getContent does internal processing and returns a json string. I have hundreds of controllers and do not want to rewrite them. Is it possible to write a custom view handler and include it in the spring servlet config?
Sample Controller:
public UserList extends BasicAction {
#Autowired
UserService userService;
#Autowired
UserCommand userCommand;
#Override
public Model getModel(Request req, Response resp)
throws ServletException, IOException {
Model model = new Model();
List<User> users;
try {
users = userService.getUsers((UserCriteria)userCommand.getResult());
model.addCollection(users);
model.setWrapper(new UserWrapper());
} catch (ValidationException e) {
e.printStackTrace();
} catch (WebCommandException e) {
e.printStackTrace();
}
return model;
}
}
I'm planning to annotate as #Controller. Specify the #RequestMapping or in the xml config, remove the base class BasicAction (legacy mvc). I've recently introduced spring to this project and refactored to use Dependency Injection and Request Scoped command objects (request wrappers)
The most straightforward is to implement View interface on your Model class. Then your legacy controllers can return this class directly (as they are now) and it will get rendered by DispatcherServlet via calling its render method.
Another possibility is to implement your own HandlerMethodReturnValueHandler, where the handler can actually render the response and mark the response as handled (mavContainer.setRequestHandled(true);) so that DispatcherServlet will not try to render any view.
I think what you want to do is create a custom ViewResolver that outputs your JSON response. This would be configured in Spring MVC to set the ViewResolver list, placing yours up top to have more precedence. The way it is supposed to work (from my recollection) is that Spring MVC will start at the top of the list, and try each ViewResolver until it finds one that returns the one that handles the return type. You will have to google how to make custom ViewResolvers, as I have only used them, never created one, but I know it's an interface so it should be do-able. I believe this would be the only way to do this that would not require any code changes in your controllers.
The more "preferred" method to do JSON, however, is to have Jackson in your classpath and simply return the object you want to serialize to JSON. Spring will auto-magically convert that to JSON, I believe using a ViewResolver they provide. But, I can surely relate to not wanting to refactor lots of working code.