Sunday, October 18, 2009

Liferay for backend developers

This is a tutorial for Back End developers (Java code) in charge of developing Portlets for Liferay Portal.

As a backend developer needs to understand how the look and feel will be applied, this tutorial is a continuation of my recent "Liferay for Designers" tutorial. So, if you haven't done it please follow steps on the Designers tutorial before continuing.

Note that I am providing instructions here for two bundles, tomcat and jboss+tomcat. You better use tomcat for development purposes as it is lighter and just use JBoss if you really need J2EE capabilities not available from Tomcat.

Extension Environment

You will need to use this development path either when:
* You are extending an existing portlet portal built-in portlet or a portlet which has been built using the extension environment
* You have found the plugin environment will be not enough for the necessary logic.

Be sure you understand that no changes are done inside the ~/liferay/portal folder which we will create right away.

1. Uncompress liferay-portal-src-5.2.3.zip into ~/liferay/portal

2. Many parameters from ~/liferay/portal/portal-impl/src/portal.properties can be modified but you do so inserting the new values into ~/liferay/ext/ext-impl/src/portal-ext.properties. Many of the properties are actually modified using ~/liferay/ext/ext-impl/src/portal-developer.properties instead (if you do not have it there already just copy it from liferay/portal/portal-impl/src/portal-developer.properties) and deploy it using "ant deploy-properties" from ~/liferay/ext/ext-impl

3. Let us install p6spy so we can sniff JDBC.

Tomcat

Add the below at the end of file ~/liferay/tomcat-6.0.18/bin/setenv.sh:


JAVA_OPTS="-Dp6.home=$HOME/liferay/tomcat-6.0.18/lib/ext $JAVA_OPTS"
Jboss

Insert the below line at the very beginning of ~/liferay/jboss-tomcat-5.0.0/bin/run.sh


JBOSS_CLASSPATH="$HOME/liferay/jboss-tomcat-5.0.0/server/default/db"
Then create file spy.properties

Tomcat


~/liferay/tomcat-6.0.18/lib/ext/spy.properties
 Jboss


~/liferay/jboss-tomcat-5.0.0/server/default/db/spy.properties
Below is the content of spy.properties file. It can be further configured but with the settings below you will be just fine.


#################################################################
# P6Spy Options File #
# See documentation for detailed instructions #
#################################################################

#################################################################
# MODULES #
# #
# Modules provide the P6Spy functionality. If a module, such #
# as module_log is commented out, that functionality will not #
# be available. If it is not commented out (if it is active), #
# the functionality will be active. #
# #
# Values set in Modules cannot be reloaded using the #
# reloadproperties variable. Once they are loaded, they remain #
# in memory until the application is restarted. #
# #
#################################################################

module.log=com.p6spy.engine.logging.P6LogFactory
#module.outage=com.p6spy.engine.outage.P6OutageFactory

#################################################################
# REALDRIVER(s) #
# #
# In your application server configuration file you replace the #
# "real driver" name with com.p6spy.engine.P6SpyDriver. This is #
# where you put the name of your real driver P6Spy can find and #
# register your real driver to do the database work. #
# #
# If your application uses several drivers specify them in #
# realdriver2, realdriver3. See the documentation for more #
# details. #
# #
# Values set in REALDRIVER(s) cannot be reloaded using the #
# reloadproperties variable. Once they are loaded, they remain #
# in memory until the application is restarted. #
# #
#################################################################

# oracle driver
# realdriver=oracle.jdbc.driver.OracleDriver

# mysql Connector/J driver
# realdriver=com.mysql.jdbc.Driver

# informix driver
# realdriver=com.informix.jdbc.IfxDriver

# ibm db2 driver
# realdriver=COM.ibm.db2.jdbc.net.DB2Driver

# the mysql open source driver
#realdriver=org.gjt.mm.mysql.Driver

# mssql open source driver
realdriver=com.mysql.jdbc.Driver

#specifies another driver to use
realdriver2=
#specifies a third driver to use
realdriver3=


#the DriverManager class sequentially tries every driver that is
#registered to find the right driver. In some instances, it's possible to
#load up the realdriver before the p6spy driver, in which case your connections
#will not get wrapped as the realdriver will "steal" the connection before
#p6spy sees it. Set the following property to "true" to cause p6spy to
#explicitily deregister the realdrivers
deregisterdrivers=false

################################################################
# P6LOG SPECIFIC PROPERTIES #
################################################################
# no properties currently available

