Friday, July 27, 2012

Spring MVC Portlet - Liferay

In this post we are going are develop a multi page portlet using Spring 3.0 Portlet MVC using annotations.

Following are screen shots of various pages -

  1. Home Page: Displays a lists of student with action button to perform edit and delete action on particular student. Add New Student link n the top opens up a new form where we can provide the details for new student and save.

    Home Page



  2. Add New Student: On clicking Add New Student following page will open.


add new student

3. Edit Student Form: Clicking edit from the action menu will display the edit student form.

edit form

  1. Delete Student: Clicking delete from the action menu will display a confirmation alert box and based on your choice will perform.delete studentFollowing are the code listings:


- StudentController.java

[sourcecode language="java"]

@Controller(value="studentController")
@RequestMapping(value = "VIEW")
public class StudentController {

@RenderMapping
public String showStudents(RenderResponse response,Model model) {
System.out.println("Render Called");
List students = null;
try {
students = StudentLocalServiceUtil.getStudents(-1, -1);
} catch (SystemException e) {
System.out.println("Error in getting student list");
e.printStackTrace();
}
model.addAttribute("students", students) ;
return "myView";
}

@RenderMapping(params = "myaction=addStudentForm")
public String showAddStudentForm(Model model) throws SystemException
{
System.out.println("showAddStudentForm Called !!!!!!!!!!!");
Student student = new StudentImpl();
model.addAttribute("student", student);
return "addStudent";
}

@ActionMapping(params = "myaction=addStudent")
public void addStudent(@ModelAttribute("student") StudentImpl student,BindingResult bindingResult, ActionRequest actionRequest, ActionResponse actionResponse)
throws Exception {

student.setPrimaryKey(CounterLocalServiceUtil.increment(Student.class.getName()));
Student newStudent = StudentLocalServiceUtil.addStudent(student);
System.out.println("Student added : " + newStudent.getName());
}

@RenderMapping(params = "myaction=editStudentForm")
public String editStudentForm(@RequestParam Long resourcePrimKey,Model model) throws PortalException, SystemException
{
System.out.println("Edit Student Form Called !!" + resourcePrimKey);
Student student = StudentLocalServiceUtil.getStudent(resourcePrimKey);
model.addAttribute("student", student);
return "editStudent";
}

@ActionMapping(params = "myaction=editStudent")
public void editStudent(@ModelAttribute StudentImpl student,@RequestParam Long resourcePrimKey,ActionResponse response) throws IOException
{
student.setId(resourcePrimKey);
System.out.println("Edit Student Called!!" + student.getId());
try {
StudentLocalServiceUtil.updateStudent(student);
} catch (SystemException e) {
System.out.println("Error Occured while updating!! ");
e.printStackTrace();
}
}

@ActionMapping(params = "myaction=deleteStudent")
public void deleteStudent(ActionRequest request,ActionResponse response){
long primKey = ParamUtil.getLong(request, "resourcePrimKey");
System.out.println("Delete Called for : " + primKey);
try {
StudentLocalServiceUtil.deleteStudent(primKey);
} catch (PortalException e) {
System.out.println("Error Occured while deleting");
e.printStackTrace();
} catch (SystemException e) {
System.out.println("Error Occured whlile deleting");
e.printStackTrace();
}
}
}

[/sourcecode]

- myView.jsp

[sourcecode language="java"]

<portlet:renderURL var="addStudentJSP">
<portlet:param name="myaction" value="addStudentForm"></portlet:param>
</portlet:renderURL>
<% PortletURL iteratorURL = renderResponse.createRenderURL(); %>

<a href="<%=addStudentJSP%>">Add New Student</a>

<br/><br/>

<liferay-ui:search-container delta="5" emptyResultsMessage="No Students were found!!" iteratorURL="<%=iteratorURL%>">
<liferay-ui:search-container-results results="<%=ListUtil.subList((List<Student>)request.getAttribute(\"students\"), searchContainer.getStart(), searchContainer.getEnd())%>" total="${students.size()}" />
<liferay-ui:search-container-row className="com.test.model.Student" keyProperty="id" modelVar="student">
<liferay-ui:search-container-column-text name="name" value="${student.name}" />
<liferay-ui:search-container-column-text name="subject" value="${student.subject}" />
<liferay-ui:search-container-column-jsp path="/WEB-INF/jsp/studentActions.jsp" align="right" />
</liferay-ui:search-container-row>
<liferay-ui:search-iterator />
</liferay-ui:search-container>

[/sourcecode]

- addStudent.jsp

[sourcecode language="java"]

<portlet:actionURL var="AddStudentURL">
<portlet:param name="myaction" value="addStudent"></portlet:param>
</portlet:actionURL>

