This repository contains a simple spring portlet. It is intended as a step by step tutorial.
- Create a new maven web application project. Maven archetype can be used to generate the application (e.g. com.liferay.maven.archetypes:liferay-portlet-archetype).
mvn archetype:generate
- Add dependencies on Spring into pom.xml.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc-portlet</artifactId>
<version>3.2.6.RELEASE</version>
</dependency>
- Insert spring initalization code inot web.xml.
- Insert spring context listener
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
- Specify the application spring configuration file location
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-context/portlet-application-context.xml</param-value>
</context-param>
- Add servlet for view processing
<servlet>
<servlet-name>ViewRendererServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.ViewRendererServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ViewRendererServlet</servlet-name>
<url-pattern>/WEB-INF/servlet/view</url-pattern>
</servlet-mapping>
- Create the application configuration file portlet-application-context.xml.
- Activate annotation and configure view resolver in main context file.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!--Enables @Autowired annotation-->
<context:annotation-config/>
<!-- Spring MVC VIEW Configuration -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="contentType" value="text/html;charset=UTF-8" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
- Define spring portlet
- Define portlet in portlet.xml
<portlet>
<portlet-name>BasicSpringPortlet</portlet-name>
<portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
<init-param>
<name>contextConfigLocation</name>
<value>/WEB-INF/spring-context/portlet/basic-spring-portlet.xml</value>
</init-param>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<supported-locale>cs</supported-locale>
<resource-bundle>content.basic-spring-portlet</resource-bundle>
</portlet>
- Configure portlet in liferay-portlet.xml
<portlet>
<portlet-name>BasicSpringPortlet</portlet-name>
<instanceable>false</instanceable>
<header-portlet-css>/css/main.css</header-portlet-css>
<header-portlet-javascript>/js/main.js</header-portlet-javascript>
</portlet>
-
Configure portlet in liferay-display.xml
<category name="category.sample"> <portlet id="BasicSpringPortlet" /> </category>
-
Create portlet spring context file
<?xml version="1.0" encoding="UTF-8"?> <!--suppress SpringFacetInspection --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- Spring MVC Message Source --> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="useCodeAsDefaultMessage" value="true"/> <property name="basenames"> <list> <value>content.basic-spring-portlet</value> </list> </property> </bean> </beans>
-
Create resource bundles
- Create Spring Controller
package eu.ibacz.sample.portlet.bascispring;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.portlet.bind.annotation.RenderMapping;
/**
* This class is base controller for VIEW mode.
*/
@Controller
@RequestMapping("VIEW")
public class BasicSpringPortletViewController {
@RenderMapping
public String doView() {
return BasicSpringPortletConstants.MAIN_VIEW;
}
}
- Create view - view.jsp
- Add component scan to spring portlet configuration.
<context:component-scan base-package="eu.ibacz.sample.portlet.bascispring.**"/>
- Add a simple form to jsp file.
<%@ page import="eu.ibacz.sample.portlet.bascispring.BasicSpringPortletConstants" %>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="portlet" uri="http://java.sun.com/portlet_2_0" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<c:set var="ns"><portlet:namespace/></c:set>
<spring:message code="basicspring-question"/>
<br/>
<portlet:actionURL var="actionUrl" name="<%=BasicSpringPortletConstants.TEST_ACTION%>"/>
<form action="${actionUrl}" method="POST">
<input type="text" name="${ns}<%=BasicSpringPortletConstants.NAME_PARAM%>">
<input type="submit" value="<spring:message code="basicspring-submit"/>" />
</form>
- Handle the form data in controler.
@ActionMapping(BasicSpringPortletConstants.TEST_ACTION)
public void doAction(@RequestParam(BasicSpringPortletConstants.NAME_PARAM) String name) {
LOG.warn("I've got name " + name);
}
- Add a greetings view.
- Add additional render method to the controller
@RenderMapping(params = PARAM_VIEW + "=" + GREETING)
public String greeting(
@RequestParam(NAME_PARAM) String name,
Model model) {
model.addAttribute(NAME_PARAM,name);
return GREETING_VIEW;
}
@ActionMapping(TEST_ACTION)
public void doAction(ActionRequest request, ActionResponse response) {
LOG.warn("Processing name.");
//order if these lines is important!
response.setRenderParameters(request.getParameterMap());
response.setRenderParameter(PARAM_VIEW, GREETING);
}
- Add greetings.jsp
<%@page contentType="text/html" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="portlet" uri="http://java.sun.com/portlet_2_0" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<spring:message code="basicspring-greeting"/> <b><c:out value="${name}"/></b>!!!
<br/>
<a href="<portlet:renderURL />"><spring:message code="basicspring-back"/> </a>
- Create transfer object PersonPto
public class PersonPto {
private String name;
private DateTime dateOfBirth;
public DateTime getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(DateTime dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "PersonPto{" +
"dateOfBirth=" + dateOfBirth +
", name='" + name + '\'' +
"} " + super.toString();
}
}
- Switch from parameters to model attribute.
@ActionMapping(TEST_ACTION)
public void doAction(
@ModelAttribute(PERSON_PTO) PersonPto personPto,
BindingResult result,
ActionResponse response) {
LOG.warn("Processing person " + personPto);
response.setRenderParameter(PARAM_VIEW, GREETING);
}
- Register InitBinder for joda time.
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(DateTime.class, new JodaDateEditor(DATE_TIME_PATTERN));
}
- Change the simple form to spring form.
<portlet:actionURL var="actionUrl" name="<%=TEST_ACTION%>"/>
<form:form action="${actionUrl}" method="POST" modelAttribute="<%=PERSON_PTO%>">
<p>
<form:label path="name" for="${ns}name"><spring:message code="basicspring-form-name"/></form:label>
<form:input path="name" id="${ns}name"/>
</p>
<p>
<form:label path="dateOfBirth" for="${ns}dateOfBirth"><spring:message code="basicspring-form-date-of-birth"/></form:label>
<form:input path="dateOfBirth" id="${ns}dateOfBirth"/>
</p>
<input type="submit" value="<spring:message code="basicspring-submit"/>" />
</form:form>
- Calculate days till birthday.
private Integer daysToBirthday(DateTime dateOfBirth) {
DateTime now = (new DateTime()).withTimeAtStartOfDay();
int year = now.getYear();
DateTime birthday = dateOfBirth.withYear(year);
if (birthday.isBeforeNow()) {
birthday = birthday.plusYears(1);
}
return Days.daysBetween(now, birthday).getDays();
}
- Add validation
- Create Validator
@Component
public class PersonPtoValidator implements Validator {
private static final DateTime minDate = DateTime.now().minusYears(130);
private static final DateTime maxDate = DateTime.now().plusYears(130);
@Override
public boolean supports(Class<?> clazz) {
return PersonPtoValidator.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "basicspring-err-null-value");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "dateOfBirth", "basicspring-err-null-value");
if (!errors.hasErrors()) {
PersonPto personPto = (PersonPto) target;
if (personPto.getDateOfBirth().isBefore(minDate)) {
errors.rejectValue("dateOfBirth", "basicspring-err-to-early");
}
if (personPto.getDateOfBirth().isAfter(maxDate)) {
errors.rejectValue("dateOfBirth", "basicspring-err-to-late");
}
}
}
}
- Use validator in action.
personPtoValidator.validate(personPto,result);
if (!result.hasErrors()) {
response.setRenderParameter(PARAM_VIEW, GREETING);
}
- Add validation error message placeholders.
<form:errors path="name" element="span" cssClass="${errorClass}"/>