Pages

Wednesday 5 March 2014

Page Rendering/Loading in LIFERAY | Flow of Liferay Portal Server (TOMCAT)



Pages are rendered via the following mechanism:
  1. Request comes in through MainServlet. Various attributes are stored in the session and request. The request attribute WebKeys.CURRENT_URL holds the currently requested path (stripped of protocol,host, and port)
  2. The "service pre action" handler com.liferay.portal.events.ServicePreAction is called, where the layout and theme to display are determined. The current layout is stored as a request attribute under the key WebKeys.LAYOUT. The other available layouts are stored as a request attribute under the key WebKeys.LAYOUTS. The theme to display is stored as a request attribute under the WebKeys.THEME key, and the color scheme to use is stored as the request attribute WebKeys.COLOR_SCHEME. The theme and color scheme are usually determined by querying the layout once it has been determined.
  3. Struts is called to handle the request, and Liferay uses the custom Struts request processor com.liferay.portal.struts.PortalRequestProcessor. The PortalRequestProcessor.getLastPath() method computes the last path visited, and supplies a default path for the first entry into the portal. The default path upon entry into the portal is <protocol>://<hostName>:<port>/portal/layout. This url may optionally contain the query parameters ?p_l_id=default if the user is known (i.e. they have just logged in).
  4. This initial request for /portal/layout is handled by com.liferay.portal.action.LayoutAction.
  5. /html/common/themes/portal.jsp is the "top level" display page. It selects...
  6. the portal_normal.jsp or portal_pop_up.jsp based on the current state of the theme's display, and includes it using the <liferay-theme:include> custom tag, implemented by com.liferay.taglib.theme.IncludeTag, which in turn calls com.liferay.taglib.util.ThemeUtil.include().
  7. Eventually, for each portlet, the method com.liferay.portal.util.PortalUtil.renderPortlet() gets called, which calls
  8. /portal/portal-web/docroot/html/portal/render_portlet.jsp to render the contents of an individual portlet. That in turn calls
  9. /portal/portal-web/docroot/html/common/themes/portlet.jsp, which ends up calling
  10. /portal/portal-web/docroot/html/common/themes/portlet_content.jsp

The "Liferay.Store" component (JavaScript )




Since AlloyUI became a base component for Liferay, various amazing tools became available for the Liferay developer.
One of these are the JavaScript components such as Liferay.Store. This component allows the developer to store user-related data quickly on the server. To see how it works, let's consider a portlet which stores how the user prefers her coffee: black, sweet or with milk. We can just create a select input with the options (github):
How do you like your coffee? 

<select id="<portlet:namespace/>coffeePref" name="<portlet:namespace/>coffeePref"> 
    <option value="black">Black</option> 
    <option value="sweet">Sweet</option> 
    <option value="milk">With milk</option>
</select>
As one would expect, the choice will be lost when the page is reloaded. Here enters  Liferay.Store: it asynchronously saves any information on the server, relating it to the authenticated user. In this case, when the select input changes we will store the new value. First, we open a new <aui:scipt> element, which should declare the use of the liferay-store module:
<aui:script use="liferay-store">

</aui:script>
In it, we retrieve the input using the YUI API and bind a function to its change event:
<aui:script use="liferay-store">
    A.one('#<portlet:namespace/>coffeePref').on(
        'change',
        function(event) { }
    );
</aui:script>
Now comes the beef: the function retrieves the current value of the select input and stores it using the Liferay.Store component (github):
<aui:script use="liferay-store">
    A.one('#<portlet:namespace/>coffeePref').on(
        'change',
        function(event) {
            var instance = this;

            Liferay.Store('<portlet:namespace/>coffee-preference', instance.val());
        }
    );
</aui:script>
In this case, we store the value of the input in a key composed by the portlet namespase followed by the coffee-preference suffix.
One can retrieve this value from JavaScript but it is a common pattern to fetch it on the server side, since it avoids unnecessary requests and slowdowns. This is what we'll do; for that, we use the get() method from the com.liferay.portal.util.SessionClicks class:
<%