<form:form action="<%=AddStudentURL.toString()%>" method="post" commandName="student">
<table>
<tr><td>Name :</td> <td><form:input path="name" /></td></tr>
<tr><td>Subject:</td> <td><form:input path="subject" /></td></tr>
<tr><td colspan="2"><input type="submit" value="Save" /></td></tr>
</table>
</form:form>

[/sourcecode]


- editStudent.jsp
[sourcecode language="java"]

<h2>Edit Student</h2>

<portlet:actionURL var="EditStudentURL">
<portlet:param name="myaction" value="editStudent" />
<portlet:param name="resourcePrimKey" value="${student.primaryKey}" />
</portlet:actionURL>

<form:form action="<%=EditStudentURL.toString()%>" method="post" commandName="student">
<table>
<tr><td>Name :</td> <td><form:input path="name" /></td></tr>
<tr><td>Subject:</td> <td><form:input path="subject" /></td></tr>
<tr><td colspan="2"><input type="submit" value="Save" /></td></tr>
</table>
</form:form>
[/sourcecode]

- studentActions.jsp

[sourcecode language="java"]

<%
ResultRow row = (ResultRow) request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);
Student myStudent = (Student) row.getObject();
String primKey = String.valueOf(myStudent.getPrimaryKey());
%>

<liferay-ui:icon-menu>
<liferay-portlet:renderURL var="editURL">
<portlet:param name="myaction" value="editStudentForm" />
<portlet:param name="resourcePrimKey" value="<%= primKey %>" />
</liferay-portlet:renderURL>
<liferay-ui:icon image="edit" message="Edit" url="<%= editURL.toString() %>" />

<portlet:actionURL var="deleteURL">
<portlet:param name="myaction" value="deleteStudent" />
<portlet:param name="resourcePrimKey" value="<%= primKey %>" />
</portlet:actionURL>
<liferay-ui:icon-delete url="<%= deleteURL.toString() %>" />
</liferay-ui:icon-menu>
[/sourcecode]

- Portlet Deployment Descriptor (portlet.xml)

[sourcecode language="java"]

<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0">

<portlet>
<portlet-name>springdemo</portlet-name>
<display-name>SpringDemo</display-name>
<portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
<init-param>
<name>contextConfigLocation</name>
<value>/WEB-INF/context/portlet/myContext.xml</value>
</init-param>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
</supports>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<portlet-info>
<title>Spring Demo</title>
<short-title>Spring Demo</short-title>
<keywords></keywords>
</portlet-info>
<security-role-ref>
<role-name>administrator</role-name>
</security-role-ref>
<security-role-ref>
<role-name>guest</role-name>
</security-role-ref>
<security-role-ref>
<role-name>power-user</role-name>
</security-role-ref>
<security-role-ref>
<role-name>user</role-name>
</security-role-ref>
</portlet>
</portlet-app>
[/sourcecode]

- Portlet Web Application Context (myContext.xml)

[sourcecode language="java"]

<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:component-scan base-package="com.test" />

<!-- <bean id="studentController"
class="com.test.controller.StudentController"/>

<bean id="portletModeHandlerMapping"
class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
<property name="portletModeMap">
<map>
<entry key="view">
<ref bean="studentController" />
</entry>
</map>
</property>
</bean>
-->

</beans>

[/sourcecode]

- Root Web Application Context (applicationContext.xml)

[sourcecode language="java"]

<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>

</beans>

[/sourcecode]

- Project Structure

project-structure

 

Build and Deploy it like any other liferay portlet.

Please comment if you have any questions or suggestions !!!

 

 

Tuesday, July 24, 2012

Liferay Custom Portlet Mode

 

The PortletMode class defines three constants—VIEW, EDIT, and HELP—corresponding
to the VIEW, EDIT, and HELP portlet modes. A portal may define support for additional portlet modes supported by the portal server or by the portlet.

 

Portal Managed Mode - If the portal server is responsible for managing the portlet mode, the portlet mode is referred to as portal-managed. Liferay provides config, about, print, preview and edit_defaults custom portlet modes.

Portlet Managed Mode - If the portlet is responsible for managing the portlet mode, it’s referred to as portlet-managed..

 

Implementing the Portal-managed Custom Portlet mode:

 

Here are the steps to implement print custom portlet mode provided by liferay

 

1. Define support in the portlet.xml -

We must specify the custom portlet mode in the portlet deployment descriptor using the <portlet-mode> subelement of the <supports> element

<portlet-mode>print</portlet-mode>

 

2. A custom portlet mode must also be defined at the portlet application level. The <custom-portlet-mode> subelement of <portlet-app> specifies the custom portlet modes that are available to portlets in the portlet application

<custom-portlet-mode>

<portlet-mode>print</portlet-mode>


