In This article, I will show step by step how to write Spring MVC Portlet in Liferay.
Download source code
Download Spring MVC Portlet
Spring framework is well known framework and provide lots of features and functionality. These features and functionality are organized in modular fashion. Spring MVC is part of Web module of Spring framework.
Spring also support its counter part Spring MVC for portlet. Portlet MVC framework is mirror image of Web MVC framework in Spring. There are little different in Spring MVC Portlet framework.
We will first get brief introduction about Spring MVC (Web MVC) framework and then we see how to write Spring MVC Portlet in Liferay.
In Spring MVC, there are 3 things. 1) M-model 2)V-view 3)C-Controller. Following is the core architecture of Spring (Web) MVC framework.
Download Spring MVC Portlet
Spring framework is well known framework and provide lots of features and functionality. These features and functionality are organized in modular fashion. Spring MVC is part of Web module of Spring framework.
Spring also support its counter part Spring MVC for portlet. Portlet MVC framework is mirror image of Web MVC framework in Spring. There are little different in Spring MVC Portlet framework.
We will first get brief introduction about Spring MVC (Web MVC) framework and then we see how to write Spring MVC Portlet in Liferay.
In Spring MVC, there are 3 things. 1) M-model 2)V-view 3)C-Controller. Following is the core architecture of Spring (Web) MVC framework.
· You can see that, Front Controller works as C(Controller).
· which will take 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.
For detail information about the Spring (WEB) MVC framework you can refer the following link.
In Spring MVC Portlet framework, the Front Controller will be DispatcherPortlet instead ofDispatcherServlet.
Now let us start work for Spring MVC portlet in liferay. Below are the steps to create Spring MVC portlet.
STEP 1 :- CREATING PORTLET PROJECT SKELETON BY CREATING LIFERAY PORTLET
STEP 1 :- CREATING PORTLET PROJECT SKELETON BY CREATING LIFERAY PORTLET
We will first create the skeleton of portlet project by creating Liferay MVC portlet. Then we will do changes to migrate it into Spring MVC Portlet.
Please refer my previous blog Create Liferay Portlet and create Liferay MVC portlet. Give project name as "first-spring-mvc"(and "-portlet" will be appended by wizard. so don't give it explicitly in project name).
After successfully creating Spring MVC portlet, the project structure will look like as per below screenshot.
STEP 2 :- DEFINING PORTLET CLASS FOR SPRING MVC
Open portlet.xml file under WEB-INF folder. It will look like below snippet.
1. <portlet>
2. <portlet-name>first-spring-mvc</portlet-name>
3. <display-name>First Spring Mvc</display-name>
4. <portlet-class>com.liferay.util.bridges.mvc.MVCPortlet</portlet-class>
5. <init-param>
6. <name>view-jsp</name>
7. <value>/view.jsp</value>
8. </init-param>
9. <expiration-cache>0</expiration-cache>
10. <supports>
11. <mime-type>text/html</mime-type>
12. </supports>
13. <portlet-info>
14. <title>First Spring Mvc</title>
15. <short-title>First Spring Mvc</short-title>
16. <keywords>First Spring Mvc</keywords>
17. </portlet-info>
18. <security-role-ref>
19. <role-name>administrator</role-name>
20. </security-role-ref>
21. <security-role-ref>
22. <role-name>guest</role-name>
23. </security-role-ref>
24. <security-role-ref>
25. <role-name>power-user</role-name>
26. </security-role-ref>
27. <security-role-ref>
28. <role-name>user</role-name>
29. </security-role-ref>
30. </portlet>
Now our portlet class will be DispatcherPortlet (provided by Spring Framework). replace
com.liferay.util.bridges.mvc.MVCPortlet with org.springframework.web.portlet.DispatcherPortlet so that it will look like as per below snippet
1. <portlet-name>first-spring-mvc</portlet-name>
2. <display-name>First Spring Mvc</display-name>
3. <portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
I have just shown first few lines of portlet.xml file to show the change (in <portlet-class>). Rest content of portlet.xml will remain as it is.
After doing this change,you may get error like The portlet class org.springframework.web.portlet.DispatcherPortlet was not found on the Java Build Path inportlet.xml file.
This is because the class DispatcherPortlet is not yet present in the class path. I will show how to resolve this in STEP 5.
STEP 3 :- CREATING SPRING APPLICATION CONTEXT FILE
Next is to define the Spring application context file. Any spring project must have atleast one Spring application context (xml) file where all beans are defined.
We will create one xml file just under docroot/WEB-INF folder and name it first-spring-mvc-portlet.xml.The file name is not just co-incident the same name as portlet. We must have to follow the following pattern for Spring application context file name.
<<PORTLET_NAME>>-portlet.xml
· Where PORTLET_NAME is the value of <portlet-name> element in portlet.xml file.
· Make sure that all character should be in lower case in application context file name.
· For Example, if value of <portlet-name> in portlet.xml file is SampleInput then the Spring application context file must be sampleinput-portlet.xml
Add the following content in it.
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
4. xmlns:context="http://www.springframework.org/schema/context"
5. xmlns:aop="http://www.springframework.org/schema/aop"
6. xsi:schemaLocation="
7. http://www.springframework.org/schema/beans
8. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
9. http://www.springframework.org/schema/context
10. http://www.springframework.org/schema/context/spring-context-3.0.xsd
11. http://www.springframework.org/schema/aop
12. http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
13. <context:annotation-config />
14. <bean
15. class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
16. </beans>
Explanation:-
· It defines parent element <beans>
· We have defined two elements <context:annotation-config> and <bean class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping" />. This both entries are required to execute annotation that we will use in controller class in STEP 9 in this blog.
STEP 4 :- POINTING SPRING APPLICATION CONTEXT FILE TO PORTLET CLASS.
We had defined DisptacherPortlet class as portlet class in portlet.xml file. Its our central(Front) controller. We have to tell it where we have defined our application context file.
We will point it through init param in portlet.xml file. param name will be "contextConfigLocation" and its value will the location of spring application context file.
After adding this, the portlet.xml file will look like below snippet (I am only showing the code which I have changed)
After adding this, the portlet.xml file will look like below snippet (I am only showing the code which I have changed)
1. <portlet-name>first-spring-mvc</portlet-name>
2. <display-name>First Spring Mvc</display-name>
3. <portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
4. <init-param>
5. <name>contextConfigLocation</name>
6. <value>/WEB-INF/first-spring-mvc-portlet.xml</value>
7. </init-param>
8. <init-param>
9. <name>view-jsp</name>
10. <value>/view.jsp</value>
11. </init-param>
Note:-
· You are free to define Spring application context file in any folder under WEB-INF folder. You have to just mentioned the location accordingly in <value> element under <init-param>element in portlet.xml file.
· For example if you created folder name context just under WEB-INF and placed spring application context file in it then you have to give the path as /WEB-INF/context/first-spring-mvc-portlet.xml in <value> element under <init-param>
STEP 5 :- PUTTING SPRING JARS INTO CLASS PATH.
Next step is to put Spring dependencies (JARs) into class path. For this you NO need to search JARs explicitly. Just follow below steps to add these dependencies.
open file liferay-plugin-package.properties resides just under /WEB-INF folder. It should looks like below snippet.
Next step is to put Spring dependencies (JARs) into class path. For this you NO need to search JARs explicitly. Just follow below steps to add these dependencies.
open file liferay-plugin-package.properties resides just under /WEB-INF folder. It should looks like below snippet.
1. name=First Spring Mvc
2. module-group-id=liferay
3. module-incremental-version=1
4. tags=
5. short-description=
6. change-log=
7. page-url=http://www.liferay.com
8. author=Liferay, Inc.
9. licenses=LGPL
Now while you have opened it(liferay-plugin-package.properties file ) just open Properties tab as shown in below screenshot.
In Properties tab you will notice Portal Dependency Jars and Portal Dependency Tlds section at right side. On clicking on Add button from Portal Dependency Jars, small window will be opened from where we can select the JAR files as per below screenshot.
select on following JARs and click on OK button.
spring-web-servlet.jar
spring-web-portlet.jar
spring-web.jar
spring-transaction.jar
spring-jdbc.jar
spring-expression.jar
spring-core.jar
spring-context.jar
spring-beans.jar
spring-asm.jar
spring-aop.jar
commons-beanutils.jar
commons-collections.jar
commons-fileupload.jar
commons-io.jar
commons-lang.jar
jstl-api.jar
spring-context-support.jar
jstl-impl.jar
Note:- jstl-api.jar and jstl-impl.jar is not required for spring MVC portlet. But I have just added if we need to use JSTL tag.
The similar way you can also add TLDs.
After adding these JARs, just open the Source tab of liferay-plugin-package.properties file. It will be look like below screenshot.
You will notice that, new property portal-dependency-jars added and its value is comma separated JARs that we added through Properties tab.
Once you become expert, you can add/remove Jars files directly through Source tab.
Till now, we only prepared list of JARs required in classpath. Still these JARs are not present in classpath. We need to build and deploy the portlet so that these JARs are made available in classpath.
To know how this works, first open the Liferay Portlet Plugin API just under the project before deploying portlet. It will looks like below screenshot.
Once you become expert, you can add/remove Jars files directly through Source tab.
Till now, we only prepared list of JARs required in classpath. Still these JARs are not present in classpath. We need to build and deploy the portlet so that these JARs are made available in classpath.
To know how this works, first open the Liferay Portlet Plugin API just under the project before deploying portlet. It will looks like below screenshot.
You can notice that just few JARs are present under it.
Now Just build and deploy the portlet. You can just drag build.xml file to Ant window, expand it and double click on 'deploy' target. You can refer my previous blog Create Liferay Portlet to know how to build and deploy portlet.
Once the portlet is deployed,refresh the project and open the Liferay Portlet Plugin API library again. You will observe that all the Jars that we added in liferay-plugin-package.properties file are added here as shown in below screenshot.
This way, whatever JARs are required, just put in liferay-plugin-package.properties file and deploy the portlet. They will be automatically be placed in class path. If you not find any JAR in this list then you have to explicitly add into lib folder.
You can observe that the error in portlet.xml file came in STEP 2 will be removed. If you still able to see the same error then just do clean and build the project from Eclipse-->Project menu.
STEP 6 :- PUTTING ViewRenderServlet ENTRY IN web.xml FILE.
This is very small but very important steps. Since SpringMVC also support all its functionality in portlet context with the help of this servet. Define this Servlet entry as per below snippet.
1. <servlet>
2. <servlet-name>view-servlet</servlet-name>
3. <servlet-lass>org.springframework.web.servlet.ViewRendererServlet</servlet-class>
4. <load-on-startup>1</load-on-startup>
5. </servlet>
6. <servlet-mapping>
7. <servlet-name>view-servlet</servlet-name>
8. <url-pattern>/WEB-INF/servlet/view</url-pattern>
9. </servlet-mapping>
These are hard-core setting so just put this entry in web.xml as it is.
STEP 7 :- CONFIGURING VIEW
SpringMVC framework support many view technologies (like JSP, Freemarker,Velocity,PDF etc). For simplicity, We will use the jsp.
In Spring framework, DispatcherPortlet will take the help of ViewResolver to choose the view( JSP in our case).
So we need to configure view (through View Resolver) in Spring application context file (first-spring-mvc-portlet.xml ). Add following entry in first-spring-mvc-portlet.xml file
1. <bean id="jspViewResolver"
2. class="org.springframework.web.servlet.view.InternalResourceViewResolver">
3. <property name="viewClass"
4. value="org.springframework.web.servlet.view.InternalResourceView" />
5. <property name="prefix" value="/WEB-INF/jsp/" />
6. <property name="suffix" value=".jsp" />
7. <property name="order" value="1" />
8. </bean>
Explanation:-
· We had defined ViewResolver as bean in application context file
· In prefix property we have to give the folder path where we kept all JSP files.
· in suffix property we have to give as ".jsp".
· For example, suppose we created jsp folder directly under /WEB-INF, then the prefix will be "/WEB-INF/jsp/" and suffix will be ".jsp".
· I will show how this works in STEP 9.
· The Order property is not required if we only have one View Resolver. If we define more than one View Resolver (ex. one is for JSP, another is for Freemarker etc.) then we have to give the order value so that DispatcherPortlet will scan in that order to find the view.
STEP 8 :- CREATE JSP
Now we will create JSP. Create folder jsp under /WEB-INF folder and create one jsp file called defaultRender.jsp and simple write one line in it like "<h1>This is Default Render Jsp</h1>"
STEP 9 :- CREATE REQUEST HANDLER (CONTOLER)
If you observe the very first image in this blog, I have mentioned controller (right side). These controllers will do the actual job. The Front Controller (DispatcherPortlet) will only delegate the request to appropriate request handler (Controller).
So our next step is to write request handler. We will call it Controller,so don't confuse this Controller with the Front Controller (DispatchPortlet).
First create pacakge com.myowncompany.test.springmvc.controller and class MyFirstSpringMVCTestController in it. You are free to choose any package and class name you want. I had put "Controller" at the end of class name to just denote that this is my controller, however its not required but a good practice.
I also give name "controller" to package so that its clearly understand that this is the package where all my controller are reside. Again its not required.
Portlet will have View, Edit and Help mode. In Spring MVC Portlet framework, we can have separate Controller (Request Handler) for each these mode.
We have to explicitly tell DispatcherPortlet that which mode will be supported by Controller. We will do this by giving annotation to MyFirstSpringMVCTestController so that it will looks like below snippet.
Now we will create JSP. Create folder jsp under /WEB-INF folder and create one jsp file called defaultRender.jsp and simple write one line in it like "<h1>This is Default Render Jsp</h1>"
STEP 9 :- CREATE REQUEST HANDLER (CONTOLER)
If you observe the very first image in this blog, I have mentioned controller (right side). These controllers will do the actual job. The Front Controller (DispatcherPortlet) will only delegate the request to appropriate request handler (Controller).
So our next step is to write request handler. We will call it Controller,so don't confuse this Controller with the Front Controller (DispatchPortlet).
First create pacakge com.myowncompany.test.springmvc.controller and class MyFirstSpringMVCTestController in it. You are free to choose any package and class name you want. I had put "Controller" at the end of class name to just denote that this is my controller, however its not required but a good practice.
I also give name "controller" to package so that its clearly understand that this is the package where all my controller are reside. Again its not required.
Portlet will have View, Edit and Help mode. In Spring MVC Portlet framework, we can have separate Controller (Request Handler) for each these mode.
We have to explicitly tell DispatcherPortlet that which mode will be supported by Controller. We will do this by giving annotation to MyFirstSpringMVCTestController so that it will looks like below snippet.
1. package com.myowncompany.test.springmvc.controller;
2.
3. import javax.portlet.RenderRequest;
4. import javax.portlet.RenderResponse;
5.
6. import org.springframework.stereotype.Controller;
7. import org.springframework.ui.Model;
8. import org.springframework.web.bind.annotation.RequestMapping;
9. import org.springframework.web.portlet.bind.annotation.RenderMapping;
10.
11. @Controller(value = "MyFirstSpringMVCTestController")
12. @RequestMapping("VIEW")
13. public class MyFirstSpringMVCTestController {
14.
15.
16. }
Explanation:-
· First we had given annotation @Controller which will used to denote that this is our controller.Value will be same as class name
· Second annotation is @RequestMapping("VIEW") which tell to DispatcherPortlet that this controller will support VIEW mode.
· One controller can support only one portlet mode at a time. So in case if we require EDIT mode, then we have to write separate Controller.
Add following method in MyFirstSpringMVCTestController class
1. @RenderMapping
2. public String handleRenderRequest(RenderRequest request,RenderResponse response,Model model){
3.
4. return "defaultRender";
5. }
Explanation:-
· first we have defined @RenderMapping annotation to this method. This annotation tell that this is default render method. It means whenever we place this portlet, it will render this method. You also can write another render method with "action" as key and its value. When we create RenderURL and passing value which match the "action" value of render method, then it will be called. I will show how to write another render method with "action" as key in next blog.
· The method name is handleRenderRequest. You are free to give any name.
· Its first parameter is RenderRequest and second parameter is RenderResponse. third parameter is object of type Model. We can set attribute in this Model object and can access it in JSP. We will see it in next blog.
· For simplicity there is no any other code and this method is returning just one String "defaultRender". We have to return the name of the JSP at the end of this render method.
In STEP 7 we have defined prefix as /WEB-INF/jsp/ and suffix as ".jsp" in View Resolver. So in this case it will pre-pand "/WEB-INF/jsp/" to defaulRender and append ".jsp". So the final string will be /WEB-INF/jsp/defaultRender.jsp.
· This way DispatcherPortlet will find the jsp path and render it.
STEP 10 :- DEFINE CONTROLLER(REQUEST HANDLER) IN SPRING APPLICATION CONTEXT
The last step is to define Controller in Spring application context file. Open the spring application context file first-spring-mvc-portlet.xml and put the entry for MyFirstSpringMVCTestController class. It will look like as per below snippet.
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
4. xmlns:context="http://www.springframework.org/schema/context"
5. xmlns:util="http://www.springframework.org/schema/util"
6. xsi:schemaLocation="http://www.springframework.org/schema/beans
7. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
8. http://www.springframework.org/schema/context
9. http://www.springframework.org/schema/context/spring-context-3.0.xsd
11. http://www.springframework.org/schema/util/spring-util-3.0.xsd">
12.
13. <context:annotation-config />
14. <bean
15. class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
16.
17. <bean class="com.myowncompany.test.springmvc.controller.MyFirstSpringMVCTestController" />
18.
19. <bean id="jspViewResolver"
20. class="org.springframework.web.servlet.view.InternalResourceViewResolver">
21. <property name="viewClass"
22. value="org.springframework.web.servlet.view.InternalResourceView" />
23. <property name="prefix" value="/WEB-INF/jsp/" />
24. <property name="suffix" value=".jsp" />
25. <property name="order" value="1" />
26. </bean>
27. </beans>
Explanation:-
I have added entry for MyFirstSpringMVCTestController as bean.
And its done. You can now build and deploy this portlet. Place it on Liferay page and you will notice that whatever we have written in defaultRender jsp page will display.
So far I have used just one method (default render) render method. We can add more that one render method ( which will be differentiate by value of "action" key).
We also can add more than one Action method and Resource Method.
Action method will be called when we create url by <portlet:actionURL> and resource method can be called when we create url by <portlet:resourceURL> from JSP.
So far I have used just one method (default render) render method. We can add more that one render method ( which will be differentiate by value of "action" key).
We also can add more than one Action method and Resource Method.
Action method will be called when we create url by <portlet:actionURL> and resource method can be called when we create url by <portlet:resourceURL> from JSP.
This is Spring 2.5. Does it work in Spring 3.0?
ReplyDeleteYa .. if we add latest Spring jars it might work ...
ReplyDeleteExcellent work, thanks for this
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteI am in confuse mode for how can i called controller's another method in portlet?? especially via ajax..
ReplyDeletecan you plz tell me how to call another method normally and with ajax?
i am consfused in passing url..