Friday, March 23, 2012

Liferay's 3 Servlets Musketeers (PortalActionServlet, PortalServlet and MainServlet)

MainServlet

This is the main servlet in Liferay, it handles all request directed to the portal itself, mapped to '/c/*' as in Liferay's ROOT web.xml. Together with various FriendlyURLServlet mappings in web.xml, they almost handle all the happenings in Liferay. FriendlyURLServlet normally analyze friendly urls and do a redirect after regenerating friendly urls back to the complex form that Liferay happens to understand through PortalUtil

PortletServlet

This is the servlet that will get autogenerated during deployment of Portlet Plugins WAR file. It is really just a simple proxy pumping in resources to your portlet. Checkout PortletAutoDeployer on how it is written to your Portlet Plugin WAR's web.xml during hot deployment. Remember that the whole Portlet framework is after all based on Servlet specs, this Servlet gives Liferay a way to push through resources to a specific portlet.

PortalActionServlet

In short, forget about this Servlet, it is a legacy Servlet as far as my understanding goes. Why? All it does is make sure a copy of PortletRequestProcessor (sounds familiar? yes, its an extension of Struts's RequestProcessor) is available in ServletContext under WebKey.PORTLET_STRUTS_PROCESSOR. This is now (Liferay 6.1.x) being taken care of in MainServlet.checkRequestProcessor method. Think of it this way. Where does your portlet lives? Inside a Portal container of course. Any request to your portlet have to go through a Portal container first, and that means going through MainServlet which will make sure a copy of PortletRequestProcessor is available nicely before eventually hitting your portlet. Humbly think that this is a nicer way rather than having each Portlet WAR file declaring a PortletActionServlet, cause the following is how PortletActionServlet stick a copy of PortletRequestProcessor into ServletContext

                ServletContext servletContext = getServletContext();

                ModuleConfig moduleConfig =
                        (ModuleConfig)servletContext.getAttribute(Globals.MODULE_KEY);

                PortletRequestProcessor portletRequestProcessor =
                        PortletRequestProcessor.getInstance(this, moduleConfig);

                servletContext.setAttribute(
                        WebKeys.PORTLET_STRUTS_PROCESSOR, portletRequestProcessor);
It's coupling in Struts's specific way of looking up ModuleConfig into it's code, which arguable isn't going to change much in the near future anyway but still ... I think it's tight coupling

This is how it's done in MainServlet.checkPortletRequestProcessor(...)

                ServletContext servletContext = getServletContext();

                PortletRequestProcessor portletReqProcessor =
                        (PortletRequestProcessor)servletContext.getAttribute(
                             WebKeys.PORTLET_STRUTS_PROCESSOR);

                if (portletReqProcessor == null) {
                        ModuleConfig moduleConfig = getModuleConfig(request);

                        portletReqProcessor =
                              PortletRequestProcessor.getInstance(this, moduleConfig);

                        servletContext.setAttribute(
                              WebKeys.PORTLET_STRUTS_PROCESSOR, portletReqProcessor);
  }

which is doing it through getModuleConfig(request) methods relying on Struts's ActionServlet to provide the implementation and hence abstracting it away from potential future changes in implementation

This are really just my understandings on briefly digging into Liferay's source through occasionally a VI on a terminal over a glass of red or coffee during spare times over lunch break or weekends. If you disagree in anyway, I'd love to be corrected.

Anyway Liferay looks like a promising portal solution and I would definitely be exploring further

Monday, March 12, 2012

Ever wonder how Liferay keeps track of it's WAR'ed Portlets

Liferay is a portlet container. With the distribution that comes with Tomcat bundled in by default, it deployed it's main portal into tomcat's ROOT. This main portal will have all the bits and pieces to manage portlets deployed subsequently as WAR files, which without much surprised gets deployed into separate webapps with it's own application context default to the war file name being dumped into Liferay's deploy directory, conveniently located one level above the tomcat home within Liferay distribution. To manage those Portlets located under different webapps, Liferay needs some way to tie the Portlet's Servlet path to it's ServletContext.

Ever wonder how Liferay does this?

Have the opportunity to dig around a bit with Liferay's code and guess what, this is how Liferay does it. It has a ServletContextPool which is basically something like a static factory singleton with a bunch of public static methods accessing a single instance of itself. Servlet corresponding to a particular Portlet when initialized will just make use of those relevant static methods to stick it's context path and ServletContext into a map.

You might wonder how is this possible when all webapps are being deployed to have it's own ClassLoader. Which means webapp1 will have it's own copy of ServletContextPool which will be separate instance from webapp2. Although they are accessing static methods they are still separate instance due to webapp1 and webapp2 have separate classloaders that do not allow them to see each other's classes.

But ClassLoaders are hierarchical and most app servers will eventually inherit a global ClassLoader, Tomcat is not difference. What Liferay does is it package ServetContextPool into portal-service.jar which unsurprisingly located in Tomcat's lib/ext folder which happens to be Tomcat's glabal classpath. Jars located there will be visible to all web application.

Interesting. Pretty simple and straight forward way of getting it done. Does this kind of implementation sounds familiar? Ring any bells? I'm guessing this is somewhat similar way SLF4j (Standard Log4j) decide which logging implementation to hook up in runtime.

Friday, March 9, 2012

Ouch!! ... mvn liferay:build-service hurts ...

I've been trying to generate bunch of services using

    mvn liferay:build-service -P liferay-plugins-development
where liferay-plugins-development profile contains properties setting of my liferay version and deployment directory, and was bumped with the following exception
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 17.172s
[INFO] Finished at: Sat Mar 10 16:50:43 EST 2012
[INFO] Final Memory: 4M/15M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal com.liferay.maven.plugins:liferay-maven-plugin:6.1.0:build-service (default-cli) on project myliferay-portlet: Execution default-cli of goal com.liferay.maven.plugins:liferay-maven-plugin:6.1.0:build-service failed: An API incompatibility was encountered while executing com.liferay.maven.plugins:liferay-maven-plugin:6.1.0:build-service: java.lang.AbstractMethodError:
 com.liferay.util.sl4fj.LiferayLoggerAdapter.log(Lorg/slf4j/Marker;Ljava/lang/String;ILjava/lang/String;Ljav/lang/Throwable;)V
[ERROR] -----------------------------------------------------
[ERROR] realm =    plugin>com.liferay.maven.plugins:liferay-maven-plugin:6.1.0
[ERROR] strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy
[ERROR] urls[0] = file:/...../maven_repo/com/liferay/maven/plugins/liferay-maven-plugin/6.1.0/liferay-maven-plugin-6.1.0.jar
[ERROR] urls[1] = file:/...../maven_repo/com/liferay/portal/portal-impl/6.1.0/portal-impl-6.1.0.jar
Suspect this has something to do with different version of SLF4j that is not compatible with the one that maven resolved to when running liferay-maven-plugin, so went ahead and force the SLF4j version that the plugin was using to 1.5.5
 ...
  
    
      
         com.liferay.maven.plugins
         liferay-maven-plugin
         ${liferay.version}
         
            ${liferay.auto.deploy.dir}
            ${liferay.version}
            portlet
         
         
           
              org.slf4j
              slf4j-log4j12
              1.5.5
           
         
       
  ...
  
 ...
It seems to fix this issue, but now I'm bumped with another NullPointerException coming off liferay-maven-plugin source itself. Logged a defect with the Liferay team here (MAVEN-15). Will have to wait and see how this paints out.

Liferay-maven-plugin turns out to be quite a pain in the neck for me ... :-( Guess I should be evaluating Liferay IDE instead to see if it suits me better. It does required that the whole plugins project to be under a Liferay-Plugin-SDK directory though.

Monday, March 5, 2012

Liferay's Maven Archetypes

Been googling a bit for Liferay maven archetypes available, well typically for 6.1.0 as pre 6.1.0 does not have any maven archetypes in public repositories, and they aren't just listed in one web page. Putting them all down here in one place just so it's easier for whoever that's googling for this piece of information. This is the official Liferay blog that list down all the possible Liferay Maven archetypes, which are :-

  • liferay-ext-archetype
  • liferay-hook-archetype
  • liferay-layouttpl-archetype
  • liferay-portlet-archetype
  • liferay-theme-archetype
  • liferay-web-archetype
with the following properties
 liferay.version
 liferay.auto.deploy.dir
through my profile settings in ~/.m2/settings.xml

  ...
  
    ...
    
       liferay-plugins-development
       
          ...
          ...
       
    
    ...
  
  ...

with the mvn command to invoke them as follows
mvn archetype:generate 
         -DarchetypeArtifactId=liferay-portlet-archetype 
         -DarchetypeGroupId=com.liferay.maven.archetypes 
         -DarchetypeVersion=6.1.0 
         -DarchetypeCatalog=local,remote 
         -DartifactId=sample-portlet 
         -DgroupId=com.liferay.sample 
         -Dversion=1.0-SNAPSHOT
and the following mvn command just to install the whatever resultant artifacts using the 'liferay-plugins-development' profile
mvn install -P liferay-plugins-development

Tuesday, February 28, 2012

Bash command that save my ass over and over again

The following linux commands are really no brainer stuff that every elementary course would have taught us, and it saved my butt many times over and over again.

   find . -iname <some file name>
To find files recursively

   find . -iname <some file name> | xargs
To find files recursively and list them out horizontally separated by space and doing escaping when necessary

  find . -iname <some file name> | xargs <some other bash commands>
To find files recursively and list them out as arguments such that we could perform other commands on it like rm -f (force delete) for example

  grep -ir 'some text' .
Find all files recursively starting with the current directory

We could do interesting stuff like

  find . -iname '*.xml' | xargs grep -i "testing"
Find all xml files recursively starting with current directory that has text 'testing' contains in it.

Bash commands ROCKS! It's always worthwhile if you are using a windows machine to install Cygwin, it comes with bash by default so you could dig up code segment quickly rather than relying on the pathetic doggy search that comes with Windows. Just me 2 cents !

Tuesday, December 20, 2011

Sample XiangQi (Chinese Chess) written with PlayN

Got a bit of time and start off writing a simple sample app of XiangQi (Chiness Chess in PlayN), following is a few screenshots of it.

Its a simple 2 player game, using simple json to transfer data between client and server running off servlet in GAE

I think PlayN is a framework with pretty good potential, just like any new framework it needs time to mature and settle in I guess. Too bad the apk generated doesn't run on an emulator, I'll need to find an actual device to try things out :-(

Picture 1
Picture 2



PlayN will not run in Android Emulator :-(

Tried deployed one of the simple game wrote using PlayN and was popped up with this
12-21 03:58:12.579: ERROR/AndroidRuntime(372): FATAL EXCEPTION: GLThread 10
12-21 03:58:12.579: ERROR/AndroidRuntime(372): java.lang.IllegalArgumentException: No configs match configSpec
12-21 03:58:12.579: ERROR/AndroidRuntime(372):     at android.opengl.GLSurfaceView$BaseConfigChooser.chooseConfig(GLSurfaceView.java:825)
12-21 03:58:12.579: ERROR/AndroidRuntime(372):     at android.opengl.GLSurfaceView$EglHelper.start(GLSurfaceView.java:981)
12-21 03:58:12.579: ERROR/AndroidRuntime(372):     at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1326)
12-21 03:58:12.579: ERROR/AndroidRuntime(372):     at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1180)
12-21 03:58:12.608: WARN/ActivityManager(75):   Force finishing activity com.tmjee.playn.xiangqi.android/.XiangqiActivity
12-21 03:58:12.618: WARN/WindowManager(75): Failure taking screenshot for (216x135) to layer 21030
Oh my god, looks like games written in PlayN cannot be tested in an emulator :-( looks like the emulator is not supporting the necessary OpenGL stuff .... Ouch !!! ....