################################################################
# EXECUTION THRESHOLD PROPERTIES #
################################################################
# This feature applies to the standard logging of P6Spy. #
# While the standard logging logs out every statement #
# regardless of its execution time, this feature puts a time #
# condition on that logging. Only statements that have taken #
# longer than the time specified (in milliseconds) will be #
# logged. This way it is possible to see only statements that #
# have exceeded some high water mark. #
# This time is reloadable. #
#
# executionthreshold=integer time (milliseconds)
#
executionthreshold=

################################################################
# P6OUTAGE SPECIFIC PROPERTIES #
################################################################
# Outage Detection
#
# This feature detects long-running statements that may be indicative of
# a database outage problem. If this feature is turned on, it will log any
# statement that surpasses the configurable time boundary during its execution.
# When this feature is enabled, no other statements are logged except the long
# running statements. The interval property is the boundary time set in seconds.
# For example, if this is set to 2, then any statement requiring at least 2
# seconds will be logged. Note that the same statement will continue to be logged
# for as long as it executes. So if the interval is set to 2, and the query takes
# 11 seconds, it will be logged 5 times (at the 2, 4, 6, 8, 10 second intervals).
#
# outagedetection=true|false
# outagedetectioninterval=integer time (seconds)
#
outagedetection=false
outagedetectioninterval=

################################################################
# COMMON PROPERTIES #
################################################################

# filter what is logged
filter = true
#sqlexpression = .*statement.*

# comma separated list of tables to include when filtering
include =
# comma separated list of tables to exclude when filtering
exclude =

# sql expression to evaluate if using regex filtering
sqlexpression =


# turn on tracing
autoflush = true

# sets the date format using Java's SimpleDateFormat routine
dateformat=HH:mm:ss

#list of categories to explicitly include
includecategories =

#list of categories to exclude: error, info, batch, debug, statement,
#commit, rollback and result are valid values
#excludecategories=info,debug,result,batch,resultset
excludecategories=info,debug,result,batch,resultset,prepared

#allows you to use a regex engine or your own matching engine to determine
#which statements to log
#
#stringmatcher=com.p6spy.engine.common.GnuRegexMatcher
#stringmatcher=com.p6spy.engine.common.JakartaRegexMatcher
stringmatcher=

# prints a stack trace for every statement logged
stacktrace=false
# if stacktrace=true, specifies the stack trace to print
stacktraceclass=

# determines if property file should be reloaded
reloadproperties=false
# determines how often should be reloaded in seconds
reloadpropertiesinterval=60

#if=true then url must be prefixed with p6spy:
useprefix=false

#specifies the appender to use for logging
#appender=com.p6spy.engine.logging.appender.Log4jLogger
#appender=com.p6spy.engine.logging.appender.StdoutLogger
appender=com.p6spy.engine.logging.appender.FileLogger

# name of logfile to use, note Windows users should make sure to use forward slashes in their pathname (e:/test/spy.log) (used for file logger only)
logfile = spy.log

# append to the p6spy log file. if this is set to false the
# log file is truncated every time. (file logger only)
append=true

#The following are for log4j logging only
log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender
log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout
log4j.appender.STDOUT.layout.ConversionPattern=p6spy - %m%n

#log4j.appender.CHAINSAW_CLIENT=org.apache.log4j.net.SocketAppender
#log4j.appender.CHAINSAW_CLIENT.RemoteHost=localhost
#log4j.appender.CHAINSAW_CLIENT.Port=4445
#log4j.appender.CHAINSAW_CLIENT.LocationInfo=true

log4j.logger.p6spy=INFO,STDOUT


#################################################################
# DataSource replacement #
# #
# Replace the real DataSource class in your application server #
# configuration with the name com.p6spy.engine.spy.P6DataSource,#
# then add the JNDI name and class name of the real #
# DataSource here #
# #
# Values set in this item cannot be reloaded using the #
# reloadproperties variable. Once it is loaded, it remains #
# in memory until the application is restarted. #
# #
#################################################################
#realdatasource=/RealMySqlDS
#realdatasourceclass=com.mysql.jdbc.jdbc2.optional.MysqlDataSource

#################################################################
# DataSource properties #
# #
# If you are using the DataSource support to intercept calls #
# to a DataSource that requires properties for proper setup, #
# define those properties here. Use name value pairs, separate #
# the name and value with a semicolon, and separate the #
# pairs with commas. #
# #
# The example shown here is for mysql #
# #
#################################################################
#realdatasourceproperties=port;3306,serverName;ibmhost,databaseName;mydb


