JBoss.orgCommunity Documentation

Chapter 3. Portal Development

3.1. Skinning the portal
3.1.1. Overview
3.1.2. Skin Components
3.1.3. Skin Selection
3.1.4. Skins in Page Markups
3.1.5. The Skin Service
3.1.6. The Default Skin
3.1.7. Creating New Skins
3.1.8. Tips and Tricks
3.2. Portal Lifecycle
3.2.1. Overview
3.2.2. Application Server start and stop
3.2.3. The Command Servlet
3.3. Default Portal Configuration
3.3.1. Overview
3.3.2. Configuration
3.4. Portal Default Permission Configuration
3.4.1. Overview
3.4.2. Overwrite Portal Default Permissions
3.5. Portal Navigation Configuration
3.5.1. Overview
3.5.2. Portal Navigation
3.5.3. Group Navigation
3.5.4. User Navigation
3.6. Data Import Strategy
3.6.1. Introduction
3.6.2. Import Mode
3.6.3. Data Import Strategy
3.7. Internationalization Configuration
3.7.1. Overview
3.7.2. Locales configuration
3.7.3. ResourceBundleService
3.7.4. Navigation Resource Bundles
3.7.5. Portlets
3.7.6. Translating the language selection form
3.8. Pluggable Locale Policy
3.8.1. LocalePolicy API
3.8.2. Default LocalePolicy
3.8.3. Custom LocalePolicy
3.8.4. LocalePolicy Configuration
3.8.5. Keeping non-bridged resources in sync with current Locale
3.9. RTL (Right To Left) Framework
3.9.1. Groovy templates
3.9.2. Stylesheet
3.9.3. Images
3.9.4. Client side JavaScript
3.10. XML Resources Bundles
3.10.1. Motivation
3.10.2. XML format
3.10.3. Portal support
3.11. JavaScript Inter Application Communication
3.11.1. Overview
3.11.2. Library
3.11.3. Syntax
3.11.4. Example of Javascript events usage
3.12. Upload Component
3.12.1. Upload Service
3.13. Deactivation of the Ajax Loading Mask Layer
3.13.1. Purpose
3.13.2. Synchronous issue
3.14. Javascript Configuration
3.15. Navigation Controller
3.15.1. Description
3.15.2. Controller in Action
3.15.3. Integrate to GateIn WebUI framework
3.15.4. Changes and migration from GateIn 3.1.x

The complete skinning of a page can be decomposed into three main parts:

A GateIn 3.2 skin contains css styles for the portal's components but also shares components that may be reused in portlets. When GateIn 3.2 generates a portal page markup, it inserts stylesheet links in the page's head tag.

There are two main types of css links that will appear in the head tag: a link to the portal skin css file and a link to the portlet skin css files.

In the code fragment below you can see the two types of links:


<head>
...
<!-- The portal skin -->
<link id="CoreSkin" rel="stylesheet" type="text/css" href="/eXoResources/skin/Stylesheet.css" />

<!-- The portlet skins -->
<link id="web_FooterPortlet" rel="stylesheet" type="text/css" href= "/web/skin/portal/webui/component/UIFooterPortlet/DefaultStylesheet.css" />
<link id="web_NavigationPortlet" rel="stylesheet" type="text/css" href= "/web/skin/portal/webui/component/UINavigationPortlet/DefaultStylesheet.css" />
<link id="web_HomePagePortlet" rel="stylesheet" type="text/css" href= "/portal/templates/skin/webui/component/UIHomePagePortlet/DefaultStylesheet.css" />
<link id="web_BannerPortlet" rel="stylesheet" type="text/css" href= "/web/skin/portal/webui/component/UIBannerPortlet/DefaultStylesheet.css" />
...
</head>

The skin service is a GateIn 3.2 service which manages the various types of skins. It is reponsible for discovering and deploying the skins into the portal.

The default skin for GateIn 3.2 is located as part of the 01eXoResource.war. The main files associated with the skin is show below:

WEB-INF/gatein(1)-resources.xml
WEB-INF/web.xm(2)l
skin/Styleshee(3)t.css

1

gatein-resources.xml: defines the skin setup to use

2

web.xml: contains the resource filer and has the display-name set

3

Stylesheet.css: contains the CSS class definitions for this skin.

gatein-resources.xml

For the default portal skin, this file contains definitions for the portal skin, the window decorations that this skin provides and well as defining some javascript resources which are not related to the skin. The default portal skin doesn't directly define portlet skins, these should be provided by the portlets themeselves.

web.xml

For the default portal skin, the web.xml of the eXoResources.war will contains a lot of information which is mostly irrelevant to the portal skining. The areas of interest in this file is the resourcerequestfilter and the fact that the display-name is set.

Stylesheet.css

The main portal skin stylesheet. The file is the main entry point to the css class definitions for the skin. Below is shown the contents of this file:

Instead of defining all the CSS classes in this one file we are instead importing other css stylesheet files, some of which may also import other CSS stylesheets. The css classes are split up between multiple files to make it easier for new skins to reuse parts of the default skin.

To reuse a CSS stylesheet from the default portal skin you would need to reference the default skin from eXoResources. For example, to include the window decorators from the default skin within a new portal skin you would need to use this import:

@import url(/eXoResources/skin/Portlet/Stylesheet.css);

A new portal will need to be added to the portal through the skin service. As such the web application which contains the skin will need to be properly configured for the skin service to discover them. This means properly configuring the ResourceRequestFilter and gatein-resources.xml.

Window styles are the CSS applied to window decoration. When an administrator choose a new application to add on a page he can decide which style of decoration would go around the window if any.

In order for the skin service to display the window decorators, it must have CSS classes with specific naming in relation to the window style name. The service will try and display css based on this naming. The css class must be included as part of the current portal skin for the window decorators to be displayed.

The location of the window decorator css classes for the default portal theme is located at:

01eXoResources.war/skin/PortletThemes/Stylesheet.css

Create the CSS file:

/*---- MyTheme ----*/
.MyTheme .WindowBarCenter .WindowPortletInfo {
	margin-right: 80px; /* orientation=lt */
	margin-left: 80px; /* orientation=rt */
}

.MyTheme .WindowBarCenter .ControlIcon {
	float: right; /* orientation=lt */
	float: left; /* orientation=rt */
	width: 24px; 
	height: 17px;
	cursor: pointer;
	background-image: url('background/MyTheme.png');
}

.MyTheme .ArrowDownIcon {
	background-position: center 20px;
}

.MyTheme .OverArrowDownIcon {
	background-position: center 116px;
}

.MyTheme .MinimizedIcon {
	background-position: center 44px;
}

.MyTheme .OverMinimizedIcon {
	background-position: center 140px;
}

