Wednesday 29 May 2013

Use Create/My Account portlet Liferay 6.1 GA

Introduction:-
To instantiate My account portlet in our portal. There is a bit different approach to follow than in the other version of liferay
(Ref:- http://issues.liferay.com/browse/LPS-33940?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel)

Note:- It is assumed that the system is set up with Liferay 6.1 tomcat bundle and eclipse is configured with the same sdk

Steps:-

1. Create an ext plugin.
    i) In Eclipse(Liferay developer studio) Go to New->Liferay Project
    ii) In the "New Liferay Project" dialog. Write the "Project Name:" (e.g    MyPortal)
    iii) Select "Plugin" type as ext.
    iv) Press the "Finish" button.

2. In your MyPortal-ext/docroot/WEB-INF/ext-web/docroot/WEB-INF/liferay-portlet-ext.xml. Write the following code
<?xml version="1.0"?>
<!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 6.1.0//EN" "http://www.liferay.com/dtd/liferay-portlet-app_6_1_0.dtd">

<liferay-portlet-app>
    <portlet>
        <portlet-name>2</portlet-name>
        <system>false</system>
    </portlet>

</liferay-portlet-app>


(Explanation :- 
system
Set the system value to true if the portlet is a system portlet that a user cannot manually add to their page. The default value is false.
)

3. Add following code to MyPortal-ext/docroot/WEB-INF/ext-web/docroot/WEB-INF/portlet-ext.xml.

<?xml version="1.0"?>

<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>2</portlet-name>
        <display-name>My Account</display-name>
        <portlet-class>com.liferay.portlet.StrutsPortlet</portlet-class>
        <init-param>
            <name>template-path</name>
            <value>/html/portlet/my_account/</value>
        </init-param>
        <init-param>
            <name>view-action</name>
            <value>/my_account/edit_user</value>
        </init-param>
        <expiration-cache>0</expiration-cache>
        <supports>
            <mime-type>text/html</mime-type>
        </supports>
        <resource-bundle>com.liferay.portlet.StrutsResourceBundle</resource-bundle>
    </portlet>
</portlet-app>

Note:- Below 2 steps (4 and 5) are followed for Liferay 6.1 GA
4. Create a folder under portal-ext/docroot/WEB-INF/ext-impl/src/ as "resource-actions"
and a file inside this folder
portal-ext/docroot/WEB-INF/ext-impl/src/resource-actions/portal-ext.xml

5. Add the below code to the file:-
<?xml version="1.0"?>
<!DOCTYPE resource-action-mapping PUBLIC "-//Liferay//DTD Resource Action Mapping 6.1.0//EN" "http://www.liferay.com/dtd/liferay-resource-action-mapping_6_1_0.dtd">

<resource-action-mapping>
    <portlet-resource>
        <portlet-name>2</portlet-name>
        <permissions>
            <supports>
                <action-key>ADD_TO_PAGE</action-key>
                <action-key>ACCESS_IN_CONTROL_PANEL</action-key>
                <action-key>CONFIGURATION</action-key>
                <action-key>VIEW</action-key>
            </supports>
            <site-member-defaults />
            <guest-defaults />
            <guest-unsupported>
                <action-key>ADD_TO_PAGE</action-key>
                <action-key>ACCESS_IN_CONTROL_PANEL</action-key>
                <action-key>CONFIGURATION</action-key>
                <action-key>VIEW</action-key>
            </guest-unsupported>
        </permissions>
    </portlet-resource>
</resource-action-mapping>


6. In portal-ext.properties add dockbar.add.portlets=2,56,101,110,71

7. Deploy your ext.

8. Reboot the server.

Result:

You'll be able to view "My Account portlet" in the dockbar "Add"->more-> Tools menu

Thursday 23 May 2013

LIFERAY:- Custom SQL Query from multiple tables

Introduction:-

Sometimes we encounter with a very complex DB and henceforth, a more complex query, where data has to ask many other tables for if they do have the same value with them or not. Basically, implementing a number of Constraints. For this we can then go for a CustomSQL mechanism of liferay.
For more:- liferay wiki