#################################################################
# JNDI DataSource lookup #
# #
# If you are using the DataSource support outside of an app #
# server, you will probably need to define the JNDI Context #
# environment. #
# #
# If the P6Spy code will be executing inside an app server then #
# do not use these properties, and the DataSource lookup will #
# use the naming context defined by the app server. #
# #
# The two standard elements of the naming environment are #
# jndicontextfactory and jndicontextproviderurl. If you need #
# additional elements, use the jndicontextcustom property. #
# You can define multiple properties in jndicontextcustom, #
# in name value pairs. Separate the name and value with a #
# semicolon, and separate the pairs with commas. #
# #
# The example shown here is for a standalone program running on #
# a machine that is also running JBoss, so the JDNI context #
# is configured for JBoss (3.0.4). #
# #
#################################################################
#jndicontextfactory=org.jnp.interfaces.NamingContextFactory
#jndicontextproviderurl=localhost:1099
#jndicontextcustom=java.naming.factory.url.pkgs;org.jboss.nameing:org.jnp.interfaces

#jndicontextfactory=com.ibm.websphere.naming.WsnInitialContextFactory
#jndicontextproviderurl=iiop://localhost:900
Then copy the driver

Tomcat

cp ~/liferay/ext/lib/development/p6spy.jar ~/liferay/tomcat-6.0.18/lib/ext
Jboss

cp ~/liferay/ext/lib/development/p6spy.jar ~/liferay/jboss-tomcat-5.0.0/server/default/lib/
Create the liferay datasources file

Tomcat
 
Edit file ~/liferay/tomcat-6.0.18/conf/context.xml


<Context>
...
<Resource name="jdbc/LiferayPool" auth="Container" type="javax.sql.DataSource" driverClassName="com.p6spy.engine.spy.P6SpyDriver" url="jdbc:mysql://localhost:3306/lportal?useUnicode=true&amp;characterEncoding=UTF-8&amp;useFastDateParsing=false" username="root" password="root" maxActive="100" maxIdle="30" maxWait="10000" />
...
Jboss

Delete ~/liferay/jboss-tomcat-5.0.0/server/default/deploy/hsqldb-ds.xml.
Edit file ~/liferay/jboss-tomcat-5.0.0/server/default/deploy/liferay-ds.xml

<?xml version="1.0" encoding="UTF-8"?>
<datasources>

    <local-tx-datasource>
        <jndi-name>jdbc/LiferayPool</jndi-name>
        <use-java-context>false</use-java-context>
             <connection-url>jdbc:mysql://localhost:3306/lportal?useUnicode=true&amp;characterEncoding=UTF-8&amp;useFastDateParsing=false</connection-url>

        <driver-class>com.p6spy.engine.spy.P6SpyDriver</driver-class>

        <user-name>root</user-name>
        <password>root</password>
        <min-pool-size>5</min-pool-size>
    </local-tx-datasource>
    <local-tx-datasource>
        <jndi-name>DefaultDS</jndi-name>
        <connection-url>jdbc:mysql://localhost:3306/lportal?useUnicode=true&amp;characterEncoding=UTF-8&amp;useFastDateParsing=false</connection-url>
        <driver-class>com.p6spy.engine.spy.P6SpyDriver</driver-class>
        <user-name>root</user-name>
        <password>root</password>
        <metadata>
          <type-mapping>mySQL</type-mapping>
        </metadata>
    </local-tx-datasource>
</datasources>

Modify/Create ~/liferay/ext/ext-impl/src/portal-developer.properties


jdbc.default.jndi.name=jdbc/LiferayPool
Stop server
Deploy using "ant deploy" from ~/liferay/ext
Start server and confirm file "spy.log" appears whenever the server was run from. I commonly enter the home directory and from there start the server. That will start the server from the user home directory. We can then look into the queries from spy.log file, created in the home directory:


tail -f ~/spy.log
4. Customize liferay copying any property from ~/liferay/portal/portal-impl/src/portal.properties and pasting it into ~/liferay/ext/ext-impl/classes/portal-ext.properties. For example to remove the captcha for new registrations use this property:


captcha.max.challenges=0
Below is an important one that removes the chat portlet. You better do it ASAP as it hits performance in your local environment


layout.static.portlets.all=
Run "ant deploy-properties" from ~/liferay/ext and restart the server (Changes through the extension environment require a server restart). Try opening an account to verify the captcha is not there. Another file that can be included to modify default Liferay behaviors is system.properties.

5. Be sure you have now a user account you have created through registration "create account". Do not forget to change the automatic generated password from "my account"

6. There is an ant task deploy-fast to deploy anything below ext-web/docroot into the app server however only tomcat comes predefined there.

JBoss