.MyTheme .MaximizedIcon {
	background-position: center 68px;
}

.MyTheme .OverMaximizedIcon {
	background-position: center 164px;
}

.MyTheme .RestoreIcon {
	background-position: center 92px;
}

.MyTheme .OverRestoreIcon {
	background-position: center 188px;
}

.MyTheme .NormalIcon {
	background-position: center 92px;
}

.MyTheme .OverNormalIcon {
	background-position: center 188px;
}

.MyTheme .Information {
	height: 18px; line-height: 18px;
	vertical-align: middle; font-size: 10px;
	padding-left: 5px; /* orientation=lt */
	padding-right: 5px; /* orientation=rt */
	margin-right: 18px; /* orientation=lt */
	margin-left: 18px; /* orientation=rt */
}

.MyTheme .WindowBarCenter .WindowPortletIcon {
	background-position: left top; /* orientation=lt */
	background-position: right top; /* orientation=rt */
	padding-left: 20px; /* orientation=lt */
	padding-right: 20px; /* orientation=rt */
	height: 16px;
	line-height: 16px;
}

.MyTheme .WindowBarCenter .PortletName {
	font-weight: bold;
	color: #333333;
	overflow: hidden;
	white-space: nowrap;
}

.MyTheme .WindowBarLeft {
	padding-left: 12px;
	background-image: url('background/MyTheme.png');
	background-repeat: no-repeat;
	background-position: left -148px;
}

.MyTheme .WindowBarRight {
	padding-right: 11px;
	background-image: url('background/MyTheme.png');
	background-repeat: no-repeat;
	background-position: right -119px;
}

.MyTheme .WindowBarCenter {
	background-image: url('background/MyTheme.png');
	background-repeat: repeat-x;
	background-position: left -90px;
	height: 21px;
	padding-top: 8px;
}

.MyTheme .MiddleDecoratorLeft {
	padding-left: 12px;
	background: url('background/MMyTheme.png') repeat-y left;
}

.MyTheme .MiddleDecoratorRight {
	padding-right: 11px;
	background: url('background/MMyTheme.png') repeat-y right;
}

.MyTheme .MiddleDecoratorCenter {
	background: #ffffff;
}

.MyTheme .BottomDecoratorLeft {
	padding-left: 12px;
	background-image: url('background/MyTheme.png');
	background-repeat: no-repeat;
	background-position: left -60px;
}

.MyTheme .BottomDecoratorRight {
	padding-right: 11px;
	background-image: url('background/MyTheme.png');
	background-repeat: no-repeat;
	background-position: right -30px;
}

.MyTheme .BottomDecoratorCenter {
	background-image: url('background/MyTheme.png');
	background-repeat: repeat-x;
	background-position: left top;
	height: 30px;
}

Portlets often require additional styles that may not be defined by the portal skin. GateIn 3.2 allows portlets to define additional stylesheets for each portlet and will append the corresponding link tags to the head.

The link ID will be of the form {portletAppName}{PortletName}. For example: ContentPortlet in content.war, will give id="contentContentPortlet"

To define a new CSS file to include whenever a portlet is available on a portal page, the following fragment needs to be added in gatein-resources.xml

<portlet-skin>
  <application-name>portletAppName</application-name>
  <portlet-name>PortletName</portlet-name>
  <skin-name>Default</skin-name>
  <css-path>/skin/DefaultStylesheet.css</css-path>
</portlet-skin>

<portlet-skin>
  <application-name>portletAppName</application-name>
  <portlet-name>PortletName</portlet-name>
  <skin-name>OtherSkin</skin-name>
  <css-path>/skin/OtherSkinStylesheet.css</css-path>
</portlet-skin>

This will load the DefaultStylesheet.css when the Default skin is used and the OtherSkinStylesheet.css when the OtherSkin is used.

The servlet is the main entry point for incoming requests, it also includes some init code when the portal is launched. This servlet (org.gatein.wci.command.CommandServlet) is automatically added during deployment and mapped to /tomcatgateinservlet.

This is equivalent to adding the following into web.xml.



<servlet>
  <servlet-name>TomcatGateInServlet</servlet-name>
  <servlet-class>org.gatein.wci.command.CommandServlet</servlet-class>
  <load-on-startup>0</load-on-startup>
</servlet>
  
<servlet-mapping>
  <servlet-name>TomcatGateInServlet</servlet-name>
  <url-pattern>/tomcatgateinservlet</url-pattern>
</servlet-mapping>

It is possible to filter on the CommandServlet by filtering the URL pattern used by the Servlet mapping.

The example below would create a servlet filter that calculates the time of execution of a portlet request.

The filter class:



package org.example;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class MyFilter implements javax.servlet.Filter {
  public void doFilter(ServletRequest request, ServletResponse response,
      FilterChain chain) throws IOException, ServletException
  {
    long beforeTime = System.currentTimeMillis();
    chain.doFilter(request, response);
    long afterTime = System.currentTimeMillis();
    System.out.println("Time to execute the portlet request (in ms): " + (afterTime - beforeTime));
  }
  public void init(FilterConfig config) throws ServletException
  {
  }
  public void destroy()
  {
  }
}

The Java EE web application configuration file (web.xml) of the portlet on which we want to know the time to serve a portlet request. As mentioned above nothing specific to GateIn 3.2 needs to be included, only the URL pattern to set has to be known.



<?xml version="1.0"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.5">
        
  <filter>
    <filter-name>MyFilter</filter-name>
    <filter-class>org.example.MyFilter</filter-class>        
  </filter>

  <filter-mapping>
    <filter-name>MyFilter</filter-name>
    <url-pattern>/tomcatgateinservlet</url-pattern>
    <dispatcher>INCLUDE</dispatcher>  
  </filter-mapping>    
    
</web-app>

The default permission configuration for the portal is defined through org.exoplatform.portal.config.UserACL component configuration in the file 02portal.war:/WEB-INF/conf/portal/portal-configuration.xml.

It defines 8 permissions types:


<component>
  <key>org.exoplatform.portal.config.UserACL</key>
  <type>org.exoplatform.portal.config.UserACL</type>   
  <init-params>      
    <value-param>
      <name>super.user</name>
      <description>administrator</description>
      <value>root</value>     
    </value-param>
      
    <value-param>
      <name>portal.creator.groups</name>
      <description>groups with membership type have permission to manage portal</description>
      <value>*:/platform/administrators,*:/organization/management/executive-board</value>     
    </value-param>
      
    <value-param>
      <name>navigation.creator.membership.type</name>
      <description>specific membership type have full permission with group navigation</description>
      <value>manager</value>     
    </value-param>
    <value-param>
      <name>guests.group</name>
      <description>guests group</description>
      <value>/platform/guests</value>     
    </value-param>     
    <value-param>
      <name>access.control.workspace</name>
      <description>groups with memberships that have the right to access the User Control Workspace</description>
      <value>*:/platform/administrators,*:/organization/management/executive-board</value>     
    </value-param>           
  </init-params>   
