Using Spring

Developing a PortletMVC4Spring Portlet

PortletMVC4Spring compliments the Spring Web framework and MVC design pattern by providing annotations that map portlet requests to Controller classes and methods. This sample portlet application uses PortletMVC4Spring, Spring, and JSP/JSPX templates to show you how it works.

The archetype's sample portlet prints a greeting (e.g., Hello, Joe Bloggs) on submitting a first and last name.

Follow these steps to create and deploy your portlet application:

  1. Create a PortletMVC4Spring project in your Liferay Workspace:

    blade create B7F9Portlet -t spring-mvc-portlet --framework portletmvc4spring --view-type jsp
    
  2. Create your Model class(es) in a package for models (e.g., java/[my-package-path]/dto). The sample Model class is User:

      public class User implements Serializable {
    
        public String getFirstName() {
          return firstName;
        }
    
        public String getLastName() {
          return lastName;
        }
    
        public void setFirstName(String firstName) {
          this.firstName = firstName;
        }
    
        public void setLastName(String lastName) {
          this.lastName = lastName;
        }
    
        private static final long serialVersionUID = 1113488483222411111L;
    
        @NotBlank
        private String firstName;
    
        @NotBlank
        private String lastName;
    
      }
    
  3. Create your View using a Spring Web-supported template type. If you didn’t generate your project with Blade, specify a View resolver for template type in your spring-context/portlet-application-context.xml portlet application context. (See PortletMVC4Spring Configuration Files for details).

    The sample user.jspx template renders a form for submitting a user’s first and last name.

    <?xml version="1.0" encoding="UTF-8"?>
    <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
    		  xmlns:portlet="http://xmlns.jcp.org/portlet_3_0"
    		  xmlns:spring="http://www.springframework.org/tags"
    		  xmlns:form="http://www.springframework.org/tags/form"
    		  version="2.1">
    	<jsp:directive.page contentType="text/html" pageEncoding="UTF-8" />
    	<portlet:defineObjects/>
    	<link href="${contextPath}/resources/css/main.css" rel="stylesheet" type="text/css"/>
    	<portlet:actionURL var="mainFormActionURL"/>
    	<form:form id="${namespace}mainForm" action="${mainFormActionURL}" class="user-form" method="post" modelAttribute="user">
    		<p class="caption">
    			<spring:message code="personal-information" />
    		</p>
    		<fieldset>
    			<div class="form-group">
    				<form:label for="${namespace}firstName" path="firstName">
    					<spring:message code="first-name" />
    				</form:label>
    				<form:input id="${namespace}firstName" cssClass="form-control" path="firstName"/>
    				<form:errors path="firstName" cssClass="portlet-msg-error"/>
    			</div>
    			<div class="form-group">
    				<form:label for="${namespace}lastName" path="lastName">
    					<spring:message code="last-name" />
    				</form:label>
    				<form:input id="${namespace}lastName" cssClass="form-control" path="lastName"/>
    				<form:errors path="lastName" cssClass="portlet-msg-error"/>
    			</div>
    		</fieldset>
    		<hr />
    		<spring:message code="submit" var="submit" />
    		<input class="btn btn-primary" value="${submit}" type="submit"/>
    	</form:form>
    </jsp:root>
    

    To invoke actions in your Controller, associate action URLs with your templates. The sample template associates the action URL variable mainFormActionURL with its form element.

    <portlet:actionURL var="mainFormActionURL"/>
    <form:form id="${namespace}mainForm" action="${mainFormActionURL}" class="user-form" method="post" modelAttribute="user">
        ...
    

    A <form:form/> element’s modelAttribute attribute targets an application Model. The sample template targets the application’s user Model.

  4. Style your portlet by adding CSS to a stylesheet (e.g., webapp/css/main.scss) and linking your template to it.

    <link href="${contextPath}/resources/css/main.css" rel="stylesheet" type="text/css"/>
    
  5. Define your portlet’s messages in a properties file (e.g., src/main/resources/content/Language.properties). The sample user.jspx template references some of these properties:

    first-name=First Name
    greetings=Greetings, {0} {1}!
    jakarta.portlet.display-name=B7F9Portlet
    jakarta.portlet.keywords=B7F9Portlet
    jakarta.portlet.short-title=B7F9Portlet
    jakarta.portlet.title=B7F9Portlet
    last-name=Last Name
    personal-information=Personal Information
    please-correct-the-following-errors=Please correct the following error(s)
    submit=Submit
    todays-date-is=Today''s date is {0}
    
  6. Create a Controller class to handle portlet requests. Here’s an example:

    @Controller
    @RequestMapping("VIEW")
    public class UserController {
        ...
    }
    

    The @Controller annotation applies the Spring Controller component stereotype. The Spring Framework scans Controller classes for Controller annotations.

    The @RequestMapping("VIEW") annotation marks the class’s public methods as request handler methods for the portlet’s VIEW mode.

  7. In your Controller, apply @RenderMapping annotations to methods for handling portlet render requests. Import the annotation com.liferay.portletmvc4spring.bind.annotation.RenderMapping and make sure each handler method returns a string that matches the name of a template to render. Here are the sample’s render request handler methods:

    @RenderMapping
    public String prepareView() {
    	  return "user";
    	 }
    
    	@RenderMapping(params = "jakarta.portlet.action=success")
    	public String showGreeting(ModelMap modelMap) {
    
    		DateFormat dateFormat = new SimpleDateFormat("EEEE, MMMM d, yyyy G");
    
    		Calendar todayCalendar = Calendar.getInstance();
    
    		modelMap.put("todaysDate", dateFormat.format(todayCalendar.getTime()));
    
    		return "greeting";
    	}
    

    The @RenderMapping annotation invokes the prepareView method above if no other handler methods match the request. prepareView renders the user template (i.e., user.jspx).

    The @RenderMapping(params = "jakarta.portlet.action=success") annotation invokes the showGreeting method if the render request has the parameter setting jakarta.portlet.action=success. showGreeting renders the greeting template (i.e., greeting.jspx).

  8. In your Controller, apply @ActionMapping annotations to your portlet action request handling methods. Import the annotation com.liferay.portletmvc4spring.bind.annotation.ActionMapping.

    The sample Controller’s action handler method below is annotated with @ActionMapping, making it the default action handler if no other action handlers match the request. Since this portlet only has one action handler, the submitApplicant method handles all of the portlet’s action requests.

    @ActionMapping
    public void submitApplicant(
      @ModelAttribute("user") User user, BindingResult bindingResult,
      ModelMap modelMap, Locale locale, ActionResponse actionResponse,
      SessionStatus sessionStatus) {
    
      _localValidatorFactoryBean.validate(user, bindingResult);
    
      if (!bindingResult.hasErrors()) {
        if (_logger.isDebugEnabled()) {
          _logger.debug("firstName=" + user.getFirstName());
          _logger.debug("lastName=" + user.getLastName());
        }
    
        MutableRenderParameters mutableRenderParameters =
          actionResponse.getRenderParameters();
    
        mutableRenderParameters.setValue("jakarta.portlet.action", "success");
    
        sessionStatus.setComplete();
      }
      else {
        bindingResult.addError(
          new ObjectError(
            "user",
            _messageSource.getMessage(
              "please-correct-the-following-errors", null, locale)));
      }
    }
    

    The @ModelAttribute annotation in method parameter @ModelAttribute("user") User user associates the View’s user Model (comprising a first name and last name) to the User object passed to this method.

    Note, the submitApplicant method sets the jakarta.portlet.action render parameter to success. The previous render handler method showGreeting matches this request parameter.

  9. Configure additional resources and beans in the project’s descriptors and Spring context files respectively. (See PortletMVC4Spring Configuration Files for details).

  10. Build the project WAR using Gradle.

  11. Deploy the WAR by copying it to your [Liferay-Home]/deploy folder.

Liferay logs the deployment and the portlet appears in the Liferay UI. Find your portlet by selecting Add (Add) and navigating to Widgets and the category you specified to the Liferay Bundle Generator (Sample is the default category).

Congratulations! You created and deployed a PortletMVC4Spring Portlet.

PortletMVC4Spring Project Anatomy

PortletMVC4Spring Annotations

PortletMVC4Spring Configuration Files

Migrating to PortletMVC4Spring