I'm experimenting with Spring MVC for an upcoming project. I'll need some web pages in the application to render multiple reusable "components" and figured that Tiles should help me accomplish this? Each component would have it's own controller.
The example I've put together is only partially working. It's a page with 3 tiles. The 3rd tile has a controller that's trying to return an ArrayList (via #ModelAttribute annotation) to the client but the ArrayList is empty when the view is included as a tile.
Here's the tiles setup in *-servlet.xml:
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
<property name="order" value="0"/>
</bean>
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles-config/tiles.xml</value>
</list>
</property>
</bean>
<bean id="viewResolver2" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="1" />
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
Here's my tiles config (tiles.xml):
<tiles-definitions>
<definition name="parent" template="/WEB-INF/jsp/tiles/parent_layout.jsp">
<put-attribute name="header" value="/WEB-INF/jsp/tiles/header.jsp"/>
<put-attribute name="body" value=""/>
<put-attribute name="recordsSection" value=""/>
<put-attribute name="widgetsSection" value=""/>
</definition>
<definition name="_n/list.htm" extends="parent">
<put-attribute name="recordsSection" value="/WEB-INF/jsp/_n/list.jsp"/>
<put-attribute name="widgetsSection" value="/WEB-INF/jsp/_n/widgets.jsp"/>
</definition>
</tiles-definitions>
Here's the JSP code behind the tile in question. The "widgetsList" expression is what is bound to the controller method but coming back empty when this view is included as a tile.
<%#page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" isELIgnored="false"%>
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%#taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%#taglib prefix="display" uri="http://displaytag.sf.net" %>
<h3>Widgets</h3>
<p>widgetsList=${widgetsList}</p>
<div id="displaylist">
<display:table id="items" name="${widgetsList}" cellspacing="0" export="true">
<display:column title="Widget ID" property="widgetId"/>
<display:column title="Widget Name" property="widgetName"/>
<display:column title="Widget State" property="widgetState"/>
</display:table>
</div>
Here's the controller:
#Controller
public class WidgetController {
#Autowired
private WidgetDaoImpl widgetDao = new WidgetDaoImpl();
#RequestMapping("_n/widgets.htm")
public String widgets() {
return "_n/widgets.htm";
}
#ModelAttribute("widgetsList")
public ArrayList<Widget> getWidgets() {
System.out.println("executing getWidgets()");
ArrayList<Widget> records = widgetDao.listWidgets();
return records;
}
}
When I access the the view "widgets.jsp" directly, the "widgetsList" attribute gets populated, it's only when it's added as a tile that it returns nothing.
Tiles only gives you a composite view and with Spring MVC a controller can only return one view, in this case a whole tiles-defined page will be returned by a single controller.
I don't have much experience with tiles configuration, but what you're trying to achieve is only possible if you can have controller-mapped URLs (and not JSPs) as tiles elements, so that the tiles processor will invoke the spring controllers for each of the "components".
But since you're talking about components and you seem to need them here, why not start with a component oriented framework (Wicket for instance can easily be integrated with Spring out-of-the box).
A pattern i found great while doing this is using jQuery, ajax and json with tiles. On the pages you want your widgets include a marker div like
<div id="displayWidgets"></>
With jQuery check for this div on page load and if it exists do a json call to the controller for this content.
This approach is great because when you include the javascript below, the controller for each page doesn't need to know about any model information except its own. I use this for populating common information like headers and footers that are dynamic.
$.ready(){
if($('div#displayWidgets')){
$.getJSON('/widgetsList', function(data) {
//loop over json and create your html, url below has great examples
}
}
}
http://api.jquery.com/jQuery.getJSON/
Json tutorial from spring controller using jackson
http://www.mkyong.com/spring-mvc/spring-3-mvc-and-json-example/
Related
Hi I am very new to Spring MVC. I am trying to create 1 login page with annotation based validation. Since I have kept #NonEmpty for user ID and password, I am expecting when user enters nothing and submit then validation should fail. But my if block(result.hasErrors()) in login controller is not getting executed when user do not enters anything.I have tried different approach but still no good result for me.
LoginController.java
public ModelAndView doLogin(HttpServletRequest request, #Valid #ModelAttribute("user") User user, BindingResult result, Model model) {
if(result.hasErrors()) {
return new ModelAndView("loginForm");
}
}
User.java
public class User {
#NonEmpty
private String userID;
#NonEmpty
private String password;
.
.
.
}
loginForm.jsp
<body>
<form:form modelAtrribute="user" method="POST" commandName="user">
User ID:
<input type="text" name="userID"/>
<form:errors path="userID"/>
Password:
<form:password path="password"/>
<form:errors path="password"/>
<input type="submit" value="Login"/>
</form>
</body>
spring-servlet.xml
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="viewResolver">
<property name="viewClass">
<value>
org.springframework.web.servlet.view.tiles2.TilesView
</value>
</property
</bean>
<bean class="org.springframework.web.servlet.view.tiles2.TilesConfigurer" id="tilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles.xml</value>
</list>
</peroperty>
</bean>
tiles.xml
<tiles-definitions>
<definition name="plain.definition" template="/WEB-INF/plain/plain.jsp">
<put-attribute name="body" value=""></put-attribute>
</definition>
<definition extends="plain.definition" name="loginForm">
<put-attribute name="title" value="Login"></put-attribute>
<put-attribute name="body" value="/WEB-INF/jsp/loginForm.jsp"></put-attribute>
</definition>
</tiles-definitions>
Found solution for this problem. In my spring-servlet.xml I had <context:annotation-config> but not <mvc:annotation-driven />.
<context:annotation-config> supported some annotations but for #Valid I needed <mvc:annotation-driven />. After adding this my validations are running fine.
Though I have not figured out completely differences between these 2 tags.
I'm using spring mvc and dwr, I already made some tests and my configuration works, but now that I'm trying to access the database from the script I get a NullPointerException on this line:
List<Citas> citas = citasManager.select();
Obviously that means that the instance of "citasManager" is not being sent to the script but the property is defined on the bean, so I don't know the correct way I should define my bean so that the class "People" gets the instance.
My bean is defined as follows:
<bean id="dwr" class="dwr.People" scope="session">
<dwr:remote javascript="People">
<dwr:include method="createCrowd" />
<dwr:include method="getMatchingFromLargeCrowd" />
</dwr:remote>
<property name="citasManager" ref="citasManager" />
</bean>
I didn't know I was supossed to declare the setter method too. Even to everywhere else spring manages them on it's own.
<bean id="dwr" class="dwr.People" scope="session">
<dwr:remote javascript="People">
<dwr:include method="createCrowd" />
<dwr:include method="getMatchingFromLargeCrowd" />
<dwr:include method="setCitasManager" />
</dwr:remote>
<property name="citasManager" ref="citasManager" />
</bean>
I'm trying to achieve so that Thymeleaf can work together with Spring MVC 3 and use 2 view resolvers, one for jsp and one for html templates. I'd like my Thymeleaf ServletContextTemplateResolver to be asked first to attempt to resolve a view and if it can't find one, pass on to the Spring MVC 3 InternalResourceViewResolver.
I've set the order value of ServletContextTemplateResolver to 1 this way:
<bean id="templateResolver"
class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML5" />
<property name="order" value="1" />
<property name="cacheable" value="false" />
</bean>
and the order of InternalResourceViewResolver" to 2 in the same fashion:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
<property name="order" value="2" />
</bean>
As I understand it from the docs the highest order is consulted last.
In the "views" folder I have one "index.jsp" and one "index.html" and my general idea is that first ServletContextTemplateResolver will be asked to attempt resolving and it will resolve to "index.html" if there is one, and only if no suitable view can be found by ServletContextTemplateResolver will the InternalResourceViewResolver be asked to resolve the view.
But the result I have is that when InternalResourceViewResolver is active, it resolves all views no matter what. If I comment it out then ServletContextTemplateResolver resolves fine.
Are these resolvers impossible to pair up in this fashion? What's the alternative?
Thymeleaf throws an error when trying to find pages outside of their view resolver instead of passing it onto the next view resolver. By setting the excludeViewNames, skips trying to resolve the view name within Thymeleaf. See my example code below.
/**
* Configures a {#link ThymeleafViewResolver}
*
* #return the configured {#code ThymeleafViewResolver}
*/
#Bean
public ThymeleafViewResolver thymeleafAjaxViewResolver()
{
String[] excludedViews = new String[]{
"login", "logout"};
AjaxThymeleafViewResolver resolver = new AjaxThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setOrder(1);
/*
* This is how we get around Thymeleaf view resolvers throwing an error instead of returning
* of null and allowing the next view resolver in the {#see
* DispatcherServlet#resolveViewName(String, Map<String, Object>, Locale,
* HttpServletRequest)} to resolve the view.
*/
resolver.setExcludedViewNames(excludedViews);
return resolver;
}
I;m trying to create RSS feeds for my web site. I follow the tutorial from mkyong (http://www.mkyong.com/spring-mvc/spring-3-mvc-and-rss-feed-example/) which was quite useful. According to this tutorial i create a model class and the following class
public class CustomRssViewer extends AbstractRssFeedView{
#Override
protected void buildFeedMetadata(){
//some code
}
#Override
protected List<Item> buildFeedItems(){
//some code
}
}
And finally the controller class
#Controller
public class RssController {
#RequestMapping(value="/rssfeed", method = RequestMethod.GET)
public ModelAndView getFeedInRss() {
//set the RSS content
ModelAndView mav = new ModelAndView();
mav.setViewName("rssViewer");
mav.addObject("feedContent", items);
return mav;
}
}
According to the tutorial the View rssViewer belongs the class CustomRssViewer , so i need to write it at the dispatcher servlet the following lines of code:
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<bean id="rssViewer" class="com.mkyong.common.rss.CustomRssViewer" />
My problem is that i'm using apache tiles. So the rssViewer can not be recognised as i didn't enhanced it to the tiles definition. And i really don't know how can i do this. For example i need to write something as the following:
<definition name="rssViewer" template="?">
<put-attribute name="title" value=""/>
<put-attribute name="content" value=""/>
</definition>
At the template i don't know what to declare as well as at the put-attribute.Because until now at the template i use to declare the direction that a specific jsp exists. Something like this:
template="/WEB-INF/pages/mypage.jsp"
And also at the view-properties i don't know what should i declare.
Thanks in advance for any comment or response.
You should use a ContentNegotiatingViewResolver in conjuction with that example's BeanNameViewResolver. Just declare the order property of your already existing BeanNameViewResolver to be 1, and set the order property of the new ContentNegotiatingViewResolver to 0.
You should then configure the ContentNegotiatingViewResolver to use the appropriate View for RSS, and set the media type for RSS.
Here is an example from the Spring Docs:
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="atom" value="application/atom+xml"/>
<entry key="html" value="text/html"/>
<entry key="json" value="application/json"/>
</map>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>
Note, they are using atom, not RSS, but the idea is the same. Also they do not set the order (which you should do).
I have a issue with my Spring.Net configuration where its not injecting an object. I have a CommService to which an object named GeneralEmail is injected to. Here is the configuration:
<!-- GeneralMail Object -->
<object id="GeneralMailObject" type="CommUtil.Email.GeneralEmail, CommUtil">
<constructor-arg name="host" value="xxxxx.com"/>
<constructor-arg name="port" value="25"/>
<constructor-arg name="user" value="xxxx#xxxxx.com"/>
<constructor-arg name="password" value="xxxxx"/>
<constructor-arg name="template" value="xxxxx"/>
</object>
<!-- Communication Service -->
<object id="CommServiceObject" type="TApp.Code.Services.CommService, TApp">
<property name="emailService" ref="GeneralMailObject" />
</object>
The communication service object is again injected to many other aspx pages & service. In one scenario, I need to call the commnucation service from an static WebMethod. I try doing:
CommService cso = new CommService();
But when i try to get the emailService object, its null! why didn't the spring inject the GeneralMail object into my cso object? What am I doing wrong and how do I access the object from spring container.
Thanks in advance for the suggestions and solutions.
Reagrds,
Abdel Olakara
IApplicationContext ctx = ContextRegistry.GetContext();
CommService cso= (CommService)ctx.GetObject("CommServiceObject");