ModelAndView in Spring MVC - spring

I am going through the Spring API. I went through the ModelAndView class. I found there two mehods in the class which return Map. One is getModel() and other is getModelInternal(). They both return Map. What is the difference between these methods.
Thank you.

Check javadoc for methods:
/**
* Return the model map. May return {#code null}.
* Called by DispatcherServlet for evaluation of the model.
*/
protected Map<String, Object> getModelInternal() {
return this.model;
}
/**
* Return the model map. Never returns {#code null}.
* To be called by application code for modifying the model.
*/
public Map<String, Object> getModel() {
return getModelMap();
}
So, one be called by client - another by framework, one nullable - another not-null.

Related

Why does the #PostConstruct of the subclass work in this case?

I have the following Spring Bean structure:
public abstract class XmlBaseChild {
protected Integer value;
protected String text;
#Autowired
transient protected MasterCodeService masterCodeService;
public XmlBaseChild(Integer value) {
setValue(value);
}
/**
* Set the Numeric value of the ChildView.
* This code is common for all childViews and handles a null value.
* #param value Numeric value of the ChildView
*/
#JsonProperty(value="id")
public void setValue(Integer value) {
if (value == null) {
this.value = null;
this.text = null;
return;
}
setConcreteValue(value);
}
/**
* Set the Numeric value of the ChildView.
* This code must be overridden by the concrete childViews.
* #param value Numeric value of the ChildView
*/
protected void setConcreteValue(Integer value){
boolean keyNotFound = true;
if (value != null && value > -1) {
this.value = value;
String messageKey = getValueFromMap(value, GetMasterCodeMapForChildView());
if (messageKey != null) {
this.text = LocalizeString(messageKey, null, getLocale);
keyNotFound = false;
}
}
if (keyNotFound){
throw new NotFoundException();
}
}
protected abstract Map<String, MasterCodeView> GetMasterCodeMapForChildView();
}
And the subclass:
#Component
#XmlRootElement(name=XmlDeployTool.VIEW_NAME)
public class XmlDeployTool extends XmlBaseChild {
public static Map<String, MasterCodeView> toolTypeCodes = new HashMap<String, MasterCodeView>();
/**
* Constructor for creating this object and preparing for marchalling (from java objects to xml/json).
* #param value Numeric value of the ChildView
* #param request HttpServletRequest
* #param includeSelf Include SELF link
* #param includeUP Include UP link
*/
public XmlDeployTool(Integer value) {
super(value);
}
/**
* Initialize the Tool Type codes after the component is wired (postconstruct),
* so that they are available in the constructor when an XmlDeploy object is created.
*/
#PostConstruct
protected void initializeDeployToolTypeCodes() {
toolTypeCodes = convertListToMap(masterCodeService.getToolTypeCodes());
}
#Override
protected Map<String, MasterCodeView> GetMasterCodeMapForChildView() {
return toolTypeCodes;
}
}
However, from what I understand from other posts like Order of #PostConstruct and inheritance, the #PostConstruct here normally executes AFTER the constructor is called. Then why is the toolTypeCodes map populated during the constructor? Is this part of the #Component annotation of Spring?
I also tried doing this with the masterCodeView map defined in the XmlBaseChild and only the PostConstruct method defined in the XmlDeployTool class, but that didn't work, the list didn't get initialized in that case. Why is this?
After checking the documentation and reading up some more, I figured out what's going on here:
Because my subclass is annotated with #Component, the PostConstruct triggers as part of the Spring startup process, even before any invocations of the normal constructor. Because of this, the static Map with MasterCodeViews gets populated, and since this is static, it stays populated as part of the subclass static properties. Because of this, this map has the proper usable data during construction.
When I tried to move the Map to the base class, In effect I turned this from a static property of the subclass to a static property of the subclass, which meant each constructor in turn populated it with the separate properties, leading to the map having the wrong data most of the time. When after that I tried to do this with a non-static map, the data wasn't retained when I invoked the constructor from code because this was effectively a new object with no initialized components.

Validation at both Service and Controller Layer?