</component>

There are three navigation types available to portal users:

These navigations are configured using the standard XML syntax in the file; "02portal.war:/WEB-INF/conf/portal/portal-configuration.xml".


<component>
   <key>org.exoplatform.portal.config.UserPortalConfigService</key>
   <type>org.exoplatform.portal.config.UserPortalConfigService</type>
   <component-plugins>
      <component-plugin>
         <name>new.portal.config.user.listener</name>
         <set-method>initListener</set-method>
         <type>org.exoplatform.portal.config.NewPortalConfigListener
      </type>
         <description>this listener init the portal configuration
      </description>
         <init-params>
            <value-param>
               <name>default.portal</name>
               <description>The default portal for checking db is empty or not</description>
               <value>classic</value>
            </value-param>
            <value-param>
               <name>page.templates.location</name>
               <description>the path to the location that contains Page templates</description>
               <value>war:/conf/portal/template/pages</value>
            </value-param>
            <value-param>
               <name>override</name>
               <description>The flag parameter to decide if portal metadata is overriden on restarting server
            </description>
               <value>false</value>
            </value-param>
            <object-param>
               <name>site.templates.location</name>
               <description>description</description>
               <object type="org.exoplatform.portal.config.SiteConfigTemplates">
                  <field name="location">
                     <string>war:/conf/portal</string>
                  </field>
                  <field name="portalTemplates">
                     <collection type="java.util.HashSet">
                        <value><string>basic</string></value>
                        <value><string>classic</string></value>
                     </collection>
                  </field>
                  <field name="groupTemplates">
                     <collection type="java.util.HashSet">
                        <value><string>group</string></value>
                     </collection>
                  </field>
                  <field name="userTemplates">
                     <collection type="java.util.HashSet">
                        <value><string>user</string></value>
                     </collection>
                  </field>
               </object>
            </object-param>
            <object-param>
               <name>portal.configuration</name>
               <description>description</description>
               <object type="org.exoplatform.portal.config.NewPortalConfig">
                  <field name="predefinedOwner">
                     <collection type="java.util.HashSet">
                        <value><string>classic</string></value>
                     </collection>
                  </field>
                  <field name="ownerType">
                     <string>portal</string>
                  </field>
                  <field name="templateLocation">
                     <string>war:/conf/portal/</string>
                  </field>
                  <field name="importMode">
                     <string>conserve</string>
                  </field>
               </object>
            </object-param>
            <object-param>
               <name>group.configuration</name>
               <description>description</description>
               <object type="org.exoplatform.portal.config.NewPortalConfig">
                  <field name="predefinedOwner">
                     <collection type="java.util.HashSet">
                        <value><string>/platform/administrators</string></value>
                        <value><string>/platform/users</string></value>
                        <value><string>/platform/guests</string></value>
                        <value><string>/organization/management/executive-board</string></value>
                     </collection>
                  </field>
                  <field name="ownerType">
                     <string>group</string>
                  </field>
                  <field name="templateLocation">
                     <string>war:/conf/portal</string>
                  </field>
                  <field name="importMode">
                     <string>conserve</string>
                  </field>
               </object>
            </object-param>
            <object-param>
               <name>user.configuration</name>
               <description>description</description>
               <object type="org.exoplatform.portal.config.NewPortalConfig">
                  <field name="predefinedOwner">
                     <collection type="java.util.HashSet">
                        <value><string>root</string></value>
                        <value><string>john</string></value>
                        <value><string>mary</string></value>
                        <value><string>demo</string></value>
                        <value><string>user</string></value>
                     </collection>
                  </field>
                  <field name="ownerType">
                     <string>user</string>
                  </field>
                  <field name="templateLocation">
                     <string>war:/conf/portal</string>
                  </field>
                  <field name="importMode">
                     <string>conserve</string>
                  </field>
               </object>
            </object-param>
         </init-params>
      </component-plugin>
   </component-plugins>
</component>

This XML configuration defines where in the portal's war to look for configuration, and which portals, groups, and user specific views to include in portal/group/user navigation. Those files will be used to create an initial navigation when the portal is launched in the first time. That information will then be stored in the JCR content repository, and can then be modified and managed from the portal UI.

Each portal, groups and users navigation is indicated by a configuration paragraph, for example:

<object-param>
   <name>portal.configuration</name>
   <description>description</description>
   <object type="org.exoplatform.portal.config.NewPortalConfig">
      <field n(1)ame="predefinedOwner">
         <collection type="java.util.HashSet">
            <value><string>classic</string></value>
         </collection>
      </field>
      <field n(2)ame="ownerType">
         <string>portal</string>
      </field>
      <field n(3)ame="templateLocation">
         <string>war:/conf/portal/</string>
      </field>
      <field n(4)ame="importMode">
         <string>conserve</string>
      </field>
   </object>
</object-param>

1

predefinedOwner define the navigation owner, portal will look for the configuration files in folder with this name, if there is no suiable folder, a default portal will be created with name is this value.

2

ownerType define the type of portal navigation. It may be a portal, group or user

3

templateLocation the classpath where contains all portal configuration files

4

importMode The mode for navigation import. There are 4 types of import mode:

  • conserve: Import data when it does not exist, otherwise do nothing.
  • insert: Import data when it does not exist, otherwise performs a strategy that adds new data only.
  • merge: Import data when it does not exist, update data when it exists.
  • rewrite: Overwrite data whatsoever.

Base on these parameters, portal will look for the configuration files and create a relevant portal navigation, pages and data import strategy. The portal configuration files will be stored in folders with path look like {templateLocation}/{ownerType}/{predefinedOwner}, all navigations are defined in the navigation.xml file, pages are defined in pages.xml and portal configuration is defined in {ownerType}.xml. For example, with the above configuration, prtal will look for all configuration files from war:/conf/portal/portal/classic path.

The portal navigation incorporates the pages that can be accessed even when the user is not logged in assuming the applicable permissions allow the public access). For example, several portal navigations are used when a company owns multiple trademarks, and sets up a website for each of them.

The classic portal is configured by four XML files in the 02portal.war:/WEB-INF/conf/portal/portal/classic directory:

portal.xml

This file describes the layout and portlets that will be shown on all pages. The layout usually contains the banner, footer, menu and breadcrumbs portlets. GateIn 3.2 is extremely configurable as every view element (even the banner and footer) is a portlet.