String coffeePreference = SessionClicks.get(request, renderResponse.getNamespace() + "coffee-preference", "");

%>
Now, we can use the retrieved value to verify which option was selected - e.g. this way (github):
<select id="<portlet:namespace/>coffeePref" name="<portlet:namespace/>coffeePref">
    <option value="black" <%= "black".equals(coffeePreference) ? "selected" : "" %>>Black</option>
        <option value="sweet" <%= "sweet".equals(coffeePreference) ? "selected" : "" %>>Sweet</option>
    <option value="milk"  <%= "milk".equals(coffeePreference) ? "selected" : "" %>>With milk</option>
</select>
This is a rather simple process to save small, user-bound bits of data asynchronously and can save a lot of time. The full code can be found on GitHub and BitBucket, if you'd like to see the example running.


Service Builder & Generated classes(relationships)



While working on the Service Builder in Liferay it’s good to have a clear picture of what all classes are generated by the Service Builder, how these classes are related to each other, where can I put my custom code, how can I invoke persistence layer methods.
In this post I intend to answer some of the questions in a very simplistic view – the very basics in plain English.
I’ll take an example. I have created a very basic service.xml.


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.0.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_0_0.dtd">
<service-builder package-path="com.ab">
      <author>gaurshar</author>
      <namespace>B</namespace>

     
      <entity name="Student" local-service="true" remote-service="false">
            <column name="studentId" type="long" primary="true" id-type="class" id-param="com.liferay.counter.service.persistence.IDGenerator"></column>
          <column name="studentName" type="String"></column>
            <column name="classId" type="long"/>    
             <finder return-type="Collection" name="StudentName">
          <finder-column name="studentName"></finder-column>
       </finder>
      </entity>
     
      <entity name="Classroom" local-service="true" remote-service="false">
            <column name="classId" type="long" primary="true" id-type="class" id-param="com.liferay.counter.service.persistence.IDGenerator"></column>
          <column name="className" type="String"></column>
          <column name="student" type="Collection" entity="Student" mapping-key="studentId"></column>
           <finder return-type="Collection" name="ClassName">
          <finder-column name="className"></finder-column>
       </finder>
      </entity>
     
</service-builder>

Few points to highlight –
1) There are two entities – Student and Classroom
2) There is a one-to-many relation defined between Classroom and Student
3) There are few finder methods defined

When we build the service through Service Builder, the framework generates a set of classes as depicted in the diagram below. 





Few things to note here –

1) All the column name mappings, getters and setters are defined in ClassroomLocalServiceBaseImpl.

2) ClassroomLocalServiceImpl does not contain any method by default and should contain any custom methods (more on it sortly…)

3) Custom service classes, jsps, controllers should use ClassroomLocalServiceUtil class for accessing any service, persistence related methods.

This class should be the single point of contact for our code .

4) There is a parallel structure for Persistence layer generated by Service Builder. There is an abstraction in terms of ClassroomUtil that wraps all the methods of the persistence layer and provides a single point of contact for Service Layer classes.

5) We should not directly invoke methods of any of the persistence layer classes including the ClassroomUtil class.

 If we need to invoke a method of ClassroomUtil, we must do the following –
  •  Taking the above example, we have a method in ClassroomUtil – getStudents(long pk) that returns all the students in a class. We should create a method ‘getStudents’ in ClassroomLocalServiceImpl (class where all the custom methods are written). This method calls ClassroomUtil.getStudents() method
  •  Regenerate the service classes using Service Builder. This regenerates the service layer classes and creates a method getStudents() in ClassroomLocalServiceUtil. This can now be invoked from custom code.
     Remember the bottom line is ‘ClassroomLocalServiceUtil’ should be the single point of contact for custom code.

Invoking persistence layer methods from Service layer sets up transaction context and hibernate sessions. 


Thanks
Srikanth