The Spring Web model-view-controller (MVC) framework is designed around a DispatcherServlet that dispatches requests to handlers, with configurable handler mappings, view resolution etc. The default handler is based on the @Controller and@RequestMapping annotations, offering a wide range of flexible handling methods.

 

In Liferay we can develop custom portlet based on Spring MVC framework by implementing Model, view & controller.

 In Spring MVC, there are 3 things: -  M-model , V-view , C-Controller.

 

 Spring MVC

 

  • Front Controller will be Dispatcher Portlet instead of Dispatcher Servlet will handle the incoming request and dispatch to related handler (Controller).
  • Handler (Controller) will process the request and send back the data in form of M(Model) back to Front Controller
  • Then Front Controller will select particular view with the help of View Resolver and send response back to client.

 

Please check below the package structure in mvc portlet :-

 

 

1) Controller class should be in riiteshXXX-portlet\docroot\WEB-INF\src

                      for ex:- RiiteshXXXController.java

 

2) Repository - It will have DAO interface & DAO implementation class.

                     riiteshXXX-portlet\docroot\WEB-INF\src\com\riitesh\mylink\riiteshXXX\repository

                     RiiteshXXXDAO.java

                     RiiteshDAOImpl.java

 

3) Model - This folder will have the bean with setter & getter properties.

                      riiteshXXX-portlet\docroot\WEB-INF\src\com\riitesh\mylink\riiteshXXX\model

               The model class should import persistence package and should have done through annotation.

              

               For Ex:-

               @Id
               private Long id;
               @Column
               private String name;
               @Column
               private String lastname;

 

4) portlet/docroot/Web-inf

liferay-plugin-package.properties :- mention the list of jars files and keep this jar files inside the root/web-inf/lib folder

    riiteshXXX-portlet.xml - define datasource or jndi lookup along with the session factory settings

 

Below is riiteshXXX-portlet.xml :-

 

  <bean id="sessionFactory"
  class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
  <property name="dataSource" ref="dataSource"/>
  
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.bytecode.use_reflection_optimizer">${hibernate.bytecode.use_reflection_optimizer}</prop>
    <prop key="hibernate.dialect">${hibernate.dialect}</prop>
    <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
    <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
    <prop key="hibernate.query.factory_class">${hibernate.query.factory_class}</prop>
    <prop key="hibernate.cache.use_second_level_cache">true</prop>
   </props>
  </property>
  
 </bean>
 
 <tx:annotation-driven transaction-manager="txManager" />

 <bean id="txManager"
  class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
 </bean>

 

 

 

 

Below is the sample implementation of Spring MVC framework for Bookmark Portlet to provide the reference.

Configuration

  1. web.xml

◦   Configured Spring render servlet

                <!-- Servlets -->  

                <servlet>

                                    <servlet-name>ViewRendererServlet</servlet-name>

                 <servlet-class>

            org.springframework.web.servlet.ViewRendererServlet

             </servlet-class>     

                 </servlet>

                <servlet-mapping>

                <servlet-name>ViewRendererServlet</servlet-name>

                <url-pattern>/WEB-INF/servlet/view</url-pattern>

                 </servlet-mapping>

  1. portlet.xml

◦   Configured portlet class as below

                <!-- Servlets -->  

                <portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>

  1. liferay-plugin-package.properties

◦   Configured portlet dependencies jars as below

 

portal-dependency-jars=\  hibernate3.jar,\spring-aop.jar,\spring-asm.jar,\spring-aspects.jar,\ spring-beans.jar,\ spring-context-support.jar,\spring-context.jar,\spring-core.jar,\spring-expression.jar,\spring-jdbc.jar,\spring-jms.jar,\spring-orm.jar,\spring-oxm.jar,\spring-transaction.jar,\spring-web-portlet.jar,\spring-web-servlet.jar,\spring-web-struts.jar,\spring-web.jar,\log4j-extras.jar,\log4j.jar,\cglib.jar,\dom4j.jar,\slf4j-api.jar,\commons-collections.jar,\javassist.jar,\antlr3-runtime.jar,\antlr3.jar,\antlr2.jar,\jstl-api.jar,\jstl-impl.jar,\commons-dbcp.jar,\commons-pool.jar,aopalliance.jar

  1. Spring Configuration File (riiteshbookmarkportlet-portlet.xml)

◦   Configure server data source in below line

                                <jee:jndi-lookup jndi-name="java:comp/env/jdbc/<DATASOURCE_NAME>"                                      id="liferayDataSource" />