<portal-config 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_2 http://www.gatein.org/xml/ns/gatein_objects_1_2"
   xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_2">
   <portal-name>classic</portal-name>
   <locale>en</locale>
   <access-permissions>Everyone</access-permissions>
   <edit-permission>*:/platform/administrators</edit-permission>
   <properties>
      <entry key="sessionAlive">onDemand</entry>
      <entry key="showPortletInfo">1</entry>
   </properties>

   <portal-layout>
      <portlet-application>
         <portlet>
            <application-ref>web</application-ref>
            <portlet-ref>BannerPortlet</portlet-ref>
            <preferences>
               <preference>
                  <name>template</name>
                  <value>par:/groovy/groovy/webui/component/UIBannerPortlet.gtmpl</value>
                  <read-only>false</read-only>
               </preference>
            </preferences>
         </portlet>
         <access-permissions>Everyone</access-permissions>
         <show-info-bar>false</show-info-bar>
      </portlet-application>

      <portlet-application>
         <portlet>
            <application-ref>web</application-ref>
            <portlet-ref>NavigationPortlet</portlet-ref>
         </portlet>
         <access-permissions>Everyone</access-permissions>
         <show-info-bar>false</show-info-bar>
      </portlet-application>

      <portlet-application>
         <portlet>
            <application-ref>web</application-ref>
            <portlet-ref>BreadcumbsPortlet</portlet-ref>
         </portlet>
         <access-permissions>Everyone</access-permissions>
         <show-info-bar>false</show-info-bar>
      </portlet-application>

      <page-body> </page-body>

      <portlet-application>
         <portlet>
            <application-ref>web</application-ref>
            <portlet-ref>FooterPortlet</portlet-ref>
            <preferences>
               <preference>
                  <name>template</name>
                  <value>par:/groovy/groovy/webui/component/UIFooterPortlet.gtmpl</value>
                  <read-only>false</read-only>
               </preference>
            </preferences>
         </portlet>
         <access-permissions>Everyone</access-permissions>
         <show-info-bar>false</show-info-bar>
      </portlet-application>

   </portal-layout>
   
</portal-config>

It is also possible to apply a nested container that can also contain portlets. Row, column or tab containers are then responsible for the layout of their child portlets.

Each application references a portlet using the id portal#{portalName}:/{portletWarName}/{portletName}/{uniqueId}

Use the page-body tag to define where GateIn 3.2 should render the current page.

The defined classic portal is accessible to "Everyone" (at /portal/public/classic) but only members of the group /platform/administrators can edit it.

navigation.xml

This file defines all the navigation nodes of the portal. The syntax is simple using the nested node tags. Each node refers to a page defined in the pages.xml file (explained next).

If the administrator want to create node labels for each language, they will have to use xml:lang attribute in the label tag with value of xml:lang is the relevant locale.

Otherwise, if they want the node label is localized by resource bundle files, the #{...} syntax will be used, the enclosed property name serves as a key that is automatically passed to the internationalization mechanism. Thus the literal property name is replaced by a localized value taken from the associated properties file matching the current locale.

For example:


<node-navigation 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_2 http://www.gatein.org/xml/ns/gatein_objects_1_2"
   xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_2">
   <priority>1</priority>
   <page-nodes>
      <node>
         <name>home</name>
         <label xml:lang="en">Home</label>
         <page-reference>portal::classic::homepage</page-reference>
      </node>
      <node>
         <name>sitemap</name>
         <label xml:lang="en">SiteMap</label>
         <visibility>DISPLAYED</visibility>
         <page-reference>portal::classic::sitemap</page-reference>
      </node>
      ..........
   </page-nodes>
</node-navigation>

This navigation tree can have multiple views inside portlets (such as the breadcrumbs portlet) that render the current view node, the site map or the menu portlets.

pages.xml

This configuration file structure is very similar to portal.xml and it can also contain container tags. Each application can decide whether to render the portlet border, the window state, the icons or portlet's mode.


<page-set
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_2 http://www.gatein.org/xml/ns/gatein_objects_1_2" 
   xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_2">

   <page>
      <name>homepage</name>
      <title>Home Page</title>
      <access-permissions>Everyone</access-permissions>
      <edit-permission>*:/platform/administrators</edit-permission>
      <portlet-application>
         <portlet>
            <application-ref>web</application-ref>
            <portlet-ref>HomePagePortlet</portlet-ref>
            <preferences>
               <preference>
                  <name>template</name>
                  <value>system:/templates/groovy/webui/component/UIHomePagePortlet.gtmpl</value>
                  <read-only>false</read-only>
               </preference>
            </preferences>
         </portlet>
         <title>Home Page portlet</title>
         <access-permissions>Everyone</access-permissions>
         <show-info-bar>false</show-info-bar>
         <show-application-state>false</show-application-state>
         <show-application-mode>false</show-application-mode>
      </portlet-application>
   </page>

   <page>
      <name>sitemap</name>
      <title>Site Map</title>
      <access-permissions>Everyone</access-permissions>
      <edit-permission>*:/platform/administrators</edit-permission>
      <portlet-application>
         <portlet>
            <application-ref>web</application-ref>
            <portlet-ref>SiteMapPortlet</portlet-ref>
         </portlet>
         <title>SiteMap</title>
         <access-permissions>Everyone</access-permissions>
         <show-info-bar>false</show-info-bar>
      </portlet-application>
   </page>
   .......
</page-set>
The 'Portal Data' term which has been referred in the previous sections can be classified into three types of object data: Portal Config, Page Data and Navigation Data; each of which has some differences in the import strategy.
The navigation data import strategy will be processed to the import mode level as the followings:
  • CONSERVE: If the navigation exists, leave it untouched. Otherwise, import data.
  • INSERT: Insert the missing description data, but add only new nodes. Other modifications remains untouched.
  • MERGE: Merge the description data, add missing nodes and update same name nodes.
  • OVERWRITE: Always destroy the previous data and recreate it.

In the GateIn navigation structure, each navigation can be referred to a tree which each node links to a page content. Each node contains some description data, such as label, icon, page reference, and more. Therefore, GateIn provides a way to insert or merge new data to the initiated navigation tree or a sub-tree.

The merge strategy performs the recursive comparison of child nodes between the existing persistent nodes of a navigation and the transient nodes provided by a descriptor:

Let's see the example with two navigation nodes in each import mode. In this case, there are 2 navigation definitions:

<node-navigation>
  <page-nodes>
    <node>
      <name>foo</name>
      <icon>foo_icon_1</icon>
      <node>
        <name>juu</name>
        <icon>juu_icon</icon>
      </node>
    </node>
    <node>
      <name>daa</name>
      <icon>daa_icon</icon>
    </node>
  </page-nodes>
</node-navigation>
Navigation node tree hierarchy