Problem:
Sometimes we want to extract data from two tables, but are not able to.

Solution:-

Lets assume we have a query like this:-
SELECT forumPosts.id as id, forumPosts.title as title, forumPosts.date as date, 
forumPosts.author as author, 
forumPosts.countAnswers as count_answers, forumUsers.firstName as fname, 
forumUsers.lastName as lname FROM forumPosts INNER JOIN users ON forumPosts.author = forumUsers.userid WHERE forumPosts.parentPost IS NULL ORDER BY id DESC;
 
Steps:
1. Create a portlet (as in here).
2. Create service.xml (as in here).
 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.1.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_1_0.dtd">
<service-builder package-path="com.test">
    <author>priyanka</author>
    <namespace>forum</namespace>


    <entity name="ForumPosts" table="forumPosts" local-service="true"
        remote-service="true" >

      <column name="id" type="long" primary="true" />
      <column name="title" type="String" />

      <column name="date" type="String" />
      <column name="parentPost" type="long" />
      <column name="author" type="long" />
      <column name="countAnswers" type="long" /> 
      <column name="lastPost" type="long" />
    </entity>

     <entity name="ForumUsers" table="forumUsers"
        local-service="true" remote-service="true">

        <column name="userid" type="long" primary="true" />
        <column name="firstName" type="String"  />
        <column name="lastName" type="String" />
    </entity>

</service-builder>

3. Do "ant build-service"
4. Create a folder custom-sql in {my-portlet}/docroot/WEB-INF/src/
5. Create default.xml in {my-portlet}/docroot/WEB-INF/src/custom-sql/
<?xml version="1.0" encoding="UTF-8"?>
<custom-sql>
    <sql file="custom-sql/get_forum_data.xml" />
</custom-sql>

6. Create get_forum_data.xml
<?xml version="1.0" encoding="UTF-8"?>
<custom-sql>
<sql id="com.test.service.persistence.ForumPostFinder.getForumData">
<![CDATA[

SELECT forumPosts.id as id, forumPosts.title as title, forumPosts.date as date, 
forumPosts.author as author, 
forumPosts.countAnswers as count_answers, forumUsers.firstName as fname, 
forumUsers.lastName as lname FROM forumPosts INNER JOIN users ON forumPosts.author = forumUsers.userid WHERE forumPosts.parentPost IS NULL ORDER BY id DESC;
 ]]>
</sql>
</custom-sql>


7. Create a class ForumPostFinderImpl under  package com.test.service.persistence

public class ForumPostFinderImpl extends BasePersistenceImpl implements
        ForumPostFinder {
public static String GET_FORUM_DATA =
ForumPostFinder.class.getName()+".getForumData";

    public Object
getForumData()
            throws SystemException {
        // open a new hibernate session in normal case when you are opening
        // session for same entity
        Session session = null;
        SQLQuery query = null;
        QueryPos qpos;
        try {
            session = openSession();
            String sql = CustomSQLUtil.get(
GET_FORUM_DATA);
            // create a SQLQuery object
            query = session.createSQLQuery(sql);
            query.setCacheable(false);


            query.addScalar("id", Type.INTEGER);
            query.addScalar("title", Type.STRING);
            query.addScalar("date", Type.DATE);
            query.addScalar("author", Type.STRING);
            query.addScalar("count_answers", Type.
INTEGER);
            query.addScalar("fname", Type.STRING);
            query.addScalar("lname", Type.STRING);
          

           //This is used when you want to pass any value to the query and place "?" in the query where you want the value to be set
           //qpos.add(Id);
           
        } catch (Exception e) {
            System.out.println("Exception : Finder custom");
            e.printStackTrace();
        } finally {
            closeSession(session);
        }

        // execute the query and return a list from the db
        return (Object) query.list().get(0);
    }
}
 


8. Go to ForumPostLocalServiceImpl and add the following functionpublic Object getForumPostAndUserData() throws SystemException {
        return ForumPostFinderUtil.getForumData;
    }