Is there a need to do another round of input validation, non business logic related in the service layer?
Service Layer
#Service
#Transactional
#Validated
public class AppServiceImpl implements AppService {
public App createApp(#Valid App app) { // is there a need to do #Valid here?
return appRepository.save(app);
}
}
Controller Layer
#RestController
#RequestMapping("/api")
public class AppResource {
private final AppRepository appRepository;
private final AppServiceImpl appServiceImpl;
#Autowired
public AppResource(AppRepository appRepository, AppServiceImpl appServiceImpl) {
this.appServiceImpl = appServiceImpl;
this.appRepository = appRepository;
}
/**
* POST /apps : Create a new app.
*
* #param app the app to create
* #return the ResponseEntity with status 201 (Created) and with body the new app, or with status 400 (Bad Request) if the app has already an ID
* #throws URISyntaxException if the Location URI syntax is incorrect
*/
#PostMapping("/apps")
#Timed
public ResponseEntity<App> createApp(#Valid #RequestBody App app) throws URISyntaxException {
log.debug("REST request to save App : {}", app);
if (app.getId() != null) {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(ENTITY_NAME, "idexists", "A new app cannot already have an ID")).body(null);
}
App result = appServiceImpl.createApp(app);
return ResponseEntity.created(new URI("/api/apps/" + result.getId()))
.headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString()))
.body(result);
}
}
Short form: Yes, you have to validate again.
From design perspective your class provides an public interface where you in general don't know who invokes the method. So for your class / method to ensure to work properly you'll have to validate the input.
If the context the class is used in is well known and you "know" that the validation is done before you may skip the additional validation. In this case you are accepting the risk that if in the future the validation is not done in the Controlling Layer or you add additional classes / use cases the invocation may fail or give unexpected results.

How to return jsp from ajax call?

I am using spring mvc without annotations.
I want to take jsp(html code) as response from ajax call.
I do not want to use response.getWriter().print(..). can any one tell me any other solution.?
You can return JSP using ModelAndView like this
#RequestMapping (
value = "/path/call",
method = RequestMethod.POST
)
#ResponseBody
public ModelAndView blah(....) {
return new ModelAndView("location to JSP file");
}
You could add data to MandV using the method below
/**
* Add an attribute to the model.
* #param attributeName name of the object to add to the model
* #param attributeValue object to add to the model (never {#code null})
* #see ModelMap#addAttribute(String, Object)
* #see #getModelMap()
*/
public ModelAndView addObject(String attributeName, Object attributeValue) {
getModelMap().addAttribute(attributeName, attributeValue);
return this;
}
i highly recommend reading the documentation, without actual knowledge of the spring framework you will have a hard time using it ... As has already been mentioned, you will usually have a Controller class which handles requests - these are annotated with #RequestMapping and the controller is annotated with #Controller, of course. This is an example from the documentation :
#Controller
#RequestMapping("/appointments")
public class AppointmentsController {
private final AppointmentBook appointmentBook;
#Autowired
public AppointmentsController(AppointmentBook appointmentBook) {
this.appointmentBook = appointmentBook;
}
#RequestMapping(method = RequestMethod.GET)
public Map<String, Appointment> get() {
return appointmentBook.getAppointmentsForToday();
}
#RequestMapping(value="/{day}", method = RequestMethod.GET)
public Map<String, Appointment> getForDay(#PathVariable #DateTimeFormat(iso=ISO.DATE) Date day, Model model) {
return appointmentBook.getAppointmentsForDay(day);
}
#RequestMapping(value="/new", method = RequestMethod.GET)
public AppointmentForm getNewForm() {
return new AppointmentForm();
}
#RequestMapping(method = RequestMethod.POST)
public String add(#Valid AppointmentForm appointment, BindingResult result) {
if (result.hasErrors()) {
return "appointments/new";
}
appointmentBook.addAppointment(appointment);
return "redirect:/appointments";
}
}
As you can see, requests are now semi-automatically resolved / passed to JSP-parsers which process your stored JSP and output HTML. That is called MVC and although the MVC-model in spring differs a bit from the standard point of view its quite useful and somewhat standard'ish.
Yet again : if you want to use spring, please read the documentation. It is important and useful.
spring mvc without annotations
pretty much defeats the whole concept. I think you need to re-do your application design, apparently it is flawed --- no offense, im just stating the obvious.

Spring MVC : Common param in all requests