<node-navigation>
  <page-nodes>
    <node>
      <name>foo</name>
      <icon>foo_icon_2</icon>
    </node>
    <node>
      <name>bar</name>
      <icon>bar_icon</icon>
    </node>
  </page-nodes>
</node-navigation>
Navigation node tree hierarchy

For example, the navigation1 is loaded before navigation2. The Navigation Importer processes on two navigation definitions, depending on the Import Mode defined in portal configuration.

  • Case 1: Import mode is CONSERVE.

    With the CONSERVE mode, data are only imported when they do not exist. So, if the navigation has been created by the navigation1 definition, the navigation2 definition does not affect anything on it. We have the result as following

  • Case 2: Import mode is INSERT.

    If a node does not exist, the importer will add new nodes to the navigation tree. You will see the following result:

    Hereafter, the node 'bar' is added to the navigation tree, because it does not exist in the initiated data. Other nodes are kept in the import process.

  • Case 3: Import mode is MERGE.

    The MERGE mode indicates that a new node is added to the navigation tree, and updates the node data (such node label and node icon in the example) if it exists.

  • Case 4: Import mode is OVERWRITE.

    Everything will be destroyed and replaced with new data if the OVERWRITE mode is used.

All GateIn 3.2 applications contain property files for various languages. They are packaged with the portlets applications in a WEB-INF/classes/locale/ directory.

These files are located in the classes folder of the WEB-INF directory, so as to be loaded by the ClassLoader.

All resource files are in a subfolder named locale.

For instance; the translations for the NavigationPortlet are located in web.war/WEB-INF/classes/locale/portlet/portal

NavigationPortlet_de.properties
NavigationPortlet_en.properties
NavigationPortlet_es.properties
NavigationPortlet_fr.properties
NavigationPortlet_nl.properties
NavigationPortlet_ru.properties
NavigationPortlet_uk.properties
NavigationPortlet_ar.xml

Inside those file are typical key=value Java EE properties. For example the French one:

javax.portlet.title=Portlet Navigation

There are also properties files in the portal itself. They form the portal resource bundle.

From a portlet you can then access translations from the portlet itself or shared at the portal level, both are aggregated when you need them.

Translation in XML format

It is also possible to use a proprietary XML format to define translations. This is a more convenient way to translate a document for some languages such as Japanese, Arabic or Russian. Property files have te be ASCII encoded, while the XML file can define its encoding. As a result it's easier for a human being to read (and fix) a translation in XML instead of having to decode and encode the property file.

For more information refer to: Section 3.10, “XML Resources Bundles”

Various languages are available in the portal package. The configuration below will define which languages are shown in the "Change Language" section and made available to users.

The 02portal.war:/WEB-INF/conf/common/common-configuration.xml file of your installation contains the following section:


<component>
  <key>org.exoplatform.services.resources.LocaleConfigService</key>
  <type>org.exoplatform.services.resources.impl.LocaleConfigServiceImpl</type>
  <init-params>
    <value-param>
      <name>locale.config.file</name>
      <value>war:/conf/common/locales-config.xml</value>
    </value-param>
  </init-params>
</component>

This configuration points to the locale configuration file.

The locale configuration file (02portal.war:/WEB-INF/conf/common/locales-config.xml) contains the following code:

<?xml version="1.0" encoding="UTF-8"?>
<locales-config>
  <locale-config>
    <locale>en(1)</locale>
    <output-en(2)coding>UTF-8</output-encoding>
    <input-enc(3)oding>UTF-8</input-encoding>
    <descripti(4)on>Default configuration for english locale</description>
  </locale-config>
 
  <locale-config>
    <locale>fr</locale>
    <output-encoding>UTF-8</output-encoding>
    <input-encoding>UTF-8</input-encoding>
    <description>Default configuration for the french locale</description>
  </locale-config>
 
  <locale-config>
    <locale>ar</locale>
    <output-encoding>UTF-8</output-encoding>
    <input-encoding>UTF-8</input-encoding>
    <description>Default configuration for the arabic locale</description>
    <orientati(5)on>rt</orientation>
  </locale-config>
</locales-config>

1

locale The locale has to be defined such as defined here http://ftp.ics.uci.edu-pub-ietf-http-related-iso639.txt. In this example "ar" is Arabic.

2

output-encoding deals with character encoding. It is recommended that UTF-8 be used.

3

input-encoding In the java implementation, the encoding parameters will be used for the request response stream. The input-encoding parameter will be used for request setCharacterEncoding(..).

4

description Description for the language

5

orientation The default orientation of text and images is Left-To-Right. GateIn 3.2 supports Right-To-Left orientation. Modifying text orientation is explained in Section 3.9, “RTL (Right To Left) Framework”.

The resource bundle service is configured in: 02portal.war:/WEB-INF/conf/common/common-configuration.xml:

<component>
  <key>org.exoplatform.services.resources.ResourceBundleService</key>
  <type>org.exoplatform.services.resources.impl.SimpleResourceBundleService</type>
  <init-params>
    <values-param>
      <name>cl(1)asspath.resources</name>
      <description>The resources  that start with the following package name should be load from file system</description>
      <value>locale.portlet</value>      
    </values-param>      
    <values-param>
      <name>in(2)it.resources</name>
      <description>Initiate the following resources during the first launch</description>
      <value>locale.portal.expression</value>
      <value>locale.portal.services</value>
      <value>locale.portal.webui</value>
      <value>locale.portal.custom</value>
      <value>locale.navigation.portal.classic</value>
      <value>locale.navigation.group.platform.administrators</value>
      <value>locale.navigation.group.platform.users</value>
      <value>locale.navigation.group.platform.guests</value>
      <value>locale.navigation.group.organization.management.executive-board</value>               
    </values-param>      
    <values-param>
      <name>po(3)rtal.resource.names</name>
      <description>The properties files of  the portal ,  those file will be merged 
        into one ResoruceBundle properties </description>
      <value>locale.portal.expression</value>
      <value>locale.portal.services</value>
      <value>locale.portal.webui</value>
      <value>locale.portal.custom</value>        
    </values-param>      
  </init-params>
</component>

1

classpath.resources are discussed in a later section.

2

init.resources TODO

3

portal.resource.names Defines all resources that belong to the Portal Resource Bundle.

These resources are merged to a single resource bundle which is accessible from anywhere in GateIn 3.2. All these keys are located in the same bundle, which is separated from the navigation resource bundles.

Portlets are independent applications and deliver their own resource files.

All shipped portlet resources are located in the locale/portlet subfolder. The ResourceBundleService parameter classpath.resources defines this subfolder.

See the portlet specification for more details about portlet internationalization.

Every request processed by every portlet is invoked within a context of current Locale. Current Locale can be retrieved by calling getLocale() method of javax.portlet.PortletRequest interface.