◦   Configured Sessionfactory,transaction manager and annotation classes as below:

 

                <context:component-scan base-package="com.riitesh.mylink.portlet.bookmark" />

                <mvc:annotation-driven />

                <tx:annotation-driven transaction-manager="txManager" />

                <bean id="txManager"

                class="org.springframework.orm.hibernate3.HibernateTransactionManager">

                <property name="sessionFactory" ref="sessionFactory" />

                </bean>

                <context:annotation-config />

 

                <bean id="sessionFactory"

                                class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryB              ean">

                                <property name="dataSource" ref="liferayDataSource"></property>

                                <property name="annotatedClasses">

                                <list>                                                     <value>com.riitesh.mylink.portlet.bookmark.model.Bookmark</value>

                                                </list>

                                </property>

                                <property name="hibernateProperties">

                                                <props>

                                                                <prop key="hibernate.bytecode.use_reflection_optimizer">$                                                                                    {hibernate.bytecode.use_reflection_optimizer}</prop>

                                                                <prop key="hibernate.cache.use_second_level_cache">true</prop>

                                                                <prop key="hibernate.dialect">${hibernate.dialect}</prop>

                                                                <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>

                                                                <prop key="hibernate.hbm2ddl.auto">update</prop>

                                                                <prop key="hibernate.query.factory_class">$                                                                                                                                   {hibernate.query.factory_class}</prop>

                                                                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>

                                                </props>

                                </property>

                </bean>             

◦   Configured view resolver and message property configuration as below:

                <bean id="viewResolver"

                                class="org.springframework.web.servlet.view.InternalResourceViewResolver">

                                <property name="viewClass"

                                                value="org.springframework.web.servlet.view.JstlView" />

                                <property name="prefix" value="/html/riiteshbookmarkportlet/" />

                                <property name="suffix" value=".jsp" />

                                </bean>

 

                <bean id="messageSource"

                                class="org.springframework.context.support.ResourceBundleMessageSource">

                                <property name="basenames">

                                                <list>

                                                                <value>content.Language</value>

                                                </list>

                                </property>

                </bean>

Below is the sample implementation:-

ñ  Model Class

◦   Class - Bookmark.java

                   Package – com.riitesh.mylink.portlet.bookmark.model

                Note: Database table name will be riitesh_bookmark

ñ  DAO Classes

◦   Class - BookmarkDAOImpl.java

                       Package – com.riitesh.mylink.portlet.bookmark.repository

 

ñ  Service Classes

◦   Class - BookmarkServiceImpl.java

                       Package – com.riitesh.mylink.portlet.bookmark.service

 

ñ  Controller Classes

◦   Class - RIITESHBookmarkController.java

                       Package – com.riitesh.mylink.portlet.bookmark.controller

◦   Three different actions for bookmark in controller as below (All are calling from the JSP)

                @ActionMapping(params="myAction=addBookmark")

                @ActionMapping(params="myAction=deleteBookmark")

                @ActionMapping(params="myAction=editBookmark")

 

◦   One default request mapping for render view

                @RequestMapping

                                public ModelAndView handleRenderRequest(RenderRequest renderReq,

                                                RenderResponse renderRes) throws Exception

ñ  JSP

◦   Name -  view.jsp

               Path –   docroot\html\riiteshbookmarkportlet\view.jsp

ñ  JSP contains UI and actions URLS for add, edit and delete bookmarks

ñ  AUI form is used for add current URL as bookmark in system

ñ  Search container is used for showing list of bookmarks.

 

Exception Handling

In Spring MVC portlet we can handle the exception by below mentioned 2 ways: -

  1. @Exception Handler: - Form level exception handling
  2. SimpleMappingExceptionResolver: -General System exception handling
    1. Add the following entry in Spring portlet context file

<bean             class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">   <property name="exceptionMappings">

   <entry key="com.riitesh.MyAppCheckedException" value="app-checked-error" />   </property>

      <property name="riiteshError" value="general-error" /> 

      </bean>

(This define if the exceptionreceive by an controller then which view to be displayed to the user) 

  1. Create your custom error.jsp to display exception and also we can display the custom message in localized content by adding resource bundle (.properties files)
  2. Create custom controller for exception handling and add the code below in the controller: -

@ExceptionHandler(Exception.class) 

   public String handleException (Exception ex, RenderRequest request) { 

    request.setAttribute("exception", ex); 

    return "general-error"; 

   } 

Ideally  there should be one common controller in project but if we have several controllers in your Liferay plugin, then put this method in an abstract superclass and have all your controllers that use this error view extend the superclass.