I have many controllers in my Spring MVC web application and there is a param mandatoryParam let's say which has to be present in all the requests to the web application.
Now I want to make that param-value available to all the methods in my web-layer and service-layer. How can I handle this scenario effectively?
Currently I am handling it in this way:
... controllerMethod(#RequestParam String mandatoryParam, ...)
and, then passing this param to service layer by calling it's method
#ControllerAdvice("net.myproject.mypackage")
public class MyControllerAdvice {
#ModelAttribute
public void myMethod(#RequestParam String mandatoryParam) {
// Use your mandatoryParam
}
}
myMethod() will be called for every request to any controller in the net.myproject.mypackage package. (Before Spring 4.0, you could not define a package. #ControllerAdvice applied to all controllers).
See the Spring Reference for more details on #ModelAttribute methods.
Thanks Alexey for leading the way.
His solution is:
Add a #ControllerAdvice triggering for all controllers, or selected ones
This #ControllerAdvice has a #PathVariable (for a "/path/{variable}" URL) or a #RequestParam (for a "?variable=..." in URL) to get the ID from the request (worth mentioning both annotations to avoid blind-"copy/past bug", true story ;-) )
This #ControllerAdvice then populates a model attribute with the data fetched from database (for instance)
The controllers with uses #ModelAttribute as method parameters to retrieve the data from the current request's model
I'd like to add a warning and a more complete example:
Warning: see JavaDoc for ModelAttribute.name() if no name is provided to the #ModelAttribute annotation (better to not clutter the code):
The default model attribute name is inferred from the declared
attribute type (i.e. the method parameter type or method return type),
based on the non-qualified class name:
e.g. "orderAddress" for class "mypackage.OrderAddress",
or "orderAddressList" for "List<mypackage.OrderAddress>".
The complete example:
#ControllerAdvice
public class ParentInjector {
#ModelAttribute
public void injectParent(#PathVariable long parentId, Model model) {
model.addAttribute("parentDTO", new ParentDTO(parentId, "A faked parent"));
}
}
#RestController
#RequestMapping("/api/parents/{parentId:[0-9]+}/childs")
public class ChildResource {
#GetMapping("/{childId:[0-9]+}")
public ChildDTO getOne(#ModelAttribute ParentDTO parent, long childId) {
return new ChildDTO(parent, childId, "A faked child");
}
}
To continue about the warning, requests are declaring the parameter "#ModelAttribute ParentDTO parent": the name of the model attribute is not the variable name ("parent"), nor the original "parentId", but the classname with first letter lowerified: "parentDTO", so we have to be careful to use model.addAttribute("parentDTO"...)
Edit: a simpler, less-error-prone, and more complete example:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#RestController
public #interface ProjectDependantRestController {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
*
* #return the suggested component name, if any
*/
String value() default "";
}
#ControllerAdvice(annotations = ParentDependantRestController.class)
public class ParentInjector {
#ModelAttribute
public ParentDTO injectParent(#PathVariable long parentId) {
return new ParentDTO(parentId, "A faked parent");
}
}
#ParentDependantRestController
#RequestMapping("/api/parents/{parentId:[0-9]+}/childs")
public class ChildResource {
#GetMapping("/{childId:[0-9]+}")
public ChildDTO getOne(#ModelAttribute ParentDTO parent, long childId) {
return new ChildDTO(parent, childId, "A faked child");
}
}

Testing ModelMap in Spring with JMock

I'm new to JMock, trying to develop a Spring controller test. Here is my test method:
#Test
public void testList() {
context.checking(new Expectations() {{
Student student = new Student(767001);
oneOf(studentService).getByNumber(767001); will(returnValue(student));
}});
ModelMap model = new ModelMap();
Student student = new Student(767001);
model.addAttribute("student", student);
CourseRightController instance = new CourseRightController();
request.setMethod("GET");
Assert.assertEquals(studentService.getByNumber(767001),model.get(student));
The question is how I'm able to test if the model contains the right object and object values? ModelMap is not that flexible than e.g ModelAndWiew. I can't get access to model attributes so the last code line here is not how it should be.
I usually use the Model interface and then in a test super class I have code which allows me to get at things in the Model
#Ignore
public abstract class SpringControllerTestCase {
/**
* Spring Model object - initialised in #Before method.
*/
private Model model;
/**
* Initialise fields before each test case.
*/
#Before
public final void setUpAll() {
model = new ExtendedModelMap();
}
public final Model getModel() {
return model;
}
#SuppressWarnings("unchecked")
public <T> T getModelValue(final String key, final Class<T> clazz) {
return (T) getModel().asMap().get(key);
}
}
then in a test I can do
assertEquals("someValue", getModelValue("bean", String.class));
or
assertTrue(getModelValue("student", Student.class).getId() == "767001");
Note this is all just shorthand for code like this
Student student = (Student) model.asMap().get("student");
assertEquals(767001, student.getId());
You can use extended model map instead for more flexibility. And you should declare references using the interface not implementation.
There is also this package to be included in spring 3.2 which may help : https://github.com/SpringSource/spring-test-mvc
However I have always been fine using extendedmodelmap and plain old hashmaps.
In your example, have you implemented equals (and hashcode) correctly, if you have not overrridden these methods the assertEquals will be testing if the objects are the same reference.

Resources