The exact algorithm for determining the current Locale is not specified by Portlet Specification, and is left to portlet containers to implement the way they deem most appropriate.

In GateIn 3.2 each portal instance has a default language which can be used to present content for new users. Another option is to use each user’s browser language preference, provided it matches one of the available localizations that GateIn 3.2 supports, and only fallback to portal default language if no match is found. Every user, while visiting a portal, has an option to change the language of the user interface by using a Language chooser. The choice can be remembered for the duration of the session, or it can be remembered for a longer period using a browser cookie, or - for registered and logged-in users - it can be saved into user’s profile.

So, we can see that there is more than one way to determine the Locale to be used for displaying a portal page to the user. For this reason the mechanism for determining the current Locale of the request is pluggable in GateIn 3.2, so the exact algorithm can be customized.

By default, org.exoplatform.portal.application.localization.DefaultLocalePolicyService - an implementation of LocalePolicy - is installed to provide the default behaviour. This, however, can easily be extended and overriden. A completely new implementation can also be written from scratch.

DefaultLocalePolicyService treats logged-in users slightly differently than anonymous users. Logged-in users have a profile that can contain language preference, while anonymous users don't.

Here is an algorithm used for anonymous users.

If no supported locale is found the return locale eventually defaults to portalLocale.

The algorithm for logged-in users is virtually the same except that the first Locale source checked is user's profile.

In portals all the resources that are not portlets themselves but are accessed through portlets - reading data through PortletRequest, and writing to PortletResponse - are referred to as 'bridged'. Any resources that are accessed directly, bypassing portal filters and servlets, are referred to as 'non-bridged'.

Non-bridged servlets, and .jsps have no access to PortalRequest. They don't use PortletRequest.getLocale() to determine current Locale. Instead, they use ServletRequest.getLocale() which is subject to precise semantics defined by Servlet specification - it reflects browser's language preference.

In other words, non-bridged resources don't have a notion of current Locale in the same sense that portlets do. The result is that when mixing portlets and non-bridged resources there may be a localization mismatch - an inconsistency in the language used by different resources composing your portal page.

This problem is addressed by LocalizationFilter. This is a filter that changes the behaviour of ServletRequest.getLocale() method so that it behaves the same way as PortletRequest.getLocale(). That way even localization of servlets, and .jsps accessed in a non-bridged manner can stay in sync with portlet localization.

LocalizationFilter is installed through portal's web.xml file: gatein.ear/02portal.war/WEB-INF/web.xml


   <filter>
        <filter-name>LocalizationFilter</filter-name>
        <filter-class>org.exoplatform.portal.application.localization.LocalizationFilter</filter-class>
    </filter>

    ...

    <filter-mapping>
       <filter-name>LocalizationFilter</filter-name>
       <url-pattern>*.jsp</url-pattern>
       <dispatcher>INCLUDE</dispatcher>
       <dispatcher>FORWARD</dispatcher>
       <dispatcher>REQUEST</dispatcher>
       <dispatcher>ERROR</dispatcher>
   </filter-mapping>
         

There is a tiny limitation with this mechanism in that it is unable to determine the current portal, and consequently its default language. As a result the portalLocale defaults to English, but can be configured to something else by using filter's PortalLocale init param. For example:


   <filter>
        <filter-name>LocalizationFilter</filter-name>
        <filter-class>org.exoplatform.portal.application.localization.LocalizationFilter</filter-class>
        <init-param>
           <param-name>PortalLocale</param-name>
           <param-value>fr_FR</param-value>
        </init-param>
    </filter> 
         

By default, LocalizationFilter is applied to *.jsp, which is considered the minimum required by GateIn 3.2 to properly keep its non-bridged resources in sync with the rest of the portal. Additionally deployed portlets, and portal applications, may need broader mapping to cover their non-bridged resources.

Avoid using /*, /public/*, /private/*, and similar broad mappings as LocalizationFilter sometimes adversely interacts with the processing of portlet requests. Use multiple filter-mappings instead to specifically target non-bridged resources.

Keeping the mapping limited to only non-bridged resources will minimize any impact on performance as well.

The text orientation depends on the current locale setting. The orientation is a Java 5 enum that provides a set of functionalities:

   LT, // Western Europe
   RT, // Middle East (Arabic, Hebrew)
   TL, // Japanese, Chinese, Korean
   TR; // Mongolian
   public boolean isLT() { ... }
   public boolean isRT() { ... }
   public boolean isTL() { ... }
   public boolean isTR() { ... }

The object defining the Orientation for the current request is the UIPortalApplication. However it should be accessed at runtime using the RequestContext that delegates to the UIPortalApplication.

In the case of a PortalRequestContext it is a direct delegate as the PortalRequestContext has a reference to the current UIPortalApplication.

In the case of a different context such as the PortletRequestContext, it delegates to the parent context given the fact that the root RequestContext is always a PortalRequestContext.

The Inter Application Communication library is found in 01eXoResources.war:/javascript/eXo/core/Topic.js

/**
 * publish is used to publish an event to the other subscribers to the given channels
 * @param {Object} senderId is a string that identify the sender
 * @param {String} topic is the topic that the message will be published
 * @param {Object} message is the message that's going to be delivered to the subscribers to the topic
 */
Topic.prototype.publish = function(/*Object*/ senderId, /*String*/ topicName, /*Object*/ message ) { ... }

/**
 * isSubscribed is used to check if a function receive the events from a topic
 * @param {String} topic The topic.
 * @param {Function} func is the name of the function of obj to call when a message is received on the topic
 */
Topic.prototype.isSubscribed = function(/*String*/ topic, /*Function*/ func) { ... }

/**
 * subscribe is used to subscribe a callback to a topic
 * @param {String} topic is the topic that will be listened
 * @param {Function} func is the name of the function of obj to call when a message is received on the topic
 * 
 * func is a function that take a Object in parameter. the event received have this format:
 * {senderId:senderId, message:message, topic: topic}
 *
 */
Topic.prototype.subscribe = function(/*String*/ topic, /*Function*/ func) { ... }

/**
 * unsubscribe is used to unsubscribe a callback to a topic
 * @param {String} topic is the topic
 * @param {Object} id is the id of the listener we want to unsubscribe
 */
Topic.prototype.unsubscribe = function(/*String*/ topic, /*Object*/ id) { ... }

Topic.prototype.initCometdBridge = function() { ... }

The service is defined by the class: org.exoplatform.upload.UploadService;

This can be configured with the following xml code:


<component>
   <type>org.exoplatform.upload.UploadService</type>
     <init-params>
       <value-param>
        <name>upload.limit.size</name>
        <description>Maximum size of the file to upload in MB</description>
        <value>10</value>
      </value-param>
    </init-params>  
  </component>