9. Again do ant build-service.

10. ForumPostFinderUtil and interface ForumPostFinder is created.

11. In your portlet doView()
                    ForumPostLocalServiceUtil
                                .getForumPostAndUserData());


This is all, you can build-service and then deploy the portlet.

Enjoy Coding :)

Wednesday 22 May 2013

Connection Pool in Service Builder Code that refers to a different DB: Liferay

Introduction:-

In this tutorial we are gonna learn - how to create a datasource that refers to a different DB(ie. other than our liferay DB) and provide custom connection pooling  values. However, Liferay has provided inbuilt connection pooling but we cannot manipulate or modify the max pool size etc and other values, when needed. So, here is the solution.

Steps:-
1. Create a new portlet.(as in here)
2. Create service.xml(as in here)
3. In /{my-portlet}/docroot/WEB-INF/src/META-INF create a file ext-spring.xml
<?xml version="1.0"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    default-destroy-method="destroy" default-init-method="afterPropertiesSet"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- placeholder that manage then data source entry from a jdbc.properties
        file from WEB-INF folder -->
    <bean
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location">
            <value>/WEB-INF/jdbc.properties</value>
        </property>
        <property name="placeholderPrefix" value="${jdbc.eportal" />
    </bean>
    <bean id="eportalDBTarget" class="com.liferay.portal.spring.jndi.JndiObjectFactoryBean"
        lazy-init="true">
        <property name="jndiName">
            <value>jdbc/eportalDB</value>
        </property>
    </bean>

    <bean id="eportalDB"
        class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy"
        lazy-init="true">

        <property name="targetDataSource">
            <ref bean="eportalDBTarget" />
        </property>

    </bean>
    <bean id="liferayHibernateSessionFactory"
        class="com.liferay.portal.spring.hibernate.PortletHibernateConfiguration">
        <property name="dataSource" ref="eportalDB" />
    </bean>
    <bean id="liferayTransactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="dataSource" ref="eportalDB" />
        <property name="globalRollbackOnParticipationFailure" value="false" />
        <property name="sessionFactory" ref="liferayHibernateSessionFactory" />
    </bean>
    <bean id="liferaySessionFactory" class="com.liferay.portal.dao.orm.hibernate.SessionFactoryImpl">
        <property name="sessionFactoryImplementor" ref="liferayHibernateSessionFactory" />
    </bean>
</beans>

4. In the web.xml append the following code in <web-app
<resource-ref>
        <res-ref-name>jdbc/eportalDB</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>

5. Create a file context.xml with the following code, in {my-portlet}/docroot/META-INF/
<?xml version="1.0" encoding="UTF-8"?>

<Context antiJARLocking="true" antiResourceLocking="true" privileged="true" crossContext="true">
    <Resource name="jdbc/eportalDB" auth="Container" type="javax.sql.DataSource"
        maxActive="100" maxIdle="30" maxWait="10000" username="postgres"
        password="postgres" driverClassName="org.postgresql.Driver"
        url="jdbc:postgresql://localhost:5432/postgres?useUnicode=true" />
</Context>

6. Last but not the least, add the datasource name to your service.xml as in here.
<!--entity-Employee -->
    <entity name="Employee" table="employee" local-service="true"
        remote-service="true" data-source="eportalDB">

        <!-- PK fields -->
        <column name="emp_id" type="String" primary="true" />
        <column name="emp_name" type="String" />

    </entity>

7. Create {my-portlet}/docroot/WEB-INF/jdbc.properties file with the following code
eportal.url=jdbc:postgresql://localhost:5432/postgres
eportal.username=postgres
eportal.driverClass=org.postgresql.Driver
eportal.password=postgres

8. Do ant build-service and then deploy.

Here is all you have to do, to implement connection pooling.

NOTE: After deploying the code, in some versions of liferay it replaces the {my-portlet}/META-INF/context.xml.(check here)
So, I just replaced my version of context.xml in the tomcat/Webapps/{my-portlet}/META-INF/ directory of my tomcat.