</custom-portlet-mode>

 

3. Setting portlet mode in the URL

PortletURL printModeUrl = renderResponse.createRenderURL();
if(renderRequest.isPortletModeAllowed(new PortletMode("print"))) {
printModeUrl.setPortletMode(new PortletMode("print"));
}
request.setAttribute("printModeUrl", printModeUrl);

 

NOTE: if(renderRequest.isPortletModeAllowed(new PortletMode("print")))

above condition checks whether liferay provides support for print custom portlet mode. The PortalContext’s getSupportedPortletModes() method returns the list of portal-managed portlet modes.

 

4. Add link to switch to print mode

<href="<%=printModeUrl.toString() %>">Print Mode</a>


 

5. Implementing the custom portlet mode behavior -

In the portlet class override the doPrint method:

@Override
public void doPrint(RenderRequest renderRequest,
RenderResponse renderResponse) throws IOException, PortletException {
System.out.println("Print Mode Called");
super.doPrint(renderRequest, renderResponse);
}

 

**Liferay doesnt provide support for portlet-managed custom portlet modes.

 

 

Friday, June 15, 2012

Liferay Ehcache Configuration : Distributed Caching in Liferay

Liferay Portal uses Ehcache to support distributed caching.


1. Enable distributed caching

To enable the distributed caching in liferay portal just set the following property in the portal-ext.properties file.

cluster.link.enabled=true

2. Default Hibernate cache settings

In Liferay Portal, hibernate is configured to use the Ehcache and default caching configurations are specified in the hibernate-clustered.xml file.

Following configuration is used by default for all the Liferay entities.



3. Customizing Hibernate Cache Settings

Let say we want to customize the hibernate cache settings for MBMessage Entity. Following are the steps to achieve our goal -

create a new folder say myEhcache in the [Tomcat Home]/webapps/ROOT /WEB-INF/classes/ folder and copy the default hibernate-clustered.xml file in the myEhcache folder.

Edit the hibernate-clustered.xml file and an entry for MBMessage Entity



4. Add an entry to portal-ext.properties file to provide the path of your custom hibernate-clustered.xml file

    net.sf.ehcache.configurationResourceName=/myEhcache/hibernate-clustered.xml

Restart the server.

5. Viewing Cache Configuration in jconsole -

jconsole can be used to view the current cache settings applied. Following is screenshot displaying the cache configuration for MBMessageImpl class:


As we customized the Hibernate cache settings, Cache settings for the clustered environment can also be configured via following files -

1.  liferay-single-vm.xml

2. liferay-multi-vm-clustered.xml


Thursday, June 14, 2012

Liferay Search Container : Orderable Columns

In this post we are going to implement the orderable columns for search container.

Following are the steps -

1. Put the following code in your jsp which is used to render the search container.


This If condition is provided to handle the default case. Here defaultColumn and defaultOrder can be replaced with the desired column and order respectively. Here we are sorting the results on the title column.

2.  Here is the TitleComparator.java code -



3.  add the following code to your controller's render method :



4.  Here is the search container part -




Now Title column in the search container will be rendered as clickable and upon clicking will sort the title column data.

Thursday, July 21, 2011

Localization Specific Logo Image : Liferay Portal

Often there are requirements to show customized logo image according to portal's current locale. In Liferay Portal there is no way achieve this but locale specific logo can applied by using the theme.

In theme, portal_normal.vm contains the following statement to display logo -

<a href="$company_url" title="#language("go-to") $company_name">
<span>$company_name</span>
</a>

Here you can place check for the locale and accordingly change the logo image -

<a href="$company_url" title="#language("go-to") $company_name">
#if($locale == "en_US")
<img src="englishlogoimage" />
#else
<img src="spanishlogoimage" />
#end
</a>

but now you can't manage the logo from the community settings as image urls are hardcoded in the theme. To make it manageable you can use web content to render the logo image.

Any comments or suggestions will be appreciated ....

 

Saturday, July 9, 2011

Community Specific Locale Liferay Portal (Per Community BasisLocalization)

Liferay provides us with fine grained built-in localization features. In Liferay Portal one can apply localization on per user basis. But how to apply default locale to a Community, Liferay doesn’t provide any such functionality but it can be implemented using Portal Properties Hook.

So case is, portal instance have more than one Community and we need to apply a default language setting for one Community that is different from other Communities. To achieve this implement an Event Hook for servlet.service.events.pre.

Following is the step by step description to achieve this functionality-

1.  Create a portal properties hook and choose the servlet.service.events.pre Event

2.  Create a new class say SampleClass by extending the com.liferay.portal.kernel.events.Action


3.  Now en_US locale will be applied to LONG_GROUP_ID Community.

Any queries or comments.........