This code allows for a default upload size limit for the service to be configured. The value unit is in MegaBytes.

This limit will be used by default by all applications if no application-specific limit is set. Setting a different limit for applications is discussed in a later section.

If the value is set at 0 the upload size is unlimited.

Procedure 3.4. How to use the upload component

  1. Create an object type org.exoplatform.webui.form.UIFormUploadInput.

    Two constructors are available for this:

    public UIFormUploadInput(String name, String bindingExpression)
    

    or:

    public UIFormUploadInput(String name, String bindingExpression, int limit)
    

    This is an example using the second form :

    PortletRequestContext pcontext = (PortletRequestContext)WebuiRequestContext.getCurrentInstance();
    
    PortletPreferences portletPref = pcontext.getRequest().getPreferences();
    int limitMB = Integer.parseInt(portletPref.getValue("uploadFileSizeLimitMB", "").trim());
    UIFormUploadInput uiInput = new UIFormUploadInput("upload", "upload", limitMB);
  2. To obtain the limit from the xml configuration, this piece of code can be added to the either portlet.xml or portlet-preferences.xml :

    
    <preference>
      <name>uploadFileSizeLimitMB</name>
      <value>30</value>
      <read-only>false</read-only>
    </preference>

    Again, a 0 value means an unlimited upload size, and the value unit is set in MegaBytes.

  3. Use the getUploadDataAsStream() method to get the uploaded data:

    UIFormUploadInput input = (UIFormUploadInput)uiForm.getUIInput("upload");
    
    InputStream inputStream = input.getUploadDataAsStream();
    ...
    jcrData.setValue(inputStream);
  4. The upload service stores a temporary file on the filesystem during the upload process. When the upload is finished, the service must be cleaned in order to:

    1. Delete the temporary file.

    2. Delete the classes used for the upload.

    Use theremoveUpload() method defined in the upload service to purge the file:

    UploadService uploadService = uiForm.getApplicationComponent(UploadService.class) ;
    
    UIFormUploadInput uiChild = uiForm.getChild(UIFormUploadInput.class) ;
    uploadService.removeUpload(uiChild.getUploadId()) ;

    Saving the uploaded file

    Ensure the file is saved before the service is cleaned.

Managing Javascript scripts in an application like GateIn 3.2 is a critical part of the configuration work. Configuring the scripts correctly will result in a faster response time from the portal.

Every portlet can have its own javscript code but in many cases it is more convenient to reuse some existing shared libraries. For that reason, GateIn 3.2 has a mechanism to easily register the libraries that will be loaded when the first page will be rendered.