Go ahead and add the below for that ant task at /Users/nurquiza/liferay/ext/ext-impl/build-parent.xml

        <if>
            <equals arg1="${app.server.type}" arg2="jboss-tomcat" />
            <then>
                <copy todir="${app.server.portal.dir}">
                    <fileset dir="docroot">
                        <include name="**/*.css" />
                        <include name="**/*.dtd" />
                        <include name="**/*.gif" />
                        <include name="**/*.jar" />
                        <include name="**/*.jpg" />
                        <include name="**/*.js" />
                        <include name="**/*.jsp" />
                        <include name="**/*.jspf" />
                        <include name="**/*.png" />
                        <include name="**/*.properties" />
                        <include name="**/*.tld" />
                        <include name="**/*.vm" />
                        <include name="**/*.xml" />
                    </fileset>
                </copy>
            </then>
        </if>




6. From Eclipse create a new java project from existing source pointing to the "ext" folder.
Note: I do not like that much running the server inside eclipse, I prefer just to connect directly to the server debug port.

Tomcat

Add the below line to ~/liferay/tomcat-6.0.18/bin/setenv.sh


JAVA_OPTS="$JAVA_OPTS -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n"
JBoss

Uncomment the below line from /Users/nurquiza/liferay/jboss-tomcat-5.0.0/bin/run.conf


JAVA_OPTS="$JAVA_OPTS -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n"

You will need to restart the server for it to start listening on debug port = 8787
From Eclipse go to Menu|Run|Debug Configuration then right click on "Remote Java Application" and select "New". Select as name "local-liferay" for example. Select port 8787 and then hit "Apply". You should be able to see all current running in the server threads from Menu|Window|Open Perspective|Debug.

7. Create a new java project using plugin folder.

8. Let us customize an existing core portlet. Note that many core portlet use just JSP and helper Java code which does not really respect the MVC pattern. For example the "Dictionary" portlet View code can be seen from the portal source code at ~/liferay/portal/portal-web/docroot/html/portlet/dictionary. Let us copy view.jsp to ~/liferay/ext/ext-web/docroot/html/portlet/dictionary


cp ~/liferay/portal/portal-web/docroot/html/portlet/dictionary/view.jsp ~/liferay/ext/ext-web/docroot/html/portlet/dictionary/
Edit the JSP, run "ant deploy-fast" from ~/liferay/ext/ext-web. Login and confirm changes are there.

9. Plugin SDK Development can be used to develop 5 kind of plugins. They are themes, layouts, portlets, hooks and web plugins. The first two I already introduced in the first tutorial and now I will show how to develop a simple JSP portlet:


cd ~liferay/plugin/portlets
./create.sh plugin_sdk_sample_1 "My first plugin SDK Portlet"
cd plugin_sdk_sample_1-portlet/
ant deploy
At this point portlet "My first plugin SDK Portlet" can be included in any page fo the portal using "Add Application" from the Liferay Dock. The new portlet can be customized changing the view.jsp and the JSPPortlet.java
To use Eclipse you will need to update the "Java Build Path" to point to the src folder of the new plugin and any new plugin you want to build/debug from Eclipse.

10. Extension Development is used to develop and extend existing portlets. Even though the recommended way to develop portlets is using plugin SDK the reality is it has limitations that the extension environment does not have. Let us create a simple JSP portlet called ext_sample_1.
Edit /Users/nurquiza/liferay/ext/ext-web/docroot/WEB-INF/portlet-ext.xml

<portlet>
        <portlet-name>ext_sample_1</portlet-name>
        <display-name>Extension Sample Portlet # 1</display-name>
        <portlet-class>com.liferay.util.bridges.jsp.JSPPortlet</portlet-class>
        <init-param>
            <name>view-jsp</name>
            <value>/html/portlet/ext/ext_sample_1/view.jsp</value>
        </init-param>
        <expiration-cache>0</expiration-cache>
        <supports>
            <mime-type>text/html</mime-type>
        </supports>
        <resource-bundle>com.liferay.portlet.StrutsResourceBundle</resource-bundle>
        <security-role-ref>
            <role-name>power-user</role-name>
        </security-role-ref>
        <security-role-ref>
            <role-name>user</role-name>
        </security-role-ref>
  </portlet>




Edit ~/liferay/ext/ext-web/docroot/WEB-INF/liferay-portlet-ext.xml


<portlet>
   <portlet-name>ext_sample_1</portlet-name>
</portlet>


Edit ~/liferay/ext/ext-web/docroot/WEB-INF/liferay-display.xml


<category name="category.example">
  <portlet id="ext_sample_1" />
