Wednesday, 31 July 2013

Liferay for u:

Liferay for u:


www.liferay4you.blogspot.com

Insalling Liferay on Amazon Cloud - Step by Step

It was fun installing Liferay on Amazon. My happiness had no bounds when I saw the cost that I am going to incur at Amazon and the robust infrastructure it offers.
You will find tons of articles on Amazon Cloud and its features and hence I will not spend time here explaining the same. Instead I prefer delving directly into what you need to do in order to setup liferay on Amazon Cloud. I will document the entire procedure in a step-by-step format.

Apache2, Liferay 6.0.5, MySQL & phpmyadmin

1.    Create an account at Amazon and subscribe for ec2 service. It will ask credit card info. Do not worry, you will be billed only when you start using the service. In the process of creating an account you might have definitely received a .pem file which contains the security information. Amazon does not allow connecting to the instance using user and password instead it uses this identity file (.pem) to identify the user logging on to the system.
2.    Go to AWS Management Console (Amazon Web Service Management Console) and select the EC2tab. This tab is used to create a new Amazon EC2 instance.
3.    Launch a new instance by selecting ami-a6f504cf as the AMI (Amazon Machine Image). You can search for this image under Community AMI(s). This is a small instance with 1.7 GB of RAM (ubuntu server) which is quite sufficient for Liferay 6.0.5.
4.    Also ensure that you select the right location of your instance; it does affect your billing. I being from India, have selected: us-east-1b
5.    Create an Elastic IP from the Elastic IP section and associate it to the instance created. Now you will be able to access your server instance using the IP address created in this process say : 50.19.110.20 for e.g. Through out this tutorial I will now consider that this the I.P address of your instance.
6.    Connect to the instance using ssh specifying the identity file as follows:
ssh -i /ec2/AmazonKeyPair.pemubuntu@50.19.110.20 (On my mac machine I have saved AmazonKeyPair.pem in ec2 folder.
7.    Once you are get access to the machine, you can start installing the required software
8.    Installing the software(s)
          I.        First you need to ensure that existing components are up-to-date and hence need to run apt-get -y update
         II.        Also run
apt-get -y upgrade and
apt-get -y install build-essential
       III.        It is not advisable to run tomcat on port 80. Instead you can run tomcat behind apache2 server. In order to install apache2 server run
apt-get -y install apache2
       IV.        You will also need the sun java sdk
apt-get -y install sun-java6-jdk
        V.        Now you must install mysql 5.x so that you can point your liferay server to this database. Run
apt-get -y install mysql-server-5.0 (Set the password for the database when asked for)
       VI.        If you want to test the mail server and send a test mail, then do the following:
apt-get -y install mailx
Test the installation as follows:
echo "The data is successfully copied to S3" |mailx -s "Data Backup to S3 successful"youremailaddress@youremaildomain.com
Check whether you receive the mail to the email address specified above.
     VII.        Change the date to the timezone you want using
dpkg-reconfigure tzdata
9.    You can now download phpmyadmin and make the application available through apache2
apt-get -y install phpmyadmin
          I.        Open apache2.conf and put the following as last line
Include /etc/phpmyadmin/apache.conf
         II.        Check with http:// 50.19.110.20/phpmyadmin. You must get a page which asks the username and password to MySQL Server.
10. Installing Liferay: You can download the latest bundle of liferay from liferay.com. However, I have uploaded the Liferay Bundle that was there on my local machine to Amazon instance using scp. I zipped the entire folder of my liferay bundle and uploaded and also took the back up of complete database from local machine and uploaded it to the MySQL instance running on Amazon instance.
11. MySQL table names: MySQL server on ubuntu is automatically configured to be case-sensitive. If you have created database structure with mixed case or upper case on your local machine (windows or mac) and have backed up the database and uploaded it on MySQL on any linux based machine, then your application will fail. This is because in your application you might have used the name of the table in the mixed case where as sql will say that the table does not exist because it searches for the name of the table exactly in mixed case. In order to work-around this problem, you have to make the following setting:
In etc/mysql/my.cnf file look for the section [mysqld]. Under that section add the following after skip-external-locking
lower_case_table_names = 1
For detailed explanation on this refer to : http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html

Configuring jk mod to make apache2 proxy for tomcat

JK Mod: This is a module that sets up apache2 as a proxy server for tomcat.
1.    apache2.conf file: Add the following in apache.conf in /etc/apache2
JkWorkersFile /etc/apache2/workers.properties
JkShmFile /var/log/apache2/mod_jk.shm
JkLogFile /var/log/apache2/mod_jk.log
JkLogLevel error
JkRequestLogFormat "%U %q "
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
JkMount /* tomcat
2.    workers.properties: In /etc/apache2, create a file called workers.properties and add the following
worker.list=tomcat
worker.tomcat.type=ajp13
worker.tomcat.host:127.0.0.1
worker.tomcat.port=8009
3.    Using no-jk: phmyadmin application is installed on apache2. Once you configure apache2 as proxy for tomcat, each and every request will land on tomcat and this would fail running of your phpmyadmin because phpmyadmin is installed on apache2 and not tomcat. So you need to tell apache2 that all calls like http://www.<domain-name>/phpmyadmin/* must not use mod jk. In order to achieve this, open /etc/apache2/sites-available/default file and the following entry just below DocumentRoot /var/www

JkMountCopy On
SetEnvIf Request_URI "/phpmyadmin/*" no-jk
4.    Rewrite module: You will notice on many web sites that the request to web sites without www will result in a redirect with www.<domain-name>.com. Check this out: On the address bar of your web browser type oracle.com and hit the enter key. You will notice that it redirects to www.oracle.com. If you wish to do the same with apache2 then, in /etc/apache2/sites-available/default file write the following towards the end of the file:
RewriteEngine on
RewriteCond %{HTTP_HOST} ^mslearningandresearch\.com$ [NC]
RewriteRule ^(.*)$ http://www.mslearningandresearch.com$1 [R=301,L]

In the above snippet I have mentioned my domain name, you replace it with your domain name.
You will have to do two more important things before things work as per your expectations and which is as follows:
a) Change AllowOverRide None in /etc/apache2/sites-available/default toAllowOverRide All (or something else, but not None)
b) Run the command (ensure that rewrite.load file is present in mods-available folder)
a2enmod rewrite

5.    Restart apache2 service by running the following command
service apache2 restart

PS: If you are running a multiple portal instances as I am running (mslearnignandresearch.com and liferayaddict.com) then do not forget to make the following entries in your portal-ext.properties file :

web.server.http.port=80
auth.token.check.enabled=false
redirect.url.ips.allowed=127.0.0.1,50.17.221.17
redirect.url.security.mode=domain
redirect.url.domains.allowed=www.mslearningandresearch.com,www.liferayaddict.com


Building Liferay Portlet Plugin Project


Liferay plugin projects are built using ant. When a new plugin project is created, there is one build.xml file created for the project. When you run ant on build.xml file, the process generates a war file and drops it in deploy folder of tomcat (or which ever is the hot deploy folder of your app server). Tomcat sees that there is a .war file in deploy folder and it immediately explodes it in webapps folder there by deploying the application.

liferay-plugin-packages.properties

If you want to use jar (or tld) files in your application that are already available with liferay portal then you can use liferay-plugin-packages.properties file  to configure them. The build process will automatically bundle these jar files in the war file of your plugin project.

web.xml

The build process also regenerates the web.xml file with the required filters and servlets that participates in request processing for the portlets that are in your plugin project. The build process does preserve the changes you have done to your web.xml file in your project. After you deploy your project, check the contents of web.xml file in webapps/your-project/WEB-INF/web.xml. You will find many filters configured.

Fast deploy

When you configure Fast Deploy of jsps, you mention the docBase as the docroot folder in your plugins sdk/portlet/your-project. In fast deploy, the build process does not create a war file and does not deploy in webapps. Instead, it copies the context file in conf/catalina/localhost of your tomcat. When tomcat sees the file a new timestamp it reloads the context considering the source of your project (docroot) as the docBase. In such a case you will be able to see the contents of lib  folder and contents of web.xml file in your project that is configured in eclipse

Custom Login Portlet in Liferay



Customizing through hooks is possible but it is highly possible that we may get stuck at some point due to limitations of how much we can override the out-of-the-box stuff in hooks. Liferay provides much easier way achieve what we want to which is as follows:

  • Create a Custom Login Portlet as a plugin. This will give you a free hand to implement any number of actions and pages in the use case. Consider the name of your portlet is MyCustomLogin
  • Create a new page on Guest Community and drag and drop MyCustomLogin. Consider you created this page with friendly url as login
  • Mark this new page as hidden (Optional)
  • Make the following entry in portal-ext.properties: auth.login.url=/web/guest/login and auth.login.portlet.name=MyCustomLogin
  • Restart the server

I18N in Liferay Plugin Portlet


Internationalization is one of the most easiest things to do in Liferay Portlet Plugin. Following are the steps to configure & pick-up properties resource bundle:
  1. In portlet.xml file make an entry under after <supports> as follows: <resource-bundle>content/Language-ext</resource-bundle>
  2. Create a folder under src called content and within content create a file called Language-ext.properties file. Configure the required properties in this file. Create properties file for other languages for e.g Langauage-ext_es.properties for Spanish.
  3. Configure a property called javax.portlet.title  in each one of the Language-ext files with values based on the language that the property file stands for.
  4. Build your portlet and deploy it. Drop your portlet on the page. Also drop Language portlet.
  5. Change from one langauge to the other (change to Esapan) and you will get title that you have configured in Langauage-ext_es.properties file and then change to English. You will get the title that you configured in Langauage-ext.properties

Accessing Database from Service other than the default Liferay Database

Using ServiceBuilder in Liferay by default creates and uses tables in the same database which liferay is by default configured to use. In order to use a different database through your service builder do the following:

Create ext-spring.xml file in /src/META-INF folder and add the following:

<bean id="customDataSource"
 class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
      <property name="targetDataSource">
   <bean class="com.liferay.portal.dao.jdbc.util.DataSourceFactoryBean">
       <property name="propertyPrefix" value="jdbc.custom.default." />
   </bean>
      </property>
</bean>
<bean id="liferayHibernateSessionFactory"
   class="com.liferay.portal.spring.hibernate.PortletHibernateConfiguration">
     <property name="dataSource" ref="customDataSource" />
</bean>

Make the following entry in your portal-ext.properties file

jdbc.custom.default.driverClassName=com.mysql.jdbc.Driver
jdbc.custom.default.url=jdbc:mysql://127.0.0.1/lportal_custom?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false
jdbc.custom.default.username=root
jdbc.custom.default.password=root

Do the following in service.xml file

While defining the entity, set the data-source as customDataSource as follows:
<entity name="Customer" local-service="true" remote-service="false" data-source="customDataSource">
This will ensure that all the calls being made, will be made to the custom (non-liferay) database. Now the next important point
The aforementioned will not help in manipulating data. For e.g. if you try to fire an update or delete your service will still hit the liferay database. Also note that your transactions will not work. For transactionManager to work such that from the <Entity>LocalServiceImpl if you throw PortalException or SystemException the transaction must be rolled back, you need to make some additional changes. Here is the complete configuration of ext-spring.xml file (you don't need to make modifications in any other .xml file).  I have tested the following configuration with Liferay 6.1 GA2 and it works very well (ensure that you configure data-source, session-factory and tx-manager as customDataSource, customSessionFactory and customTransactionManager respectively in service.xml file for your Entities)
    <bean id="customDataSource"
        class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
        <property name="targetDataSource">
            <bean class="com.liferay.portal.dao.jdbc.util.DataSourceFactoryBean">
                <property name="propertyPrefix" value="jdbc.custom.default." />
            </bean>
        </property>
    </bean>

    <bean id="customHibernateSessionFactory" 
        class="com.liferay.portal.kernel.spring.util.SpringFactoryUtil"
        factory-method="newBean">
        <constructor-arg
            value="com.liferay.portal.spring.hibernate.PortletHibernateConfiguration" />
        <constructor-arg>
            <map>
                <entry key="dataSource" value-ref="customDataSource" />
            </map>
        </constructor-arg>
    </bean>
    <bean id="customSessionFactory" class="com.liferay.portal.kernel.spring.util.SpringFactoryUtil"
        factory-method="newBean">
        <constructor-arg
            value="com.liferay.portal.dao.orm.hibernate.PortletSessionFactoryImpl" />
        <constructor-arg>
            <map>
                <entry key="dataSource" value-ref="customDataSource" />
                <entry key="sessionFactoryClassLoader" value-ref="portletClassLoader" />
                <entry key="sessionFactoryImplementor" value-ref="customHibernateSessionFactory" />
            </map>
        </constructor-arg>
    </bean>
    
    <bean id="transactionAdvice" class="com.liferay.portal.kernel.spring.util.SpringFactoryUtil"
          factory-method="newBean">
        <constructor-arg value="com.liferay.portal.spring.transaction.TransactionInterceptor" />
        <constructor-arg>
            <map>
                <entry key="platformTransactionManager" value-ref="customTransactionManager" />
                <entry key="transactionAttributeSource" value-ref="transactionAttributeSource" />
            </map>
        </constructor-arg>
    </bean>
    
    

    <bean id="customTransactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager"
        lazy-init="true">
        <property name="dataSource">
            <ref bean="customDataSource" />
        </property>
        <property name="sessionFactory">
            <ref bean="customHibernateSessionFactory" />
        </property>
    </bean>

Liferay 6.0.x to Liferay 6.1 Migration Tips


Following are the important tips

  1. Unzip Liferay 6.1 bundle
  2. Copy complete data folder as is from Liferay 6.0.x bundle to Liferay 6.1 bundle
  3. Take backup of your the database that Liferay 6.0.x points to
  4. Drop portal-ext.properties from Liferay 6.0.x to Liferay 6.1 (in ROOT/WEB-INF/classes ofcourse). Essentially you are pointing your new Liferay 6.1 to old database
  5. Start Liferay 6.1 and it will automatically detect that it needs to upgrade the database and will run the upgrade scripts. In case there is any exception, you will have to restore the backup of old database and start all over again. Usually the exception is because of missing resource in data folder that the entry in database refers to.
  6. If all goes well then your Liferay 6.1 will start successfully with old database upgraded.
  7. Check whether you get all your Users and Organizations under "Users & Organizations" in control panel. If not then run - Control Panel >> Server Administration >> Re-Index all Search Indexes

Struts Portlet in plugin project

  1. If you have struts portlet in plugin project, on Liferay 6.1, it will throw InvokeFilter Exception and will result in StackOver Flow.
  2. Make an entry in liferay-plugin-packages.properties file : liferay-web-xml-enabled=false
  3. Be careful while rebuilding the existing services generated using ServiceBuilder. Delete all rows from servicecomponent table and reset the service builder number to 0 and then build services. Liferay keeps a backup of database tables associated with entitties in the service and will rewrite the contents of some of the tables. If you do not want your tables to be overwritten then set  build.auto.upgrade=false in service.properties file in src folder of your plugin project

Conditional Transition in Liferay Workflow using default Kaleo Engine

Conditional Transition in Liferay Workflow using default Kaleo Engine



Loads of material is available on Liferay forums and Liferay WIKI on Workflow in Liferay. I do not intend to play  gimmick by replicating here. I intend to only illustrate how to do conditional transition. Before moving ahead with this blog entry of mine, please go through http://www.liferay.com/documentation/liferay-portal/6.1/user-guide/-/ai/creating-process-definitions first.
Use Case: There is single-approver-definition by default bundled with kaleo workflow engine in Liferay 6.x This default definition worked just fine for my client but with the only difference that if the content is created by a user having Administrator role, then it must NOT be kept pending for approval instead must automatically be considered as approved. And if any user with any role other than Administrator creates the content then it must go through the normal approval process i.e. review and then either reject or approve.
I must mention here that my knowledge of UML (especially statechart & activity diagrams) really helped when I had initially started with BPMN. Well without any further ado, let me illustrate how to achieve the above mentioned requirement.
Create a copy of single-approver-definition called my-workflow and add the following immediately after first state node named as "created"
<condition>
        <name>checkuser</name>
        <description>Checking whether the user is a portal admin</description>
        <script>
            <![CDATA[
           
                import com.liferay.portal.model.User;
                import com.liferay.portal.kernel.workflow.WorkflowConstants;
                import com.liferay.portal.service.UserLocalServiceUtil;
                import com.liferay.portal.model.Role;
                import java.util.List;
               
               
                String userId = (String) workflowContext.get(WorkflowConstants.CONTEXT_USER_ID);
                User user = UserLocalServiceUtil.getUser(Long.valueOf(userId));
                returnValue  = "review";
       
                 List<Role>  roles =  user.getRoles();
                 for (Role r : roles) {
                    println r.getName();
                    if (r.getName().equals("Administrator")) {
                      println "Setting approve for user :  " + user.getFirstName();
                      returnValue = "approve";
                      break;
                    }
                 }
           
                 println user.getFirstName() + " transitioning to : " + returnValue;
                
                 return;
           
            ]]>
        </script>
        <script-language>groovy</script-language>
        <transitions>
            <transition>
                <name>approve</name>
                <target>approved</target>
                <default>true</default>
            </transition>
            <transition>
                <name>review</name>
                <target>review</target>
            </transition>

        </transitions>

    </condition>
Explanation: A condition can have a script which when evaluated must have a value returned back. The name of the variable kaleo looks for is returnValue (normal groovy stuff I suppose). Based on some conditions you set the value in returnValue. Kaleo will do the transition to the value returned by the script. For e.g. if the value of returnValue is "xyz" then it will look for the transition called xyz.
In the script above, I am checking the role of the user logged on to the system. If the user has Administrator role then the value of returnValue is "approve" - this approve is the name of the transition defined withing the condition which is mapped to the target state called "approved". If the user is not Administrator then the transition happens to task called "review". Note that "approved" is the state and "review" is the task. Tasks require user input to proceed and "state" does not.
Res of the entire definition remains as is. I have attached the complete xml to this blog for direct use - my-workflow.xml

Simple Internationalization in Liferay

Simple Internationalization in Liferay


I will make it short and simple
  1. Create the language bundle in src/content folder with files like Language.properties, Language_ar_SA.properties etc.
  2. For portlet api to use this bundle so that it can set the title etc as per the language selected, you need to set this bundle in portlet.xml file using
      <resource-bundle>content.Language</resource-bundle>
    
      
  3. Then in bundle, you can set javax.portlet.title property for portlet to pick up based on the language selected.
  4. In jsps, for properties used in your use cases you can write the following:
      <fmt:setLocale value="<%=themeDisplay.getLanguageId() %>"/>
    
    <fmt:setBundle basename="content.Language" />
    
    <h1><fmt:message key="profile.label"/></h1>
    
  5. In your portlet class you can do the following:
         ThemeDisplay display = (ThemeDisplay) request
                    .getAttribute(WebKeys.THEME_DISPLAY)
       System.out.println(display.getLanguageId());
    
       ResourceBundle rb = ResourceBundle.getBundle("content.Language",
                    LocaleUtil.fromLanguageId(display.getLanguageId()));// LocaleUtil.fromLanguageId("ar"));
       String label = rb.getString("profile.label");
    
       System.out.println(label);

New Action in Hook in Liferay 6.1

New Action in Hook in Liferay 6.1


In one of my projects I needed to define a new action for a Mesage Board Portlet. Overriding the action class of an existing action is cake walk. However, creating a new action and then forwarding to a custom jsp is challenging. As mentioned in the post here : http://www.liferay.com/web/mika.koivisto/blog/-/blogs/7132115 I did create a new action and it did not work owing to ClassCastException for   com.liferay.portal.struts.ActionAdapter  . I could solve this problem but then the custom jsp that was included was not wrapped in the theme. Following are the steps to get the whole thing working:
  1. Do not extend your custom action class from BaseStrutsAction instead extend from BaseStrutsPortletAction class to avoid ClassCastException
  2. Now the jsp that you will return back from the render method in your Action class will take you to jsp but without wrapping it in to theme. In order to get this working, defined the same action in struts-config-ext.xml file, specify some Action class which is already there in core portal. Specify the forward of your own, map it to a custom tiles def of your own. Then create a file called tiles-defs-ext.xml file and define the custom tiles definition. forwarding to jsp that you created in hook.
  3. Restart the tomcat server. 
  4. Deploy your hook. 
Essentially what you are doing is that you are declaring action and tiles definition in struts-config-ext and tiles-defs-ext respectively and then overriding it through a hook.

AutoLogin and Custom Authenticator in Liferay

AutoLogin and Custom Authenticator in Liferay


Nothing can be simpler than implementing AutoLogin and Custom Authenticator in Liferay. Following is the "How-To"
  1. Create a Hooks Project
  2. Put the following properties in portal.properties file in your hooks project
    1. auth.pipleline.pre=com.mslc.authenticators.EMMAuthenticator
    2. auto.login.hooks=com.mslc.authenticators.EMMAutoLogin
    3. auto.login.ignore.paths=/web/guest.home  (optional)
  3. Then create the classes configured
  4. EMMAuthenticator must implement com.liferay.portal.security.auth.Authenticator
  5. EMMAutoLogin must implement com.liferay.portal.security.auth.AutoLogin
  6. EMMAuthenticator must return an int. If it returns Authenticator.SUCCESS then the authentication is passed or else if it returns Authenticator.FAILURE then it has failed.
  7. EMMAutoLogin must return a String[]. The array must contain 3 elements
    1. userId
    2. password
    3. true or false (string format ofcourse) to indicate whether the password is encrypted or not

Service Architecture on Liferay Project - Multiple service.xml file

Service Architecture on Liferay Project - Multiple service.xml file
Top of Form
12/20/12 8:35 AM
The best place to learn Liferay Architecture is the Liferay source code itself. When it comes to creating services in Liferay Usually people create one monolithic service.xml in docroot/WEB-INF folder. Well... that is not the right approach if the number of custom services you intend to develop is large. In one of my projects that I am currently working on, I have 10s of services. The best approach is to create one package structure for each service for e.g. com.mslc.projectname.conceptualnameofservice and create a service.xml file in this package. Different services will have different package structure. You can very well build these services exactly as same as you build services in WEB-INF folder; more conviniently if you have Liferay IDE.  Take a look at Liferay source code and scan through the service.xml files they have create in portal-impl/src folder under package structures like com.liferay.portal, com.liferay.portlet.blogs, com.liferay.portlet.journal etc.
So far so good. But then when you run build-wsdd target in order to generate the service-config.wsdd file in order to expose your service as web service, then you run into problems. By default the build-wsdd target looks for service.xml file in docroot/WEB-INF folder. As mentioned above, the service.xml file can be created in their respective package structure so we do not have service.xml file in docroot/WEB-INF which is expected by build-wsdd. So what do we do ? Here is the solution
1.     open sdk/build-common-plugin file
2.     search for target build-wsdd
3.     you will find one argument called wsdd.input.file in the line which looks like this:
            I.       
<arg value="wsdd.input.file=docroot/WEB-INF/service.xml" />
4.     Change the value to   ${service.input.file}  . This is how the line will look like after changes
    I.     <arg value="wsdd.input.file=${service.input.file}" />
Save the file and run build-wsdd on respective service.xml file. It works like a charm - it generates service-config.wsdd in WEB-INF folder perfectly for each service you build the wsdd for.

Transaction Management with Liferay Service

Transaction Management with Liferay Service


Following are the important points that a Liferay Architect must make note of in order to work with Transactions in Liferay
  1. The entry point to transaction (start of transaction boundary) in Liferay service is the <Entity>LocalServiceImpl class. Hence, while firing DML oriented methods like update, insert and delete on X Entity from Y do not use make calls to XLocalServiceUtil or XLocalService as this will result in two transaction boundaries - one for entity X and the other for entity Y. Relate the two entities through service.xml file (one to many or many to many based on your domain model) or else refer one entity in the other using <reference... in service.xml file. For e.g. if you want to make call on X entity from YLocalServiceImpl then in service.xml file while configuring Y Entity put

    <reference entity="X" package-path="com.company.project.X"></reference>

    On building the service, you will have access to xPersistence in YLocalServiceImpl. You can now make calls to xPersistence for update, insert and delete. <reference... tag is exactly for this reason. You can also include something like <reference entity="User" package-path="com.liferay.portal"></reference> to get the userPersistence object in your Entity. Then the calls to your entity and userPersistence will be the part of same transaction. In other words, updates to your tables and user_ table will be part of same transaction - either everything will commit or evrerything will be rolledback on encounter PortalException or SystemException.
  2. Ensure that your database supports transactions; yes ! this is needless to say but then if you are using MySQL, the database engine other MyISAM does not support transactions. You must use InnoDB as the database engine for your tables.
  3. As you know Liferay Entity code is generated by service builder and you get an interface called XLocalService. It is this interface which is marked with Transactional Annotation. It is set such that if you throw PortalException orSystemException from any of the methods, Hibernate Transaction Manager will rollback the transaction. Hence on violation of any business rule in the custom business-service method of XLocalServiceImpl if you want to rollback the transaction, you must throw either PortalException or SystemException

How to expose Finder methods from service to LocalServiceUtil in liferay portal

How to expose Finder methods from service to LocalServiceUtil in liferay portal 



In liferay portal if we want to find records from table then we need finder methods. To create finder method we need to specify the finder tag in the service.xml file.

Assume that we have a database table named customer having fields like name, city, age etc. We want to find all the customers belonging to a particular city.

Following are the steps to create finder method for the above requirement-

1.  In the service.xml file add the following line after database columns declarations,

<finder name=”City” return-type=”Collection”>
<finder-column name=”city”></finder-column>
</finder>

name– method name , a method named "findByCity" will be generated

Collection – return type
finder column name – name of the corresponding column in the customer table

2.  Run the ant target “ant build-service”. In the SampleUtil.java you will find the following finder -method –

public static java.util.List<com.sample.model.Sample> findByCity(
java.lang.String city) throws com.liferay.portal.SystemException {
return getPersistence().findByCity(city);
}

3.  Copy the finder method from this class and paste it inside theSampleLocalServiceImpl class and modify the method definition as following

-       Remove the static modifier

-       Replace getPersistence() with SampleUtil.

4.  Run the target “ant build-service” again. After build finder will be available in your SampleLocalServiceUtil class.

List<Sample> customerList = SampleLocalServiceUtil.findByCity(“cityname”);