To do so, every WAR deployed in GateIn 3.2 can register the .js files with the groovy script WEB-INF/conf/script/groovy/JavascriptScript.groovy. (TODO: this file doesn't seem to exist)

The example file below is found in the 01eXoResources.war

JavascriptService.addJavascript("eXo", "/javascript/eXo.js", ServletContext);

/* Animation Javascripts */
JavascriptService.addJavascript("eXo.animation.ImplodeExplode", "/javascript/eXo/animation/ImplodeExplode.js", ServletContext);
/* Application descriptor */
JavascriptService.addJavascript("eXo.application.ApplicationDescriptor", "/javascript/eXo/application/ApplicationDescriptor.js", ServletContext);
/* CORE Javascripts */
JavascriptService.addJavascript("eXo.core.Utils", "/javascript/eXo/core/Util.js", ServletContext);
JavascriptService.addJavascript("eXo.core.DOMUtil", "/javascript/eXo/core/DOMUtil.js", ServletContext);
JavascriptService.addJavascript("eXo.core.Browser", "/javascript/eXo/core/Browser.js", ServletContext);
JavascriptService.addJavascript("eXo.core.MouseEventManager", "/javascript/eXo/core/MouseEventManager.js", ServletContext);
JavascriptService.addJavascript("eXo.core.UIMaskLayer", "/javascript/eXo/core/UIMaskLayer.js", ServletContext);
JavascriptService.addJavascript("eXo.core.Skin", "/javascript/eXo/core/Skin.js", ServletContext);
JavascriptService.addJavascript("eXo.core.DragDrop", "/javascript/eXo/core/DragDrop.js", ServletContext);
JavascriptService.addJavascript("eXo.core.TemplateEngine", "/javascript/eXo/core/TemplateEngine.js", ServletContext);
/* Widget Javascripts */
JavascriptService.addJavascript("eXo.widget.UIWidget", "/javascript/eXo/widget/UIWidget.js", ServletContext);
JavascriptService.addJavascript("eXo.widget.UIAddWidget", "/javascript/eXo/widget/UIAddWidget.js", ServletContext);
JavascriptService.addJavascript("eXo.widget.UIExoWidget", "/javascript/eXo/widget/UIExoWidget.js", ServletContext);
/* Desktop Javascripts */
JavascriptService.addJavascript("eXo.desktop.UIDockbar", "/javascript/eXo/desktop/UIDockbar.js", ServletContext);
JavascriptService.addJavascript("eXo.desktop.UIDesktop", "/javascript/eXo/desktop/UIDesktop.js", ServletContext);
/* WebUI Javascripts */ 
JavascriptService.addJavascript("eXo.webui.UIItemSelector", "/javascript/eXo/webui/UIItemSelector.js", ServletContext);
JavascriptService.addJavascript("eXo.webui.UIForm", "/javascript/eXo/webui/UIForm.js", ServletContext);
JavascriptService.addJavascript("eXo.webui.UIPopup", "/javascript/eXo/webui/UIPopup.js", ServletContext);
JavascriptService.addJavascript("eXo.webui.UIPopupSelectCategory", "/javascript/eXo/webui/UIPopupSelectCategory.js", ServletContext);
JavascriptService.addJavascript("eXo.webui.UIPopupWindow", "/javascript/eXo/webui/UIPopupWindow.js", ServletContext);
JavascriptService.addJavascript("eXo.webui.UIVerticalScroller", "/javascript/eXo/webui/UIVerticalScroller.js", ServletContext);
JavascriptService.addJavascript("eXo.webui.UIHorizontalTabs", "/javascript/eXo/webui/UIHorizontalTabs.js", ServletContext);
JavascriptService.addJavascript("eXo.webui.UIPopupMenu", "/javascript/eXo/webui/UIPopupMenu.js", ServletContext);
JavascriptService.addJavascript("eXo.webui.UIDropDownControl", "/javascript/eXo/webui/UIDropDownControl.js", ServletContext);
/* Portal Javascripts */ 
JavascriptService.addJavascript("eXo.portal.PortalHttpRequest", "/javascript/eXo/portal/PortalHttpRequest.js", ServletContext);
JavascriptService.addJavascript("eXo.portal.UIPortal", "/javascript/eXo/portal/UIPortal.js", ServletContext);
JavascriptService.addJavascript("eXo.portal.UIWorkspace", "/javascript/eXo/portal/UIWorkspace.js", ServletContext);
JavascriptService.addJavascript("eXo.portal.UIPortalControl", "/javascript/eXo/portal/UIPortalControl.js", ServletContext);
JavascriptService.addJavascript("eXo.portal.PortalDragDrop", "/javascript/eXo/portal/PortalDragDrop.js", ServletContext);
JavascriptService.addJavascript("eXo.portal.UIPortalNavigation", "/javascript/eXo/portal/UIPortalNavigation.js", ServletContext);
JavascriptService.addJavascript("eXo.portal.UIMaskWorkspace", "/javascript/eXo/portal/UIMaskWorkspace.js", ServletContext);
JavascriptService.addJavascript("eXo.portal.UIExoStartMenu", "/javascript/eXo/portal/UIExoStartMenu.js", ServletContext);
/* Desktop Javascripts 2 */
JavascriptService.addJavascript("eXo.desktop.UIWindow", "/javascript/eXo/desktop/UIWindow.js", ServletContext);

Note that even registered dedicated javascripts will be merged into a single merged.js file when the server loads. This reduces the number of HTTP calls as seen in the home page source code:


<script type="text/javascript" src="/portal/javascript/merged.js"></script>

Although this optimization is useful for a production environment, it may be easier to deactivate this optimization while debugging javascript problems.

To do this, set the java system property exo.product.developing to true.

To see or use the merged file set this property to false.

The property can be passed as a JVM parameter with the -D option in your GateIn.sh or GateIn.bat startup script.

Every javascript file is associated with a module name which acts as a namespace. The module name is passed as a first parameter to JavascriptService.addJavascript() function as in the following example:



JavascriptService.addJavascript("eXo.core.DragDrop",
      "/javascript/eXo/core/DragDrop.js", ServletContext);
   

Inside the associated javascript files, functions are exposed as global javascript function variables using the module name.

For example:



eXo.core.DragDrop = new DragDrop();
   

It is also possible to use eXo.require() javascript method to lazy load and evaluate some javascript code. This is quite useful for the portlet or widget applications that will use this javascript only once. Otherwise, if the library is reusable in several places it is better to reference it in the groovy file.

The controller configuration that contains the routing rules is loaded from a file named controller.xml that is retrieved in the GateIn configuration directory. Its location is determined by the gatein.controller.config property.

WebAppController loads and initializes the mapping engine

GateIn's extension project can define their own routing table, thanks to the extension mechanism.

The controller.xml can be changed and reloaded at runtime, this help the testing of different configurations easily (configuration loading operations) and provide more insight into the routing engine (the findRoutes operation). see Rebuiding controller for more detail

The WebAppController is annotated with @Managed annotations and is bound under the view=portal,service=controller JMX name and under the "portalcontroller" REST name.

It provides the following attributes and operations

Most of the controller configuration cares about defining rules (Routing table - contains routes object) that will drive the resolution. Routes are processed during the controller initialization to give a tree of node. Each node

A parameter is defined by a qualified name and there are three kind of parameters

Path parameters allow to associate a portion of the request path with a parameter. Such parameter will match any non empty portion of text except the / character (that is the [^/]+ regular expression) otherwise they can be associated with a regular expression for matching specific patterns. Path parameters are mandatory for matching since they are part of the request path, however it is allowed to write regular expression matching an empty value.

Encoding

Path parameters may contain '/' character which is a reserved char for URI path. This case is specially handled by the navigation controller by using a special character to replace '/' literals. By default the character is the semi colon : and can be changed to other possible values (see controller XML schema for possible values) to give a greater amount of flexibility.

This encoding is applied only when the encoding performed for parameter having a mode set to the default-form value, for instance it does not happen for navigation node URI (for which / are encoded literally). The separator escape char can still be used but under it's percent escaped form, so by default a path parameter value containing : would be encoded as %3A and conversely the %3A value will be decoded as :.

Example:

No pattern defined, used the default one [^/]+

If the request path contains another "/" char it will not work,default encoding mode is : default-form. For example:"/foo/bar" --> not matched, return empty parameter map

However this could be solved with the following configuration:

GateIn defines a set of parameters in its routing table, for each client request, the mapping engine processes the request path and return the defined parameters with their values as a Map<QualifiedName, String>

gtn:handler

The gtn:handler names is one of the most important qualified name as it determines which handler will take care of the request processing just after the controller has determined the parameter map. The handler value is used to make a lookup in the handler map of the controller. An handler is a class that extends the WebRequestHandler class and implements the execute(ControllerContext) method. Several handlers are available by default:

gtn:sitetype / gtn:sitename / gtn:path

Those qualified names drives a request for the portal handler. They are used to determine which site to show and which path to resolve against a navigation. For instance the (gtn:sitetype=portal,gtn:sitename=classic,gtn:path=home) instruct the portal handler to show the home page of the classic portal site.

gtn:lang

The language in the url for the portal handler. This is a new feature offered, now language can be specified on URL. that mean user can bookmark that URL (with the information about language) or he can changed language simply by modifying the URL address

gtn:componentid / gtn:action / gtn:objectid

The webui parameters used by the portal handler for managing webui component URLs for portal applications (and not for portlet applications).

The controller is designed to render a Map<QualifiedName, String> as an http URL according to its routing table. But to integrate it for using easily in WebUI Framework of GateIn, we need some more components

PortalURL play a similar role at the portal level, its main role is to abstract the creation of an URL for a resource managed by the portal.

The PortalURL declaration may seem a bit strange at first sight with two generic types U and R and the circular recursion of the U generic parameter, but it's because most of the time you will not use the PortalURL object but instead subclasses.

A portal URL has various methods but certainly the most important method is the toString() method that generates an URL representing that will target the resource associated with the url. The remaining methods are getter and setter for mutating the url configuration, those options will affect the URL representation when it is generated.

Obtaining a PortalURL

PortalURL objects are obtained from RequestContext instance such as the PortalRequestContext or the PortletRequestContext. Usually those are obtained thanks to getCurrentInstance method of the RequestContext class:

PortalURL are created via to the createURL method that takes as input a resource type. A resource type is usually a constant and is a type safe object that allow to retrieve PortalURL subclasses:

In reality you will use a concrete type constant and have instead more concrete code like:

The navication controller implies a migration of the client code that is coupled to several internal APIs of GateIn. As far as we know the major impact is related to anything dealing with URL:

There are also changes in the configuration, because there is a change of how things are internally.