</category>



Add file ~/liferay/ext/ext-web/docroot/html/portlet/ext/ext_sample_1/view.jsp with content:


<h1>Hello Liferay extension environment!</h1>

Edit /Users/nurquiza/liferay/ext/ext-impl/src/content/Language-ext.properties


javax.portlet.title.ext_sample_1=Extension Sample Portlet # 1
category.example=Tutorial
Let us deploy the portlet:


cd ~/liferay/ext/
ant deploy
You could also use "ant deploy" from ext-web and from ext-impl.
this takes time. In my local machine 3567 files are copied into ROOT.war and on top of that the server needs to be restarted.
Select "Add application" from Liferay dock. Portlet "Extension Sample Portlet # 1" should show up below "Tutorial" category
11. Let us create now a Struts portlet. Let us call it ext_sample_2:

Edit /Users/nurquiza/liferay/ext/ext-web/docroot/WEB-INF/portlet-ext.xml

<portlet>
                <portlet-name>ext_sample_2</portlet-name>
                <display-name>Extension Sample Portlet # 2 (Struts)</display-name>
                <portlet-class>com.liferay.portlet.StrutsPortlet</portlet-class>
                <init-param>
                    <name>view-action</name>
                    <value>/ext/ext_sample_2/view</value>
                </init-param>
                <expiration-cache>0</expiration-cache>
                <supports>
                    <mime-type>text/html</mime-type>
                </supports>
                <resource-bundle>com.liferay.portlet.StrutsResourceBundle</resource-bundle>
                <security-role-ref>
                    <role-name>power-user</role-name>
                </security-role-ref>
                <security-role-ref>
                    <role-name>user</role-name>
                </security-role-ref>
</portlet>




Edit ~/liferay/ext/ext-web/docroot/WEB-INF/liferay-portlet-ext.xml


<portlet>
   <portlet-name>ext_sample_2</portlet-name>
   <struts-path>ext/ext_sample_2</struts-path>
   <use-default-template>false</use-default-template>
</portlet>


Edit ~/liferay/ext/ext-web/docroot/WEB-INF/liferay-display.xml


<category name="category.tutorial">
  <portlet id="ext_sample_2" />
</category>



Edit ~/liferay/ext/ext-web/docroot/WEB-INF/struts-config.xml


<action path="/ext/ext_sample_2/view" forward="portlet.ext.ext_sample_2.view" />


Edit ~/liferay/ext/ext-web/docroot/WEB-INF/tiles-defs.xml


<definition name="portlet.ext.ext_sample_2" extends="portlet" />
<definition name="portlet.ext.ext_sample_2.view" extends="portlet.ext.ext_sample_2">
  <put name="portlet_content" value="/portlet/ext/ext_sample_2/view.jsp" />
</definition>


Create file ~/liferay/ext/ext-web/docroot/html/portlet/ext/ext_sample_2/init.jsp


<%@ include file="/html/portlet/init.jsp" %>
Create file ~/liferay/ext/ext-web/docroot/html/portlet/ext/ext_sample_2/view.jsp


<%@ include file="/html/portlet/ext/ext_sample_2/init.jsp" %>
<h1>Hello Liferay extension environment (From Struts)!</h1>

Edit /Users/nurquiza/liferay/ext/ext-impl/src/content/Language-ext.properties


javax.portlet.title.ext_sample_2=Extension Sample Portlet # 2 (Struts)
category.tutorial=Tutorial
Let us deploy the portlet:


cd ~/liferay/ext/
ant deploy
12. Any further development you do you will need the portal source so go ahead and download liferay-portal-src-5.2.3.zip and uncompress it to ~/liferay/portal, then create a Java project in eclipse pointing to that folder. All liferay sources are there so you can debug your projects even navigating through the core implementation. It is not a bad idea to point directly to the official SVN TAG repository so you are aware right away if the portal source is locally modified by accident. Remember, all changes must be done from the extension environment and never from the portal source.

13. Existing plugin projects can be hosted in their own project SVN paths, you just need to check those projects into the proper plugin-sdk folder. Do not keep all binary files on SVN. I still do not understand why Liferay does not support maven out of the box.

14. Extension projects most likely will be composed of subfolders of ext-impl, ext-service and ext-web. Try keeping those folders *only* on SVN to minimize download time per project. It is again a very bad idea to host binary files on SVN, especially JAR files. Maven should come to our rescue for this matter.

1 comment:

Rose said...

really very nice information sharing and i am looking for such type of informative news and i get through this blog so i am very much thankful to you.
- liferay development

Followers