JBoss.orgCommunity Documentation

GateIn Reference Guide


1. Introduction
1.1. Related Links
2. Configuration
2.1. Database Configuration
2.1.1. Overview
2.1.2. Configuring the database for JCR
2.1.3. Configuring the database for the default identity store
2.2. E-Mail Service Configuration
2.2.1. Overview
2.2.2. Configuring the outgoing e-mail account
2.3. HTTPS Configuration
2.3.1. Overview
2.3.2. Generate your key
2.3.3. Setup Jboss configuration to use your key
2.3.4. Setup Tomcat configuration to use your key
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.5.5. Tips
3.6. Internationalization Configuration
3.6.1. Overview
3.6.2. Locales configuration
3.6.3. ResourceBundleService
3.6.4. Navigation Resource Bundles
3.6.5. Portlets
3.6.6. Translating the language selection form
3.7. Pluggable Locale Policy
3.7.1. LocalePolicy API
3.7.2. Default LocalePolicy
3.7.3. Custom LocalePolicy
3.7.4. LocalePolicy Configuration
3.7.5. Keeping non-bridged resources in sync with current Locale
3.8. RTL (Right To Left) Framework
3.8.1. Groovy templates
3.8.2. Stylesheet
3.8.3. Images
3.8.4. Client side JavaScript
3.9. XML Resources Bundles
3.9.1. Motivation
3.9.2. XML format
3.9.3. Portal support
3.10. JavaScript Inter Application Communication
3.10.1. Overview
3.10.2. Library
3.10.3. Syntax
3.10.4. Example of Javascript events usage
3.11. Upload Component
3.11.1. Upload Service
3.12. Deactivation of the Ajax Loading Mask Layer
3.12.1. Purpose
3.12.2. Synchronous issue
3.13. Javascript Configuration
4. Portlet development
4.1. Portlet Primer
4.1.1. JSR-168 and JSR-286 overview
4.1.2. Tutorials
4.2. Global porlet.xml file
4.2.1. Global portlet.xml usecase
4.2.2. Global metadata
5. Gadget development
5.1. Gadgets
5.1.1. Existing Gadgets
5.1.2. Create a new Gadget
5.1.3. Remote Gadget
5.1.4. Gadget Importing
5.1.5. Gadget Web Editing
5.1.6. Gadget IDE Editing
5.1.7. Dashboard Viewing
5.2. Setup a Gadget Server
5.2.1. Virtual servers for gadget rendering
5.2.2. Configuration
6. Authentication and Identity
6.1. Predefined User Configuration
6.1.1. Overview
6.1.2. Plugin for adding users, groups and membership types
6.1.3. Membership types
6.1.4. Groups
6.1.5. Users
6.1.6. Plugin for monitoring user creation
6.2. Authentication Token Configuration
6.2.1. What is Token Service?
6.2.2. Implementing the Token Service API
6.2.3. Configuring token services
6.3. PicketLink IDM integration
6.3.1. Configuration files
6.4. Organization API
6.5. Accessing User Profile
6.6. SSO - Single Sign On
6.6.1. Overview
6.6.2. CAS - Central Authentication Service
6.6.3. JOSSO
6.6.4. OpenSSO - The Open Web SSO project
6.6.5. SPNEGO
7. Web Services for Remote Portlets (WSRP)
7.1. Introduction
7.2. Level of support in GateIn 3.2
7.3. Deploying GateIn's WSRP services
7.3.1. Considerations to use WSRP when running GateIn on a non-default port or hostname
7.3.2. Considerations to use WSRP with SSL
7.4. Making a portlet remotable
7.5. Consuming GateIn's WSRP portlets from a remote Consumer
7.6. Consuming remote WSRP portlets in GateIn
7.6.1. Overview
7.6.2. Configuring a remote producer walk-through
7.6.3. Configuring access to remote producers via XML
7.6.4. Examples
7.7. Consumers maintenance
7.7.1. Modifying a currently held registration
7.7.2. Consumer operations
7.7.3. Importing and exporting portlets
7.7.4. Erasing local registration data
7.8. Configuring GateIn's WSRP Producer
7.8.1. Overview
7.8.2. Default configuration
7.8.3. Registration configuration
7.8.4. WSRP validation mode
8. Advanced Development
8.1. Foundations
8.1.1. GateIn Kernel
8.1.2. Configuring services
8.1.3. Configuration syntax
8.1.4. InitParams configuration object
8.1.5. Configuring a portal container
8.1.6. GateIn Extension Mechanism, and Portal Extensions
8.1.7. Running Multiple Portals

GateIn 3.2 is the merge of two mature Java projects; JBoss Portal and eXo Portal. This new community project takes the best of both offerings and incorporates them into a single portal framework. The aim is to provide an intuitive user-friendly portal, and a framework to address the needs of today's Web 2.0 applications.

This book provides a deep-dive information about installation and configuration of the services provided by GateIn.

To configure the database used by JCR you will need to edit the file:

$JBOSS_HOME/server/default/conf/gatein/configuration.properties

For Tomcat, the file is located at

$TOMCAT_HOME/gatein/conf/configuration.properties

And edit the values of driver, url, username and password with the values for your JDBC connection (please, refer to your database JDBC driver documentation).


gatein.jcr.datasource.driver=org.hsqldb.jdbcDriver
gatein.jcr.datasource.url=jdbc:hsqldb:file:${gatein.db.data.dir}/data/jdbcjcr_${name}
gatein.jcr.datasource.username=sa
gatein.jcr.datasource.password=

By default, the name of the database is "jdbcjcr_${name}" - ${name} should be a part of the database name, as it is dynamically replaced by the name of the portal container extension (for instance, gatein-sample-portal.ear defines "sample-portal" as container name and the default portal defines "portal" as container name).

In the case of HSQL the databases are created automatically. For any other database you will need to create a database named jdbcjcr_portal (and "jdbcjcr_sample-portal" if you have gatein-sample-portal.ear in $JBOSS_HOME/server/default/deploy - note that some databases don't accept '-' in the database name, so you may have to remove $JBOSS_HOME/server/default/deploy/gatein-sample-portal.ear)

Make sure the user has rights to create tables on jdbcjcr_portal, and to update them as they will be automatically created during the first startup .

Also add your database's JDBC driver into the classpath - you can put it in $JBOSS_HOME/server/default/lib (or $TOMCAT_HOME/lib, if you are running on Tomcat)

MySQL example:

Let's configure our JCR to store data in MySQL. Let's pretend we have a user named "gateinuser" with a password "gateinpassword". We would create a database "mygateindb_portal" (remember that _portal is required), and assign our user the rights to create tables.

Then we need to add MySQL's JDBC driver to the classpath, and finally edit gatein.ear/02portal.war/WEB-INF/conf/jcr/jcr-configuration to contain the following:

gatein.jcr.datasource.driver=com.mysql.jdbc.Driver
gatein.jcr.datasource.url=jdbc:mysql://localhost:3306/mygateindb${container.name.suffix}
gatein.jcr.datasource.username=gateinuser
gatein.jcr.datasource.password=gateinpassword
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.5.5. Tips
3.6. Internationalization Configuration
3.6.1. Overview
3.6.2. Locales configuration
3.6.3. ResourceBundleService
3.6.4. Navigation Resource Bundles
3.6.5. Portlets
3.6.6. Translating the language selection form
3.7. Pluggable Locale Policy
3.7.1. LocalePolicy API
3.7.2. Default LocalePolicy
3.7.3. Custom LocalePolicy
3.7.4. LocalePolicy Configuration
3.7.5. Keeping non-bridged resources in sync with current Locale
3.8. RTL (Right To Left) Framework
3.8.1. Groovy templates
3.8.2. Stylesheet
3.8.3. Images
3.8.4. Client side JavaScript
3.9. XML Resources Bundles
3.9.1. Motivation
3.9.2. XML format
3.9.3. Portal support
3.10. JavaScript Inter Application Communication
3.10.1. Overview
3.10.2. Library
3.10.3. Syntax
3.10.4. Example of Javascript events usage
3.11. Upload Component
3.11.1. Upload Service
3.12. Deactivation of the Ajax Loading Mask Layer
3.12.1. Purpose
3.12.2. Synchronous issue
3.13. Javascript Configuration

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;
}
.UIPageDesktop .MyTheme .ResizeArea {
  float: right;/* orientation=lt */
  float: left;/* orientation=rt */
  width: 18px; height: 18px;
  cursor: nw-resize;
  background: url('background/ResizeArea18x18.gif') no-repeat left top; /* orientation=lt */
  background: url('background/ResizeArea18x18-rt.gif') no-repeat right top; /* orientation=rt */
}
.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;
  width: 100%;
}
.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;
}
.MyTheme .WindowBarCenter .FixHeight {
  height: 21px;
  padding-top: 8px;
}
.MyTheme .MiddleDecoratorLeft {
  padding-left: 12px;
  background: url('background/MyTheme.png') repeat-y left;
}
.MyTheme .MiddleDecoratorRight {
  padding-right: 11px;
  background: url('background/MyTheme.png') repeat-y right;
}
.MyTheme .MiddleDecoratorCenter {
  background: #ffffff;
}
.MyTheme .BottomDecoratorLeft {
  MyTheme: 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;
}
.MyTheme .BottomDecoratorCenter .FixHeight {
  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 types of navigation available to portal users:

These navigations are configured using 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> 
       <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>
               <value><string>webos</string></value>
             </collection>
           </field>
           <field  name="ownerType"><string>portal</string></field>
           <field  name="templateLocation"><string>war:/conf/portal</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> 
         </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>
             </collection>
           </field>
           <field  name="ownerType"><string>user</string></field>
           <field  name="templateLocation"><string>war:/conf/portal</string></field> 
         </object>
       </object-param>
     </init-params>
   </component-plugin>
</component-plugins>

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

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

The classic portal is configured by four XML files in 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. Usually the layout 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.


<?xml version="1.0" encoding="ISO-8859-1"?>
<portal-config>
  <portal-name>classic</portal-name>
  <locale>en</locale>
  <factory-id>office</factory-id>
  <access-permissions>Everyone</access-permissions>
  <edit-permission>*:/platform/administrators</edit-permission>
  <creator>root</creator>    
    
  <portal-layout>   
  <application>
     <instance-id>portal#classic:/web/BannerPortlet/banner</instance-id>
     <show-info-bar>false</show-info-bar>
   </application>
   <application>
    <instance-id>portal#classic:/web/NavigationPortlet/toolbar</instance-id>
     <show-info-bar>false</show-info-bar>
   </application>
  
   <application>
     <instance-id>portal#classic:/web/BreadcumbsPortlet/breadcumbs</instance-id>
     <show-info-bar>false</show-info-bar>
   </application>
   
 
   <page-body> </page-body>
   
   <application>
     <instance-id>portal#classic:/web/FooterPortlet/footer</instance-id>
     <show-info-bar>false</show-info-bar>
   </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 the portal will have. The syntax is simple, using nested node tags. Each node references a page defined in pages.xml file (explained next).

When #{...} syntax is used, the enclosed property name serves as a key that is automatically passed to internationalization mechanism so the literal property name is replaced by a localized value taken from the associated properties file matching the current locale.



 
<?xml version="1.0" encoding="ISO-8859-1"?>
<node-navigation
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_0 http://www.gatein.org/xml/ns/gatein_objects_1_0"
    xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_0">
  <priority>1</priority>
  <page-nodes>
     <node>
        <uri>home</uri>
        <name>home</name>
        <label>#{portal.classic.home}</label>
        <page-reference>portal::classic::homepage</page-reference>      
     </node>   
     <node>
        <uri>sitemap</uri>
        <name>sitemap</name>
        <label>#{portal.classic.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.



  
<?xml version="1.0" encoding="ISO-8859-1"?>
<page-set
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_0 http://www.gatein.org/xml/ns/gatein_objects_1_0"
    xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_0">

  <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>

portlet-preferences.xml

Porlet instances can be associated with portlet-preferences that override the ones defined in portlet.xml of the portlet application war (TODO: clarify which file in which war).


<?xml version="1.0" encoding="ISO-8859-1"?>
<portlet-preferences-set>
  <portlet-preferences>
    <owner-type>portal</owner-type>
    <owner-id>classic</owner-id>
    <window-id>portal#classic:/web/BannerPortlet/banner</window-id>
    <preference>
      <name>template</name>
      <value>par:/groovy/groovy/webui/component/UIBannerPortlet.gtmpl</value>
      <read-only>false</read-only>
    </preference>
  </portlet-preferences>
  <portlet-preferences>
    <owner-type>portal</owner-type>
    <owner-id>classic</owner-id>
    <window-id>portal#classic:/web/NavigationPortlet/toolbar</window-id>
    <preference>
      <name>useAJAX</name>
      <value>true</value>
      <read-only>false</read-only>
    </preference>
  </portlet-preferences>
  <portlet-preferences>
    <owner-type>portal</owner-type>
    <owner-id>classic</owner-id>
    <window-id>portal#classic:/web/FooterPortlet/footer</window-id>
    <preference>
      <name>template</name>
      <value>par:/groovy/groovy/webui/component/UIFooterPortlet.gtmpl</value>
      <read-only>false</read-only>
    </preference>
  </portlet-preferences>
  
  
  <portlet-preferences>
    <owner-type>portal</owner-type>
    <owner-id>classic</owner-id>
    <window-id>portal#classic:/web/GroovyPortlet/groovyportlet</window-id>
    <preference>
      <name>template</name>
      <value>par:/groovy/groovy/webui/component/UIGroovyPortlet.gtmpl</value>
      <read-only>false</read-only>
    </preference>
  </portlet-preferences>
</portlet-preferences-set>

User navigation is the set of nodes and pages that are owned by a user. They are part of the user's dashboard.

Three files configure the user navigation (navigation.xml, pages.xml and portlet-preferences.xml). They are located in the directory "portal.war/WEB-INF/conf/portal/users/{userName}".

This directory also contains a gadgets.xml file (formerly called widgets.xml). This file defines the gadgets located in the user's workspace.

The user's workspace is located at the left hand side of the page and access is restricted to some privileged users, see Section 6.1, “Predefined User Configuration”


<?xml version="1.0" encoding="ISO-8859-1"?>
<widgets>
  <owner-type>user</owner-type>
  <owner-id>root</owner-id>
 
  <container id="Information">
    <name>Information</name>
    <description>Information's Description</description>
    <application>
      <instance-id>user#root:/GateInWidgetWeb/WelcomeWidget/WelcomeWidget1</instance-id>
      <application-type>GateInWidget</application-type>
    </application>
      
    <application>
      <instance-id>user#root:/GateInWidgetWeb/StickerWidget/StickerWidget</instance-id>
      <application-type>GateInWidget</application-type> 
    </application>
    
    <application>
      <instance-id>user#root:/GateInWidgetWeb/InfoWidget/InfoWidget1</instance-id>
      <application-type>GateInWidget</application-type>
    </application>
  </container>
  
  <container id="Calendar">
    <name>Calendar</name>
    <description>Calendar's Description</description>
    <application>
      <instance-id>user#root:/GateInWidgetWeb/CalendarWidget/CalendarWidget</instance-id>
      <application-type>GateInWidget</application-type> 
    </application>
  </container> 
 
</widgets>

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.9, “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.8, “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 Java Community Process (JCP) uses Java Specification Requests (JSRs) to define proposed specifications and technologies designed for the Java platform.

The Portlet Specifications aim at defining portlets that can be used by any JSR-168 (Portlet 1.0) or JSR-286 (Portlet 2.0) portlet container.

Most Java EE (Enterprise Edition) portals include at least one compliant portlet container, and GateIn 3.2 is no exception. In fact, GateIn 3.2 includes a container that supports both versions.

This chapter gives a brief overview of the Portlet Specifications but portlet developers are strongly encouraged to read the JSR-286 Portlet Specification .

GateIn 3.2 is fully JSR-286 compliant. Any JSR-168 or JSR-286 portlet operates as it is mandated by the respective specifications inside the portal.

The tutorials contained in this chapter are targeted toward portlet developers. It is also recommend that developers read and understand the JSR-286 Portlet Specification .

Maven

This example is using Maven to compile and build the web archive. Maven versions can be downloaded from maven.apache.org

This section describes how to deploy a portlet in GateIn 3.2. A sample portlet called SimplestHelloWorld is located in the examples directory at the root of your GateIn 3.2 binary package. This sample is used in the following examples.

Below is the SimplestHelloWorldPortlet/src/main/java/org/gatein/portal/examples/portlets/SimplestHelloWorldPortlet.java Java source:

package org.gatein.portal.examples.portlets;

 
import java.io.IOException;
import java.io.PrintWriter;
 
import javax.portlet.GenericPortlet;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
 
(1)public class SimplestHelloWorldPortlet extends GenericPortlet
{
   public void doView(RenderRequest request, 
(2)                       RenderResponse response) throws IOException
   {
(3)      PrintWriter writer = response.getWriter();
(4)      writer.write("Hello World !");
(5)      writer.close();
   }
}

1

All portlets must implement the javax.portlet.Portlet interface. The portlet API provides a convenient implementation of this interface.

The javax.portlet.Portlet interface uses the javax.portlet.GenericPortlet class which implements the Portlet render method to dispatch to abstract mode-specific methods. This makes it easier to support the standard portlet modes.

Portlet render also provides a default implementation for the processAction, init and destroy methods. It is recommended to extend GenericPortlet for most cases.

2

If only the view mode is required, then only the doView method needs to be implemented. The GenericPortletrender implementation calls our implementation when the view mode is requested.

3

Use the RenderResponse to obtain a writer to be used to produce content.

4

Write the markup to display.

5

Closing the writer.

GateIn 3.2 requires certain descriptors to be included in a portlet WAR file. These descriptors are defined by the Jave EE (web.xml) and Portlet Specification (portlet.xml).

Below is an example of the SimplestHelloWorldPortlet/WEB-INF/portlet.xml file. This file must adhere to its definition in the JSR-286 Portlet Specification. More than one portlet application may be defined in this file:

<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd 
                                         http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
   version="2.0">
   <portlet>
      <portlet(1)-name>SimplestHelloWorldPortlet</portlet-name>
      <portlet(2)-class>
         org.gatein.portal.examples.portlets.SimplestHelloWorldPortlet
      </portlet-class>
      <support(3)s>
        <mime-type>text/html</mime-type>
      </supports>
      <portlet(4)-info>
          <title>Simplest Hello World Portlet</title>
      </portlet-info>
   </portlet>
</portlet-app>

1

Define the portlet name. It does not have to be the class name.

2

The Fully Qualified Name (FQN) of your portlet class must be declared here.

3

The <supports> element declares all of the markup types that a portlet supports in the render method. This is accomplished via the <mime-type> element, which is required for every portlet.

The declared MIME types must match the capability of the portlet. It allows administrators to pair which modes and window states are supported for each markup type.

This does not have to be declared as all portlets must support the view portlet mode.

Use the <mime-type> element to define which markup type the portlet supports. In the example above this is text/html. This section tells the portal to only output HTML.

4

When rendered, the portlet's title is displayed as the header in the portlet window, unless it is overridden programmatically. In the example above the title would be Simplest Hello World Portlet .

This section discusses:

The code below is from the JSPHelloUser/src/main/java/org/gatein/portal/examples/portlets/JSPHelloUserPortlet.java Java source. It is split in different pieces.

package org.gatein.portal.examples.portlets;

 
import java.io.IOException;
 
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.UnavailableException;
 
public class JSPHelloUserPortlet extends GenericPortlet
{
    
   public void doView(RenderRequest request, RenderResponse response)
(1)       throws PortletException, IOException
   {
      String sYourName = (String) request.getParameter("yourname");
(2)      if (sYourName != null)
      {
         request.setAttribute("yourname", sYourName);
         PortletRequestDispatcher prd = 
(3)            getPortletContext().getRequestDispatcher("/jsp/hello.jsp");
(4)         prd.include(request, response);
      }
      else
      {
         PortletRequestDispatcher prd = getPortletContext().getRequestDispatcher("/jsp/welcome.jsp");
         prd.include(request, response);
      }
   }
...

1

Override the doView method (as in the first tutorial).

2

This entry attempts to obtain the value of the render parameter named yourname. If defined it should redirect to the hello.jsp JSP page, otherwise to the welcome.jsp JSP page.

3

Get a request dispatcher on a file located within the web archive.

4

Perform the inclusion of the markup obtained from the JSP.

As well as the VIEW portlet mode, the specification defines two other modes; EDIT and HELP.

These modes need to be defined in the portlet.xml descriptor. This will enable the corresponding buttons on the portlet's window.

The generic portlet that is inherited dispatches the different views to the methods: doView , doHelp and doEdit.

...

   protected void doHelp(RenderRequest rRequest, RenderResponse rResponse) throws PortletException, IOException,
         UnavailableException
   {
      rResponse.setContentType("text/html");
      PortletRequestDispatcher prd = getPortletContext().getRequestDispatcher("/jsp/help.jsp");
      prd.include(rRequest, rResponse);
   }
   protected void doEdit(RenderRequest rRequest, RenderResponse rResponse) throws PortletException, IOException,
         UnavailableException
   {
      rResponse.setContentType("text/html");
      PortletRequestDispatcher prd = getPortletContext().getRequestDispatcher("/jsp/edit.jsp");
      prd.include(rRequest, rResponse);
   }
...

Portlet calls happen in one or two phases. One when the portlet is rendered and two when the portlet is actioned then rendered.

An action phase is a phase where some state changes. The render phase will have access to render parameters that will be passed each time the portlet is refreshed (with the exception of caching capabilities).

The code to be executed during an action has to be implemented in the processAction method of the portlet.

...

(1)         public void processAction(ActionRequest aRequest, ActionResponse aResponse) throws PortletException, IOException,
         UnavailableException
   {
(2)      String sYourname = (String) aRequest.getParameter("yourname");
(3)      aResponse.setRenderParameter("yourname", sYourname);
   }
...

1

processAction is the method from GernericPorlet to override for the action phase.

2

Here the parameter is retrieved through an action URL .

3

The value of yourname is kept to make it available in the rendering phase. The previous line simply copies an action parameters to a render parameter for this example.

The help.jsp and edit.jsp files are very simple. Note that CSS styles are used as defined in the portlet specification. This ensures that the portlet will render well within the theme and across portal vendors.


<div class="portlet-section-header">Help mode</div>
<div class="portlet-section-body">This is the help mode, a convenient place to give the user some help information.</div>

<div class="portlet-section-header">Edit mode</div>
<div class="portlet-section-body">This is the edit mode, a convenient place to let the user change his portlet preferences.</div>

The landing page contains the links and form to call our portlet:

<%@ taglib uri(1)="http://java.sun.com/portlet" prefix="portlet" %>
 
<div class="portlet-section-header">Welcome !</div>
 
<br/>
 
<div class="portlet-font">Welcome on the JSP Hello User portlet,
my name is GateIn Portal. What's yours ?</div>
 
<br/>
 
<div class="portlet-font">Method 1: We simply pass the parameter to the render phase:<br/>
<a href="<port(2)let:renderURL><portlet:param name="yourname" value="John Doe"/>
                </portlet:renderURL>">John Doe</a></div>
 
<br/>
 
<div class="portlet-font">Method 2: We pass the parameter to the render phase, using valid XML:
Please check the source code to see the difference with Method 1.
<portlet:rende(3)rURL var="myRenderURL">
    <portlet:param name="yourname" value='John Doe'/>
</portlet:renderURL>
<br/>
<a href="<%= m(4)yRenderURL %>">John Doe</a></div>
 
<br/>
 
<div class="portlet-font">Method 3: We use a form:<br/>
 
<portlet:actio(5)nURL var="myActionURL"/>
<form action="(6)<%= myActionURL %>" method="POST">
         <span class="portlet-form-field-label">Name:</span>
         <input class="portlet-form-input-field" type="text" name="yourname"/>
         <input class="portlet-form-button" type="Submit"/>
</form>
</div>

1

The portlet taglib, needs to be declared.

2

The first method showed here is the simplest one. portlet:renderURL will create a URL that calls the render phase of the current portlet and append the result at the place of the markup (within a tag). A parameter is also added directly to the URL.

3

In this method the var attribute is used. This avoids having one XML tag within another. Instead of printing the url the portlet:renderURL tag will store the result in the referenced variable ( myRenderURL).

4

The variable myRenderURL is used like any other JSP variable.

5

The third method mixes form submission and action request. Again, a temporary variable is used to put the created URL into.

6

The action URL is used in HTML form.

In the third method the action phase is triggered first then the render phase is triggered, which outputs some content back to the web browser based on the available render parameters.

In order to write a portlet using JSF a 'bridge' is needed. This software allows developers to write a portlet application as if it was a JSF application. The bridge then negotiates the interactions between the two layers.

An example of the JBoss Portlet Bridge is available in examples/JSFHelloUser. The configuration is slightly different from a JSP application. This example can be used as a base to configure instead of creating a new application.

As in any JSF application, the file faces-config.xml is required. It must contain the following information:


<faces-config>
...
    <application>
      <view-handler>org.jboss.portletbridge.application.PortletViewHandler</view-handler>
      <state-manager>org.jboss.portletbridge.application.PortletStateManager</state-manager>
   </application>
...
</faces-config>

The portlet bridge libraries must be available and are usually bundled with the WEB-INF/lib directory of the web archive.

The other difference compare to a regular portlet application, can be found in the portlet descriptor. All details about it can be found in the JSR-301 specification that the JBoss Portlet Bridge implements.

The Global metadata is declared in the portlet.xml file conforming with Portlet 2.0 's XSD.


<portlet-app version="1.0" xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">

</portlet-app>

A gadget is a mini web application, embedded in a web page and running on an application server platform. These small applications help users perform various tasks.

GateIn 3.2 supports gadgets such as: Todo gadget, Calendar gadget, Calculator gadget, Weather Forecasts and and RSS Reader.

Important

The following sections require more textual information.

The predefined membership types are specified in the membershipType field of the OrganizationConfig plugin parameter.


<field name="membershipType">
  <collection type="java.util.ArrayList">
    <value>
      <object type="org.exoplatform.services.organization.OrganizationConfig$MembershipType">
        <field name="type">
          <string>member</string>
        </field>
        <field name="description">
          <string>member membership type</string>
        </field>
      </object>
    </value>
    <value>
      <object type="org.exoplatform.services.organization.OrganizationConfig$MembershipType">
        <field name="type">
          <string>owner</string>
        </field>
        <field name="description">
          <string>owner membership type</string>
        </field>
      </object>
     </value>
     <value>
       <object type="org.exoplatform.services.organization.OrganizationConfig$MembershipType">
         <field name="type">
           <string>validator</string>
         </field>
         <field name="description">
           <string>validator membership type</string>
         </field>
       </object>
     </value>
   </collection>
</field>

The plugin of type org.exoplatform.services.organization.impl.NewUserEventListener specifies which groups all the newly created users should become members of. It specifies the groups and the memberships to use (while group is just a set of users, a membership type represents a user's role within a group). It also specifies a list of users that should not be processed (i.e. administrative users like 'root').


<component-plugin>
  <name>new.user.event.listener</name>
  <set-method>addListenerPlugin</set-method>
  <type>org.exoplatform.services.organization.impl.NewUserEventListener</type>
  <description>this listener assign group and membership to a new created user</description>
  <init-params>
    <object-param>
      <name>configuration</name>
      <description>description</description>
      <object type="org.exoplatform.services.organization.impl.NewUserConfig">
        <field  name="group">
          <collection type="java.util.ArrayList">
            <value>
              <object type="org.exoplatform.services.organization.impl.NewUserConfig$JoinGroup">
                <field name="groupId"><string>/user</string></field>
                <field name="membership"><string>member</string></field>
              </object>
            </value>               
          </collection>
        </field>
        <field  name="ignoredUser">
          <collection type="java.util.HashSet">
            <value><string>exo</string></value>
            <value><string>root</string></value>
            <value><string>company</string></value>
            <value><string>community</string></value>
          </collection>
        </field>
      </object>
    </object-param>
  </init-params>
</component-plugin>

GateIn 3.2 uses PicketLink IDM component to keep the necessary identity information (users, groups, memberships, etc.). While legacy interfaces are still used (org.exoplatform.services.organization) for identity management, there is a wrapper implementation that delegates to PicketLink IDM framework.

This section doesn't provide information about PicketLink IDM and its configuration. Please, refer to the appropriate project documentation (http://jboss.org/picketlink/IDM.html) for further information.

Note

It is important to fully understand the concepts behind this framework design before changing the default configuration.

The identity model represented in 'org.exoplatform.services.organization' interfaces and the one used in PicketLink IDM have some major differences.

TODO: tell more about org.exoplatform.services.organization

For example: PicketLink IDM provides greater abstraction. It is possible for groups in IDM framework to form memberships with many parents (which requires recursive ID translation), while GateIn model allows only pure tree-like membership structures.

Additionally, GateIn membership concept needs to be translated into the IDM Role concept. Therefore PicketLink IDM model is used in a limited way. All these translations are applied by the integration layer.

The main configuration file is idm-configuration.xml:

<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_0.xsd http://www.exoplaform.org/xml/ns/kernel_1_0.xsd"
               xmlns="http://www.exoplaform.org/xml/ns/kernel_1_0.xsd">
 
   <component>(1)
        <key>org.exoplatform.services.organization.idm.PicketLinkIDMService</key>
      <type>org.exoplatform.services.organization.idm.PicketLinkIDMServiceImpl</type>
      <init-params>
         <value-param>
            <name>config</name>
            <value>war:/conf/organization/idm-config.xml</value>
         </value-param>
         <value-param>
            <name>portalRealm</name>
            <value>realm${container.name.suffix}</value>
         </value-param>
       </init-params>
   </component>
 
 
   <component>
      <key>org(2).exoplatform.services.organization.OrganizationService</key>
      <type>org.exoplatform.services.organization.idm.PicketLinkIDMOrganizationServiceImpl</type>
      <init-params>
      <object-param>
        <name>configuration</name>
        <object type="org.exoplatform.services.organization.idm.Config">
          <field name="useParentIdAsGroupType">
            <boolean>true</boolean>
          </field>
 
          <field name="forceMembershipOfMappedTypes">
            <boolean>true</boolean>
          </field>
 
          <field name="pathSeparator">
            <string>.</string>
          </field>
 
          <field name="rootGroupName">
            <string>GTN_ROOT_GROUP</string>
          </field>
 
          <field name="groupTypeMappings">
            <map type="java.util.HashMap">
              <entry>
                <key><string>/</string></key>
                <value><string>root_type</string></value>
              </entry>
 
              <!-- Sample mapping -->
              <!--
              <entry>
                <key><string>/platform/*</string></key>
                <value><string>platform_type</string></value>
              </entry>
              <entry>
                <key><string>/organization/*</string></key>
                <value><string>organization_type</string></value>
              </entry>
              -->
 
            </map>
          </field>
 
          <field name="associationMembershipType">
            <string>member</string>
          </field>
 
          <field name="ignoreMappedMembershipType">
            <boolean>false</boolean>
          </field>
        </object>
      </object-param>
    </init-params>
 
 
   </component>
 
</configuration>

1

The org.exoplatform.services.organization.idm.PicketLinkIDMServiceImpl service has the following options:

config

(value-param)

PicketLink IDM configuration file

hibernate.properties

(properties-param)

A list of hibernate properties used to create SessionFactory that will be injected to JBoss Identity IDM configuration registry.

hibernate.annotations

A list of annotated classes that will be added to Hibernate configuration.

hibernate.mappings

A list of xml files that will be added to hibernate configuration as mapping files.

jndiName

(value-param)

If the 'config' parameter is not provided, this parameter will be used to perform JNDI lookup for IdentitySessionFactory

portalRealm

(value-param)

The realm name that should be used to obtain proper IdentitySession. The default is 'PortalRealm'.

2

The org.exoplatform.services.organization.idm.PicketLinkIDMOrganizationServiceImpl key is a main entrypoint implementing org.exoplatform.services.organization.OrganizationService and is dependant on org.exoplatform.services.organization.idm.PicketLinkIDMService

org.exoplatform.services.organization.idm.PicketLinkIDMOrganizationServiceImpl service has the following options defined as fields of object-param of type org.exoplatform.services.organization.idm.Config:

defaultGroupType

The name of the PicketLink IDM GroupType that will be used to store groups. The default is 'GTN_GROUP_TYPE'.

rootGroupName

The name of the PicketLink IDM Group that will be used as a root parent. The default is 'GTN_ROOT_GROUP'

passwordAsAttribute

This parameter specifies if a password should be stored using PicketLink IDM Credential object or as a plain attribute. The default is false.

useParentIdAsGroupType

This parameter stores the parent ID path as a group type in PicketLink IDM for any IDs not mapped with a specific type in 'groupTypeMappings'. If this option is set to false, and no mappings are provided under 'groupTypeMappings', then only one group with the given name can exist in the GateIn 3.2 group tree.

pathSeparator

When 'userParentIdAsGroupType is set to true, this value will be used to replace all "/" characters in IDs. The "/" character is not allowed to be used in group type name in PicketLink IDM.

associationMembershipType

If this option is used, then each Membership, created with MembrshipType that is equal to the value specified here, will be stored in PicketLink IDM as simple Group-User association.

groupTypeMappings

This parameter maps groups added with GateIn 3.2 API as children of a given group ID, and stores them with a given group type name in PicketLink IDM.

If the parent ID ends with "/*", then all child groups will have the mapped group type. Otherwise, only direct (first level) children will use this type.

This can be leveraged by LDAP if LDAP DN is configured in PicketLink IDM to only store a specific group type. This will then store the given branch in GateIn 3.2 group tree, while all other groups will remain in the database.

forceMembershipOfMappedTypes

Groups stored in PicketLink IDM with a type mapped in 'groupTypeMappings' will automatically be members under the mapped parent. Group relationships linked by PicketLink IDM group association will not be necessary.

This parameter can be set to false if all groups are added via GateIn 3.2 APIs. This may be useful with LDAP configuration as, when set to true, it will make every entry added to LDAP appear in GateIn 3.2. This, however, is not true for entries added via GateIn 3.2 management UI.

ignoreMappedMembershipType

If "associationMembershipType" option is used, and this option is set to true, then Membership with MembershipType configured to be stored as PicketLink IDM association will not be stored as PicketLink IDM Role.

Additionally, JBossIDMOrganizationServiceImpl uses those defaults to perform identity management operations

  • GateIn 3.2 User interface properties fields are persisted in JBoss Identity IDM using those attributes names: firstName, lastName, email, createdDate, lastLoginTime, organizationId, password (if password is configured to be stored as attribute)

  • GateIn 3.2 Group interface properties fields are persisted in JBoss Identity IDM using those attributes names: label, description

  • GateIn 3.2 MembershipType interface properties fields are persisted in JBoss Identity IDM using those RoleType properties: description, owner, create_date, modified_date

A sample PicketLink IDM configuration file is shown below. To understand all the options it contains, please refer to the PicketLink IDM Reference Guide


<jboss-identity xmlns="urn:jboss:identity:idm:config:v1_0_beta"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="urn:jboss:identity:idm:config:v1_0_alpha identity-config.xsd">
    <realms>
        <realm>
            <id>PortalRealm</id>
            <repository-id-ref>PortalRepository</repository-id-ref>
            <identity-type-mappings>
                <user-mapping>USER</user-mapping>
            </identity-type-mappings>
        </realm>
    </realms>
    <repositories>
        <repository>
            <id>PortalRepository</id>
            <class>org.jboss.identity.idm.impl.repository.WrapperIdentityStoreRepository</class>
            <external-config/>
            <default-identity-store-id>HibernateStore</default-identity-store-id>
            <default-attribute-store-id>HibernateStore</default-attribute-store-id>
        </repository>
    </repositories>
    <stores>
        <attribute-stores/>
        <identity-stores>
            <identity-store>
                <id>HibernateStore</id>
                <class>org.jboss.identity.idm.impl.store.hibernate.HibernateIdentityStoreImpl</class>
                <external-config/>
                <supported-relationship-types>
                    <relationship-type>JBOSS_IDENTITY_MEMBERSHIP</relationship-type>
                    <relationship-type>JBOSS_IDENTITY_ROLE</relationship-type>
                </supported-relationship-types>
                <supported-identity-object-types>
                    <identity-object-type>
                        <name>USER</name>
                        <relationships/>
                        <credentials>
                            <credential-type>PASSWORD</credential-type>
                        </credentials>
                        <attributes/>
                        <options/>
                    </identity-object-type>
                </supported-identity-object-types>
                <options>
                    <option>
                        <name>hibernateSessionFactoryRegistryName</name>
                        <value>hibernateSessionFactory</value>
                    </option>
                    <option>
                        <name>allowNotDefinedIdentityObjectTypes</name>
                        <value>true</value>
                    </option>
                    <option>
                        <name>populateRelationshipTypes</name>
                        <value>true</value>
                    </option>
                    <option>
                        <name>populateIdentityObjectTypes</name>
                        <value>true</value>
                    </option>
                    <option>
                        <name>allowNotDefinedAttributes</name>
                        <value>true</value>
                    </option>
                    <option>
                        <name>isRealmAware</name>
                        <value>true</value>
                    </option>
                </options>
            </identity-store>
        </identity-stores>
    </stores>
</jboss-identity>

The exo.platform.services.organization package has five main components: user, user profile, group, membership type and membership. There is an additional component that serves as an entry point into Organization API - OrganizationService component, that provides handling functionality for the five components.

The User component contains basic information about a user - such as username, password, first name, last name, and email. The User Profile component contains extra information about a user, such as user's personal information, and business information. You can also add additional information about a user if your application requires it. The Group component contains a group graph. The Membership Type component contains a list of predefined membership types. Finally, the Membership component connects a User, a Group and a Membership Type.

A user can have one or more memberships within a group, for example: user A can have the 'member' and 'admin' memberships in group /user. A user belongs to a group if he has at least one membership in that group.

Exposing the Organization API to developers the OrganizationService component provides developers with access to handler objects for managing each of the five components - UserHandler, UserProfileHandler, GroupHandler, MembershipTypeHandler, and MembershipHandler.

The five central API components are really designed like persistent entities, and handlers are really specified like data access objects (DAO).

Organization API simply describes a contract, meaning it is not a concrete implementation. The described components are interfaces, allowing for different concrete implementations. In practial terms that means, you can replace the existing implementation with a different one.

GateIn 3.2 provides some form of Single Sign On (SSO) as an integration and aggregation platform.

When logging into the portal users gain access to many systems through portlets using a single identity. In many cases, however, the portal infrastructure must be integrated with other SSO enabled systems. There are many different Identity Management solutions available. In most cases each SSO framework provides a unique way to plug into a Java EE application.

In this tutorial, the SSO server is installed in a Tomcat installation. Tomcat can be obtained from http://tomcat.apache.org.

All the packages required for setup can be found in a zip file located at: http://repository.jboss.org/maven2/org/gatein/sso/sso-packaging. In this document we will call the directory where the file is extracted $GATEIN_SSO_HOME.

Users are advised to not run any portal extensions that could override the data when manipulating the gatein.ear file directly.

Remove $JBOSS_HOME/server/default/deploy/gatein-sample-extension.ear and $JBOSS_HOME/server/default/deploy/gatein-sample-portal.ear which are packaged by default with GateIn 3.2.

This Single Sign On plugin enables seamless integration between GateIn 3.2 and the CAS Single Sign On Framework. Details about CAS can be found here.

The integration consists of two parts; the first part consists of installing or configuring a CAS server, the second part consists of setting up the portal to use the CAS server.

First, set up the server to authenticate against the portal login module. In this example the CAS server will be installed on Tomcat.

CAS can be downloaded from http://www.jasig.org/cas/download.

Extract the downloaded file into a suitable location. This location will be referred to as $CAS_HOME in the following example.

To configure the web archive as desired, the simplest way is to make the necessary changes directly in CAS codebase.

First, we need to change the default authentication handler with the one provided by GateIn 3.2.

The CAS Server Plugin makes secure authentication callbacks to a RESTful service installed on the remote GateIn server in order to authenticate a user.

In order for the plugin to function correctly, it needs to be properly configured to connect to this service. This configuration is done via the cas.war/WEB-INF/deployerConfigContext.xml file.

  1. Open CAS_HOME/cas-server-webapp/src/main/webapp/WEB-INF/deployerConfigContext.xml

  2. Replace:

     <!--
      | Whereas CredentialsToPrincipalResolvers identify who it is some Credentials might authenticate, 
      | AuthenticationHandlers actually authenticate credentials.  Here e declare the AuthenticationHandlers that
      | authenticate the Principals that the CredentialsToPrincipalResolvers identified.  CAS will try these handlers in turn
      | until it finds one that both supports the Credentials presented and succeeds in authenticating.
      +-->
     <property name="authenticationHandlers">
       <list>
         <!--
          | This is the authentication handler that authenticates services by means of callback via SSL, thereby validating
          | a server side SSL certificate.
          +-->
         <bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
               p:httpClient-ref="httpClient" />
         <!--
          | This is the authentication handler declaration that every CAS deployer will need to change before deploying CAS 
          | into production.  The default SimpleTestUsernamePasswordAuthenticationHandler authenticates UsernamePasswordCredentials
          | where the username equals the password.  You will need to replace this with an AuthenticationHandler that implements your
          | local authentication strategy.  You might accomplish this by coding a new such handler and declaring
          | edu.someschool.its.cas.MySpecialHandler here, or you might use one of the handlers provided in the adaptors modules.
          +-->
         <bean
            class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />
       </list>
     </property>
    

  3. With the following (Make sure to set the host, port and context with the values corresponding to your portal). Also available in GATEIN_SSO_HOME/cas/plugin/WEB-INF/deployerConfigContext.xml.

    <!--
     | Whereas CredentialsToPrincipalResolvers identify who it is some Credentials might authenticate, 
     | AuthenticationHandlers actually authenticate credentials.  Here we declare the AuthenticationHandlers that
     | authenticate the Principals that the CredentialsToPrincipalResolvers identified.  CAS will try these handlers in turn
     | until it finds one that both supports the Credentials presented and succeeds in authenticating.
     +-->
     <property name="authenticationHandlers">
       <list>
         <!--
          | This is the authentication handler that authenticates services by means of callback via SSL, thereby validating
          | a server side SSL certificate.
          +-->
         <bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
               p:httpClient-ref="httpClient" />
         <!--
          | This is the authentication handler declaration that every CAS deployer will need to change before deploying CAS 
          | into production.  The default SimpleTestUsernamePasswordAuthenticationHandler authenticates UsernamePasswordCredentials
          | where the username equals the password.  You will need to replace this with an AuthenticationHandler that implements your
          | local authentication strategy.  You might accomplish this by coding a new such handler and declaring
          | edu.someschool.its.cas.MySpecialHandler here, or you might use one of the handlers provided in the adaptors modules.
          +-->
         <!-- Integrates with the Gatein Authentication Service to perform authentication -->
         <!--
          | Note: Modify the Plugin Configuration based on the actual information of a GateIn instance.
          | The instance can be anywhere on the internet...Not necessarily on localhost where CAS is running 
          +-->
         <bean class="org.gatein.sso.cas.plugin.AuthenticationPlugin">
            <property name="gateInHost"><value>localhost</value></property>
            <property name="gateInPort"><value>8080</value></property>
            <property name="gateInContext"><value>portal</value></property>
         </bean>
       </list>
     </property>
    

  4. Copy GATEIN_SSO_HOME/cas/plugin/WEB-INF/lib/sso-cas-plugin-<VERSION>.jar and GATEIN_SSO_HOME/cas/plugin/WEB-INF/lib/commons-httpclient-<VERSION>.jar into the CAS_HOME/cas-server-webapp/src/main/webapp/WEB-INF/lib created directory.

  5. Get an installation of Tomcat and extract it into a suitable location (which will be called TOMCAT_HOME for these instructions).

    Change the default port to avoid a conflict with the default GateIn 3.2 (for testing purposes). Edit TOMCAT_HOME/conf/server.xml and replace the 8080 port to 8888.

    Note

    If GateIn 3.2 is running on the same machine as Tomcat, other ports need to be changed in addition to 8080 in order to avoid port conflicts. They can be changed to any free port. For example, you can change admin port from 8005 to 8805, and AJP port from 8009 to 8809.

  6. Go to CAS_HOME/cas-server-webapp and execute the command:

    mvn install
    

  7. Copy CAS_HOME/cas-server-webapp/target/cas.war into TOMCAT_HOME/webapps.

    Tomcat should start and be accessible at http://localhost:8888/cas. Note that at this stage login won't be available.

To utilize the Central Authentication Service, GateIn 3.2 needs to redirect all user authentication to the CAS server.

Information about where the CAS is hosted must be properly configured within the GateIn 3.2 instance. The required configuration is done by modifying three files:

Once these changes have been made, all links to the user authentication pages will redirect to the CAS centralized authentication form.

This Single Sign On plugin enables seamless integration between GateIn 3.2 and the JOSSO Single Sign On Framework. Details about JOSSO can be found here.

Setting up this integration involves two steps. The first step is to install or configure a JOSSO server, and the second is to set up the portal to use the JOSSO server.

The next part of the process is to redirect all user authentication to the JOSSO server.

Information about where the JOSSO server is hosted must be properly configured within the GateIn 3.2 instance. The required configuration is done by modifying four files:

From now on, all links redirecting to the user authentication pages will redirect to the JOSSO centralized authentication form.

This Single Sign On plugin enables seamless integration between GateIn 3.2 and the OpenSSO Single Sign On Framework. Details about OpenSSO can be found here.

Setting up this integration involves two steps. The first step is to install or configure an OpenSSO server, and the second is to set up the portal to use the OpenSSO server.

This section details the setting up of OpenSSO server to authenticate against the GateIn 3.2 login module.

In this example the OpenSSO server will be installed on Tomcat.

OpenSSO can be downloaded from https://opensso.dev.java.net/public/use/index.html.

Once downloaded, extract the package into a suitable location. This location will be referred to as OPENSSO_HOME in this example.

To configure the web server as desired, it is simpler to directly modify the sources.

The first step is to add the GateIn 3.2 Authentication Plugin:

The plugin makes secure authentication callbacks to a RESTful service installed on the remote GateIn 3.2 server in order to authenticate a user.

In order for the plugin to function correctly, it needs to be properly configured to connect to this service. This configuration is done via the opensso.war/config/auth/default/AuthenticationPlugin.xml file.

  1. Obtain a copy of Tomcat and extract it into a suitable location (this location will be referred to as TOMCAT_HOME in this example).

  2. Change the default port to avoid a conflict with the default GateIn 3.2 port (for testing purposes). Do this by editing TOMCAT_HOME/conf/server.xml and replacing the 8080 port to 8888.

  3. Ensure the TOMCAT_HOME/webapps/opensso/config/auth/default/AuthenticationPlugin.xml file looks like this:

    <?xml version='1.0' encoding="UTF-8"?>
    
    <!DOCTYPE ModuleProperties PUBLIC "=//iPlanet//Authentication Module Properties XML Interface 1.0 DTD//EN"
              "jar://com/sun/identity/authentication/Auth_Module_Properties.dtd">
    
    <ModuleProperties moduleName="AuthenticationPlugin" version="1.0" >
      <Callbacks length="2" order="1" timeout="60"
                 header="GateIn OpenSSO Login" >    
        <NameCallback>
          <Prompt>
    		Username
          </Prompt>
        </NameCallback>
        <PasswordCallback echoPassword="false" >
          <Prompt>
    		Password
          </Prompt>
        </PasswordCallback>
      </Callbacks>
    </ModuleProperties>
    

  4. Copy GATEIN_SSO_HOME/opensso/plugin/WEB-INF/lib/sso-opensso-plugin-<VERSION>.jar, GATEIN_SSO_HOME/opensso/plugin/WEB-INF/lib/commons-httpclient-<VERSION>.jar, and GATEIN_SSO_HOME/opensso/plugin/WEB-INF/lib/commons-logging-<VERSION>.jar into the Tomcat directory at TOMCAT_HOME/webapps/opensso/WEB-INF/lib.

  5. Copy GATEIN_SSO_HOME/opensso/plugin/WEB-INF/classes/gatein.properties into TOMCAT_HOME/webapps/opensso/WEB-INF/classes

  6. Tomcat should start and be able to access http://localhost:8888/opensso/UI/Login?realm=gatein. Login will not be available at this point.

Configure "gatein" realm:

  1. Direct your browser to http://localhost:8888/opensso

  2. Create default configuration

  3. Login as amadmin and then go to tab "Configuration" -> tab "Authentication" -> link "Core" -> add new value and fill in the class name "org.gatein.sso.opensso.plugin.AuthenticationPlugin". This step is really important. Without it AuthenticationPlugin is not available among other OpenSSO authentication modules.

  4. Go to tab "Access control" and create new realm called "gatein".

  5. Go to "gatein" realm and click on "Authentication" tab. At the bottom in the section "Authentication chaining" click on "ldapService". Here change the selection from "Datastore", which is the default module in the authentication chain, to "AuthenticationPlugin". This enables authentication of "gatein" realm by using GateIn REST service instead of the OpenSSO LDAP server.

  6. Go to "Advanced properties" and change UserProfile from "Required" to "Dynamic". This step is needed because GateIn 3.2 users are not in OpenSSO Datastore (LDAP server), so their profiles can't be obtained if "Required" is active. By using "Dynamic" all new users are automatically created in OpenSSO datastore after successful authentication.

  7. Increase the user privileges to allow REST access. Go to "Access control" -> Top level realm -> "Privileges" tab -> All authenticated users, and check the last two checkboxes:

    • Read and write access only for policy properties

    • Read and write access to all realm and policy properties

  8. Do the same for "gatein" realm.

TODO: The above OpenSSO manual configuration could be replaced by configuration files prepared in advance

The next part of the process is to redirect all user authentication to the OpenSSO server.

Information about where the OpenSSO server is hosted must be properly configured within the Enterprise Portal Platform instance. The required configuration is done by modifying three files:

From now on, all links redirecting to the user authentication pages will redirect to the OpenSSO centralized authentication form.

SPNEGO (Simple and Protected GSSAPI Negotiation Mechanism) is used to authenticate transparently through the web browser after the user has been authenticated when logging-in his session.

A typical use case is the following:

GateIn uses JBoss Negotiation to enable SPNEGO based desktop SSO for the Portal. Here are the steps to integrate SPNEGO with GateIn.

  1. Activate the Host authentication Under conf/login-config.xml, add the following host login module:

    <!-- SPNEGO domain -->
      <application-policy name="host">
       <authentication>
          <login-module code="com.sun.security.auth.module.Krb5LoginModule"
             flag="required">
             <module-option name="storeKey">true</module-option>
             <module-option name="useKeyTab">true</module-option>                                                     
             <module-option name="principal">HTTP/server.local.network@LOCAL.NETWORK</module-option>             
             <module-option name="keyTab">/home/user/krb5keytabs/jboss.keytab</module-option>
             <module-option name="doNotPrompt">true</module-option>
             <module-option name="debug">true</module-option>
          </login-module>
       </authentication>
     </application-policy>
    

    the 'keyTab' value should point to the keytab file that was generated by the kadmin kerberos tool. See the Setting up your Kerberos Development Environment guide for more details.

  2. Extend the core authentication mechanisms to support SPNEGO Under deployers/jbossweb.deployer/META-INF/war-deployers-jboss-beans.xml, add 'SPNEGO' authenticators property

    <property name="authenticators">
             <map keyClass="java.lang.String" valueClass="java.lang.String">
                <entry>
                   <key>BASIC</key>
                   <value>org.apache.catalina.authenticator.BasicAuthenticator</value>
                </entry>
                <entry>
                   <key>CLIENT-CERT</key>
                   <value>org.apache.catalina.authenticator.SSLAuthenticator</value>
                </entry>
                <entry>
                   <key>DIGEST</key>
                   <value>org.apache.catalina.authenticator.DigestAuthenticator</value>
                </entry>
                <entry>
                   <key>FORM</key>
                   <value>org.apache.catalina.authenticator.FormAuthenticator</value>
                </entry>
                <entry>
                   <key>NONE</key>
                   <value>org.apache.catalina.authenticator.NonLoginAuthenticator</value>
                </entry>
    
            <!-- Add this entry -->
            <entry>
              <key>SPNEGO</key>
              <value>org.jboss.security.negotiation.NegotiationAuthenticator</value>
            </entry>
             </map>         
          </property>

  3. Add the JBoss Negotiation binary copy $GATEIN_SSO_HOME/spnego/jboss-negotiation-2.0.3.GA.jar to lib

  4. Add the Gatein SSO module binaries Add $GATEIN_SSO_HOME/spnego/gatein.ear/lib/sso-agent.jar, and $GATEIN_SSO_HOME/spnego/gatein.ear/lib/sso-spnego.jar to deploy/gatein.ear/lib

  5. Activate SPNEGO LoginModule for GateIn Modify deploy/gatein.ear/META-INF/gatein-jboss-beans.xml, so that it looks like this:

    <deployment xmlns="urn:jboss:bean-deployer:2.0">
      <application-policy xmlns="urn:jboss:security-beans:1.0" name="gatein-domain">
        <!-- Uncomment this for Kerberos based SSO integration -->
        <authentication>
          <login-module
             code="org.gatein.sso.spnego.SPNEGOLoginModule"
             flag="requisite">
             <module-option name="password-stacking">useFirstPass</module-option>
             <module-option name="serverSecurityDomain">host</module-option>
          </login-module>      
          <login-module
             code="org.gatein.sso.agent.login.SPNEGORolesModule"
             flag="required">
          <module-option name="password-stacking">useFirstPass</module-option>
          <module-option name="portalContainerName">portal</module-option>
          <module-option name="realmName">gatein-domain</module-option>
          </login-module>      
       </authentication>
      </application-policy>
    </deployment>

  6. Integrate SPNEGO support into the Portal web archive Switch GateIn authentication mechanism from the default "FORM" based to "SPNEGO" based authentication as follows: Modify gatein.ear/02portal.war/WEB-INF/web.xml

        <!--
        <login-config>
          <auth-method>FORM</auth-method> 
          <realm-name>gatein-domain</realm-name> 
            <form-login-config>
              <form-login-page>/initiatelogin</form-login-page> 
                <form-error-page>/errorlogin</form-error-page>
          </form-login-config>
        </login-config>
        -->
        <login-config>
          <auth-method>SPNEGO</auth-method>
          <realm-name>SPNEGO</realm-name>    
        </login-config>

    Integrate request pre-processing needed for SPNEGO via filters. Add the following filters to the web.xml at the top of the Filter chain:

       <filter>
          <filter-name>LoginRedirectFilter</filter-name>
          <filter-class>org.gatein.sso.agent.filter.LoginRedirectFilter</filter-class>
          <init-param>                                 
            <!-- This should point to your SSO authentication server -->                                                                                              
            <param-name>LOGIN_URL</param-name>                                                                                                
            <param-value>/portal/private/classic</param-value>                                                                                                         
          </init-param>
        </filter>
        <filter>
            <filter-name>SPNEGOFilter</filter-name>
            <filter-class>org.gatein.sso.agent.filter.SPNEGOFilter</filter-class>
        </filter>
        
        <filter-mapping>
          <filter-name>LoginRedirectFilter</filter-name>
          <url-pattern>/*</url-pattern>             
        </filter-mapping>
        <filter-mapping>                                           
            <filter-name>SPNEGOFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>

  7. Modify the Portal's 'Sign In' link to perform SPNEGO authentication Modify the 'Sign In' link on gatein.war/web.war/groovy/groovy/webui/component/UIBannerPortlet.gtmpl as follows:

    <!--
    <a onclick="$signInAction"><%=_ctx.appRes("UILoginForm.label.Signin")%></a>
    -->
    <a href="/portal/sso"><%=_ctx.appRes("UILoginForm.label.Signin")%></a>

  8. Start the GateIn Portal

    sudo ./run.sh -Djava.security.krb5.realm=LOCAL.NETWORK -Djava.security.krb5.kdc=server.local.network -c spnego -b server.local.network

  9. Login to Kerberos

    kinit -A demo

    You should be able to click the 'Sign In' link on the GateIn Portal and the 'demo' user from the GateIn portal should be automatically logged in

The WSRP Technical Committee defined WSRP Use Profiles to help with WSRP interoperability. We will refer to terms defined in that document in this section.

GateIn provides a Simple level of support for our WSRP Producer except that out-of-band registration is not currently handled. We support in-band registration and persistent local state (which are defined at the Complex level).

On the Consumer side, GateIn provides a Medium level of support for WSRP, except that we only handle HTML markup (as GateIn itself doesn't handle other markup types). We do support explicit portlet cloning and we fully support the PortletManagement interface.

As far as caching goes, we have Level 1 Producer and Consumer. We support Cookie handling properly on the Consumer and our Producer requires initialization of cookies (as we have found that it improved interoperabilty with some consumers). We don't support custom window states or modes, as GateIn doesn't either. We do, however, support CSS on both the Producer (though it's more a function of the portlets than inherent Producer capability) and Consumer.

While we provide a complete implementation of WSRP 1.0, we do need to go through the Conformance statements and perform more interoperability testing (an area that needs to be better supported by the WSRP Technical Committee and Community at large).

New for 3.2 of GateIn is support of WSRP 2.0 with a complete implementation of the non-optional features. The only thing that we have not implemented is support for lifetimes and leasing support.

Note

As of version 3.2 of GateIn, WSRP is only activated and supported when GateIn is deployed on JBoss Application Server.

GateIn provides a complete support of WSRP 1.0 standard interfaces and offers both consumer and producer services. WSRP support is provided by the following files, assuming $GATEIN_HOME is where GateIn has been installed, $WSRP_VERSION (at the time of the writing, it was 2.0.0-GA) is the version of the WSRP component and $PORTAL_VERSION (at the time of the writing, it was 3.2.0-GA) is the current GateIn version:

If you're not going to use WSRP in GateIn, it won't adversely affect your installation to leave it as-is. Please see the WSRP service removal procedure for a complete guide on how to remove the WSRP service.

JBoss WS (the web service stack that GateIn uses) should take care of the details of updating the port and host name used in WSDL. See the JBoss WS user guide on that subject for more details.

Of course, if you have modified you have modified the host name and port on which your server runs, you will need to update the configuration for the consumer used to consume GateIn's 'self' producer. Please refer to the Section 7.6, “Consuming remote WSRP portlets in GateIn” to learn how to do so.

GateIn does NOT, by default, expose local portlets for consumption by remote WSRP consumers. In order to make a portlet remotely available, it must be made "remotable" by marking it as such in the associated portlet.xml. This is accomplished by using a specific org.gatein.pc.remotable container-runtime-option. Setting its value to true makes the portlet available for remote consumption, while setting its value to false will not publish it remotely. As specifying the remotable status for a portlet is optional, you do not need to do anything if you don't need your portlet to be available remotely.

In the following example, the "BasicPortlet" portlet is specified as being remotable.


It is also possible to specify that all the portlets declared within a given portlet application to be remotable by default. This is done by specifying the container-runtime-option at the portlet-app element level. Individual portlets can override that value to not be remotely exposed. Let's look at an example:


In the example above, we defined two portlets. The org.gatein.pc.remotable container-runtime-option being set to true at the portlet-app level, all portlets defined in this particular portlet application are exposed remotely by GateIn's WSRP producer. Note, however, that it is possible to override the default behavior: specifying a value for the org.gatein.pc.remotable container-runtime-option at the portlet level will take precedence over the default. In the example above, the RemotelyExposedPortlet inherits the remotable status defined at the portlet-app level since it does not specify a value for theorg.gatein.pc.remotable container-runtime-option. TheNotRemotelyExposedPortlet, however, overrides the default behavior and is not remotely exposed. Note that in the absence of a top-level org.gatein.pc.remotable container-runtime-option value set totrue, portlets are NOT remotely exposed.

WSRP Consumers vary a lot as far as how they are configured. Most of them require that you specify the URL for the Producer's WSDL definition. Please refer to your Consumer's documentation for specific instructions. For instructions on how to do so in GateIn, please refer to Section 7.6, “Consuming remote WSRP portlets in GateIn”.

GateIn's Producer is automatically set up when you deploy a portal instance with the WSRP service. You can access the WSDL file at http://{hostname}:{port}/wsrp-producer/v2/MarkupService?wsdl. If you wish to use only the WSRP 1 compliant version of the producer, please use the WSDL file found at http://{hostname}:{port}/wsrp-producer/v1/MarkupService?wsdl. The default hostname is localhost and the default port is 8080.

Let's work through the steps of defining access to a remote producer so that its portlets can be consumed within GateIn. We will configure access to Oracle's public WSRP producer. We will first examine how to do so using the configuration portlet. We will then show how the same result can be accomplished with a producer descriptor, though it is far easier to do so via the configuration portlet.

GateIn provides a portlet to configure access (among other functions) to remote WSRP Producers grahically. Starting with 3.2, the WSRP configuration portlet is installed by default. You can find it at http://localhost:8080/portal/login?initialURI=%2Fportal%2Fprivate%2Fclassic%2FwsrpConfigurationp&username=root&password=gtn

You should see a screen similar to:

This screen presents all the configured Consumers associated with their status and possible actions on them. A Consumer can be active or inactive. Activating a Consumer means that it is ready to act as a portlet provider. Note also that a Consumer can be marked as requiring refresh meaning that the information held about it might not be up to date and refreshing it from the remote Producer might be a good idea. This can happen for several reasons: the service description for that remote Producer has not been fetched yet, the cached version has expired or modifications have been made to the configuration that could potentially invalidate it, thus requiring re-validation of the information.

Note

The WSRP configuration didn't use to be installed by default in previous versions of GateIn. We include here the legacy instructions on how to install this portlet in case you ever need to re-install it.

Use the usual procedure to log in as a Portal administrator and go to the Application Registry. With the default install, you can just go to http://localhost:8080/portal/login?initialURI=%2Fportal%2Fprivate%2Fclassic%2Fadministration%2Fregistry&username=root&password=gtn Add the WSRP Configuration portlet to the Administration category. If you use the Import Applications functionality, the WSRP Configuration portlet will be automatically added to the Administration category.

Now that the portlet is added to a category, it can be added to a page and used. We recommend adding it to the same page as the Application Registry as operations relating to WSRP and adding portlets to categories are somewhat related as we will see. Go ahead and add the WSRP Configuration portlet to the page using the standard procedure.

Next, we create a new Consumer which we will call oracle. Type "oracle" in the "Create a consumer named:" field then click on "Create consumer":

You should now see a form allowing you to enter/modify the information about the Consumer. Set the cache expiration value to 300 seconds, leave the default timeout value for web services (WS) operations and enter the WSDL URL for the producer in the text field and press the "Refresh & Save" button:

This will retrieve the service description associated with the Producer which WSRP interface is described by the WSDL file found at the URL you just entered. In our case, querying the service description will allow us to learn that the Producer requires registration but didn't request any registration property:

The Consumer for the oracle Producer should now be available as a portlet provider and be ready to be used.

Now, assuming that the producer required a value for an email registration property, GateIn's WSRP consumer would have informed you that you were missing some information:

Note

At this point, there is no automated way to learn about which possible values (if any) are expected by the remote Producer. Sometimes, the possible values will be indicated in the registration property description but this is not always the case... Please refer to the specific Producer's documentation.

If you entered "example@example.com" as the value for the registration property and press "Save & Refresh" once more, you would have seen something similar to:

While we recommend you use the WSRP Configuration portlet to configure Consumers, we provide an alternative way to configure consumers by editing the XML file located at $GATEIN_HOME/lib/wsrp-consumer-$WSRP_VERSION.jar/conf/wsrp-consumers-config.xml.



<?xml version='1.0' encoding='UTF-8' ?>
<deployments xmlns="http://www.gatein.org/xml/ns/gatein_wsrp_consumer_1_0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_wsrp_consumer_1_0 http://www.jboss.org/portal/xsd/gatein_wsrp_consumer_1_0.xsd">
   <deployment>
      <wsrp-producer id="self" expiration-cache="300" ws-timeout="30000">
         <endpoint-wsdl-url>http://localhost:8080/wsrp-producer/v2/MarkupService?wsdl</endpoint-wsdl-url>
         <registration-data>
            <property>
               <name>email</name>
               <lang>en</lang>
               <value>example@example.com</value>
            </property>
         </registration-data>
      </wsrp-producer>
   </deployment>
   <deployment>
      <wsrp-producer id="oracle" expiration-cache="300">
         <endpoint-wsdl-url>http://portalstandards.oracle.com/portletapp/portlets?WSDL</endpoint-wsdl-url>
         <registration-data/>
      </wsrp-producer>
   </deployment>
</deployments>

The file as shown above specifies access to two producers: self, which consumes GateIn's own WSRP producer albeit in a version that assumes that the producer requires a value for an email registration property, and oracle, which consumes Oracle's public producer, both in configurations as shown in the walk-through above.

We will look at the details of the meaning of elements later on.

While we recommend you use the WSRP Configuration portlet to configure Consumers, we provide an alternative way to configure consumers by editing the XML file located at $GATEIN_HOME/lib/wsrp-consumer-$WSRP_VERSION.jar/conf/wsrp-consumers-config.xml.

It is also possible to provide addtional configuration, which, in some cases, might be important to establish a proper connection to the remote producer.

One such optional configuration concerns caching. To prevent useless roundtrips between the local consumer and the remote producer, it is possible to cache some of the information sent by the producer (such as the list of offered portlets) for a given duration. The rate at which the information is refreshed is defined by the expiration-cache attribute of the <wsrp-producer> element which specifies the refreshing period in seconds. For example, providing a value of 120 for expiration-cache means that the producer information will not be refreshed for 2 minutes after it has been somehow accessed. If no value is provided, GateIn will always access the remote producer regardless of whether the remote information has changed or not. Since, in most instances, the information provided by the producer does not change often, we recommend that you use this caching facility to minimize bandwidth usage.

It is also possible to define a timeout after which WS operations are considered as failed. This is helpful to avoid blocking the WSRP service, waiting forever on the service that doesn't answer. Use the ws-timeout attribute of the <wsrp-producer> element to specify how many milliseconds the WSRP service will wait for a response from the remote producer before timing out and giving up.

Additionally, some producers require consumers to register with them before authorizing them to access their offered portlets. If you know that information beforehand, you can provide the required registration information in the producer configuration so that the consumer can register with the remote producer when required.

Registration configuration is done via the <registration-data> element. Since GateIn can generate the mandatory information for you, if the remote producer does not require any registration properties, you only need to provide an empty <registration-data> element. Values for the registration properties required by the remote producer can be provided via <property> elements. See the example below for more details. Additionally, you can override the default consumer name automatically provided by GateIn via the <consumer-name> element. If you choose to provide a consumer name, please remember that this should uniquely identify your consumer.

Here is the configuration of the selfv1 and selfv2 consumers as found in $GATEIN_HOME/lib/wsrp-consumer-$WSRP_VERSION.jar/conf/wsrp-consumers-config.xml with a cache expiring every 500 seconds and with a 50 second timeout for web service operations.


Here is an example of a WSRP descriptor with registration data and cache expiring every minute:


Producers often offer several levels of service depending on consumers' subscription levels (for example). This is implemented at the WSRP level with the registration concept: producers can assert which level of service to provide to consumers based on the values of given registration properties.

There might also be cases where you just want to update the registration information because it has changed. For example, the producer required you to provide a valid email and the previously email address is not valid anymore and needs to be updated.

It is therefore sometimes necessary to modify the registration that concretizes the service agreement between a consumer and a producer. Let's take the example of the producer requiring an email we configured in Section 7.6.2.1, “Using the configuration portlet”. If you recall, the producer was requiring registration and required a value to be provided for the email property.

Suppose now that we would like to update the email address that we provided to the remote producer. We will need to tell the producer that our registration data has been modified. Let's see how to do this. Assuming you have configured access to the producer as previously described, please go to the configuration screen for the self producer and modify the value of email to foo@example.com instead ofexample@example.com:

Now click on "Update properties" to save the change. A "Modify registration" button should now appear to let you send this new data to the remote producer:

Click on this new button and, if everything went well and your updated registration has been accepted by the remote producer, you should see something similar to:

It can also happen that a producer administrator decided to change its requirement forregistered consumers. In this case, invoking operations on the producer will fail with an OperationFailedFault. GateIn will attempt to help you in this situation. Let's walk through an example using the self producer. Let's assume that registration is requiring a valid value for an email registration property (as we have seen so far). If you go to the configuration screen for this producer, you should see:

Now suppose that the administrator of the producer now additionaly requires a value to be provided for a name registration property. We will actually see how to do perform this operation in GateIn when we examine how to configure GateIn's producer in Section 7.8, “Configuring GateIn's WSRP Producer”. Operations with this producer will now fail. If you suspect that a registration modification is required, you should go to the configuration screen for this remote producer and refresh the information held by the consumer by pressing "Refresh & Save":

As you can see, the configuration screen now shows the currently held registration information and the expected information from the producer. Enter a value for the name property and then click on "Modify registration". If all went well and the producer accepted your new registration data, you should see something similar to:

Note

WSRP 1 makes it rather difficult to ascertain for sure what caused an OperationFailedFault as it is the generic exception returned by producers if something didn't quite happen as expected during a method invocation. This means that OperationFailedFault can be caused by several different reasons, one of them being a request to modify the registration data. Please take a look at the log files to see if you can gather more information as to what happened. WSRP 2 introduces an exception that is specific to a request to modify registrations thus reducing the ambiguity that currently exists.

Import and export are new functionalities added in WSRP 2. Exporting a portlet allows a consumer to get an opaque representation of the portlet which can then be use by the corresponding import operation to reconstitute it. It is mostly used in migration scenarios during batch operations. Since GateIn does not currently support automated migration of portal data, the functionality that we provide as part of WSRP 2 is necessarily less complete than it could be with full portal support.

The import/export implementation in GateIn 3.2 allows users to export portlets from a given consumer. These portlets can then be used to replace existing content on pages. This is accomplished by assiging previously exported portlets to replace the content displayed by windows on the portal's pages. Let us walk through an example to make things clearer.

Clicking on the "Export" action for a given consumer will display the list of portlets currently made available by this specific consumer. An example of such a list is shown below:

Once portlets have been selected, they can be exported by clicking on the "Export" button thus making them available for later import:

You can re-import the portlets directly by pressing the "Use for import" button or, on the Consumers list page, using the "Import" action for a given consumer. Let's assume that you used that second option and that you currently have several available sets of previously exported portlets to import from. After clicking the action link, you should see a screen similar to the one below:

As you can see this screen presents the list of available exports with available operations for each.

Once you've selected an export to import from, you will see a screen similar to the one below:

The screen displays the list of available exported portlets for the previously selected export. You can select which portlet you want to import by checking the checkbox next to its name. Next, you need to select the content of which window the imported portlet will replace. This process is done in three steps. Let's assume in this example that you have the following page called page1 and containing two windows called NetUnity WSRP 2 Interop - Cache Markup (remote) and /samples-remotecontroller-portlet.RemoteControl (remote) as shown below:

In this example, we want to replace the content of the /samples-remotecontroller-portlet.RemoteControl (remote) by the content of the /ajaxPortlet.JSFAJAXPortlet portlet that we previously exported. To do so, we will check the checkbox next to the /ajaxPortlet.JSFAJAXPortlet portlet name to indicate that we want to import its data and then select the page1 in the list of available pages. The screen will then refresh to display the list of available windows on that page, similar to the one seen below:

Note that, at this point, we still need to select the window which content we want to replace before being able to complete the import operation. Let's select the /samples-remotecontroller-portlet.RemoteControl (remote) window, at which point the "Import" button will become enabled, indicating that we now have all the necessary data to perform the import. If all goes well, pressing that button should result in a screen similar to the one below:

If you now take a look at the page1 page, you should now see that the content /samples-remotecontroller-portlet.RemoteControl (remote) window has been replaced by the content of the /ajaxPortlet.JSFAJAXPortlet imported portlet and the window renamed appropriately:

The default producer configuration is to require that consumers register with it before providing access its services but does not require any specific registration properties (apart from what is mandated by the WSRP standard). It does, however, require consumers to be registered before sending them a full service description. This means that our WSRP producer will not provide the list of offered portlets and other capabilities to unregistered consumers. The producer also uses the default RegistrationPolicy paired with the default RegistrationPropertyValidator. We will look into property validators in greater detail later inSection 7.8.3, “Registration configuration”. Suffice to say for now that this allows users to customize how Portal's WSRP Producer decides whether a given registration property is valid or not.

GateIn provides a web interface to configure the producer's behavior. You can access it by clicking on the "Producer Configuration" tab of the "WSRP" page of the "admin" portal. Here's what you should see with the default configuration:

As would be expected, you can specify whether or not the producer will send the full service description to unregistered consumers, and, if it requires registration, which RegistrationPolicy to use (and, if needed, which RegistrationPropertyValidator), along with required registration property description for which consumers must provide acceptable values to successfully register.

New in GateIn 3.2, we now display the WSDL URLs to access GateIn's WSRP producer either in WSRP 1 or WSRP 2 mode.

In order to require consumers to register with Portal's producer before interacting with it, you need to configure Portal's behavior with respect to registration. Registration is optional, as are registration properties. The producer can require registration without requiring consumers to pass any registration properties as is the case in the default configuration. Let's configure our producer starting with a blank state:

We will allow unregistered consumers to see the list of offered portlets so we leave the first checkbox ("Access to full service description requires consumers to be registered.") unchecked. We will, however, specify that consumers will need to be registered to be able to interact with our producer. Check the second checkbox ("Requires registration. Modifying this information will trigger invalidation of consumer registrations."). The screen should now refresh and display:

You can specify the fully-qualified name for your RegistrationPolicy and RegistrationPropertyValidator there. We will keep the default value. See Section 7.8.3.1, “Customization of Registration handling behavior” for more details. Let's add, however, a registration property called email. Click "Add property" and enter the appropriate information in the fields, providing a description for the registration property that can be used by consumers to figure out its purpose:

Press "Save" to record your modifications.

Note

At this time, only String (xsd:string) properties are supported. If your application requires more complex properties, please let us know.

Note

If consumers are already registered with the producer, modifying the configuration of required registration information will trigger the invalidation of held registrations, requiring consumers to modify their registration before being able to access the producer again. We saw the consumer side of that process in Section 7.7.1.2, “Registration modification on producer error”.

Registration handling behavior can be customized by users to suit their Producer needs. This is accomplished by providing an implementation of the RegistrationPolicy interface. This interface defines methods that are called by Portal's Registration service so that decisions can be made appropriately. A default registration policy that provides basic behavior is provided and should be enough for most user needs.

While the default registration policy provides default behavior for most registration-related aspects, there is still one aspect that requires configuration: whether a given value for a registration property is acceptable by the WSRP Producer. This is accomplished by plugging a RegistrationPropertyValidator in the default registration policy. This allows users to define their own validation mechanism.

Please refer to the Javadoc™ for org.gatein.registration.RegistrationPolicy and org.gatein.registration.policies.RegistrationPropertyValidator for more details on what is expected of each method.

Defining a registration policy is required for the producer to be correctly configured. This is accomplished by specifying the qualified class name of the registration policy. Since we anticipate that most users will use the default registration policy, it is possible to provide the class name of your custom property validator instead to customize the default registration policy behavior. Note that property validators are only used by the default policy.

GateIn Kernel supports non-component objects that can be configured, instantiated, and injected into registered components, using method calls. The mechanism is called 'plugins', and allows portal extensions to add additional configurations to core services.

External plugin is defined by using <external-component-plugins> wrapper element which contains one or more <component-plugin> definitions. <external-component-plugins> uses <target-component> to specify a target service component that will receive injected objects.

Every <component-plugin> defines an implementation type, and a method on target component to use for injection (<set-method>).

A plugin implementation class has to implement org.exoplatform.container.component. ComponentPlugin interface.

In the following example PortalContainerDefinitionPlugin implements ComponentPlugin:


Configuration files may contain a special variable reference ${container.name.suffix}. This variable resolves to the name of the current portal container, prefixed by underscore (_). This facilitates reuse of configuration files in situations where portal specific unique names need to be assigned to some resources (i.e. JNDI names, Database / DataSource names, JCR repository names, etc ...).

This variable is only defined when there is a current PortalContainer available - only for PortalContainer scoped services.

A good example for this is HibernateService:

Example 8.3. HibernateService using variables



<?xml version="1.0" encoding="ISO-8859-1"?>
<configuration
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_0.xsd
                       http://www.exoplaform.org/xml/ns/kernel_1_0.xsd"
   xmlns="http://www.exoplaform.org/xml/ns/kernel_1_0.xsd">

   <component>
      <key>org.exoplatform.services.database.HibernateService</key>
      <jmx-name>database:type=HibernateService</jmx-name>
      <type>org.exoplatform.services.database.impl.HibernateServiceImpl</type>
      <init-params>
         <properties-param>
            <name>hibernate.properties</name>
            <description>Default Hibernate Service</description>
            <property name="hibernate.show_sql" value="false" />
            <property name="hibernate.cglib.use_reflection_optimizer" value="true" />
            <property name="hibernate.connection.url"
                            value="jdbc:hsqldb:file:../temp/data/exodb${container.name.suffix}" />
            <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" />
            <property name="hibernate.connection.autocommit" value="true" />
            <property name="hibernate.connection.username" value="sa" />
            <property name="hibernate.connection.password" value="" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
            <property name="hibernate.c3p0.min_size" value="5" />
            <property name="hibernate.c3p0.max_size" value="20" />
            <property name="hibernate.c3p0.timeout" value="1800" />
            <property name="hibernate.c3p0.max_statements" value="50" />
         </properties-param>
      </init-params>
   </component>
</configuration>
         

InitParams is a configuration object that is essentially a map of key-value pairs, where key is always a String, and value can be any type that can be described using kernel configuration xml.

Service components that form the GateIn 3.2 insfrastructure use InitParams object to configure themselves. A component can have one instance of InitParams injected at most. If the service component's constructor takes InitParams as any of the parameters it will automatically be injected at component instantiation time. The xml configuration for a service component that expects InitParams object must include <init-params> element (even if an empty one).

Let's use an example to see how the kernel xml configuration syntax looks for creating InitParams instances.


InitParams object description begins with <init-params> element. It can have zero or more children elements each of which is one of <value-param>, <values-param>, <properties-param>, or <object-param>. Each of these child elements takes a <name> that serves as a map entry key, and an optional <description>. It also takes a type-specific value specification.

For <properties-param> the value specification is in the form of one or more <property> elements, each of which specifies two strings - a property name, and a property value. Each <properties-params> defines one java.util.Properties instance. Also see Example 8.3, “HibernateService using variables” for an example.


For <value-param> the value specification is in the form of <value> element, which defines one String instance.


For <values-param> the value specification is in the form of one or more <value> elements, each of which represents one String instance, where all the String values are then collected into a java.util.List instance.


For <object-param> in our case, the value specification comes in a form of <object> element, which is used for POJO style object specification (you specify an implementation class - <type>, and property values - <field>).

Also see Example 8.8, “Portal container declaration example” for an example of specifying a field of Collection type.

The InitParams structure - the names and types of entries is specific for each service, as it is the code inside service components's class that decides what entry names to look up and what types it expects to find.

A portal container is defined by several attributes.

First, there is a portal container name, which is always equal to URL context to which the current portal is bound.

Second, there is a REST context name, which is used for REST access to portal application - every portal has exactly one (unique) REST context name.

Then, there is a realm name which is the name of security realm used for authentication when users log into the portal.

Finally, there is a list of Dependencies - other web applications, whose resources are visible to current portal (via extension mechanism described later), and are searched in the specified order.

Example 8.8. Portal container declaration example



<?xml version="1.0" encoding="UTF-8"?>
<configuration
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_0.xsd
                       http://www.exoplaform.org/xml/ns/kernel_1_0.xsd"
   xmlns="http://www.exoplaform.org/xml/ns/kernel_1_0.xsd">

   <external-component-plugins>
      <!-- The full qualified name of the PortalContainerConfig -->
      <target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>

      <component-plugin>
         <!-- The name of the plugin -->
         <name>Add PortalContainer Definitions</name>

         <!-- The name of the method to call on the PortalContainerConfig
              in order to register the PortalContainerDefinitions -->
         <set-method>registerPlugin</set-method>

         <!-- The full qualified name of the PortalContainerDefinitionPlugin -->
         <type>org.exoplatform.container.definition.PortalContainerDefinitionPlugin</type>

         <init-params>
            <object-param>
               <name>portal</name>
               <object type="org.exoplatform.container.definition.PortalContainerDefinition">
                  <!-- The name of the portal container -->
                  <field name="name"><string>portal</string></field>

                  <!-- The name of the context name of the rest web application -->
                  <field name="restContextName"><string>rest</string></field>

                  <!-- The name of the realm -->
                  <field name="realmName"><string>exo-domain</string></field>

                  <!-- All the dependencies of the portal container ordered by loading priority -->
                  <field name="dependencies">
                     <collection type="java.util.ArrayList">
                        <value>
                           <string>eXoResources</string>
                        </value>
                        <value>
                           <string>portal</string>
                        </value>
                        <value>
                           <string>dashboard</string>
                        </value>
                        <value>
                           <string>exoadmin</string>
                        </value>
                        <value>
                           <string>eXoGadgets</string>
                        </value>
                        <value>
                           <string>eXoGadgetServer</string>
                        </value>
                        <value>
                           <string>rest</string>
                        </value>
                        <value>
                           <string>web</string>
                        </value>
                        <value>
                           <string>wsrp-producer</string>
                        </value>
                        <!-- The sample-ext has been added at the end of the dependency list
                             in order to have the highest priority -->
                        <value>
                           <string>sample-ext</string>
                        </value>
                     </collection>
                  </field>
               </object>
            </object-param>
         </init-params>
      </component-plugin>
   </external-component-plugins>
</configuration>
      

Note

Dependencies are part of the extension mechanism.

Every portal container is represented by a PortalContainer instance, which contains:

  • associated ExoContainerContext, which contains information about the portal

  • unified servlet context, for web-archive-relative resource loading

  • unified classloader, for classpath based resource loading

  • methods for retrieving services

Unified servlet context, and unified classloader are part of the extension mechanism (explained in next section), and provide standard API (ServletContext, ClassLoader) with specific resource loading behavior - visibility into associated web application archives, configured with Dependencies property of PortalContainerDefinition. Resources from other web applications are queried in the order specified by Dependencies. The later entries in the list override the previous ones.

Extension mechanism is a functionality that makes it possible to override portal resources in an almost plug-and-play fashion - just drop in a .war archive with the resources, and configure its position on the portal's classpath. This way any customizations of the portal don't have to involve unpacking and repacking the original portal .war archives. Instead, you create your own .war archive with changed resources, that override the resources in the original archive.

A web archive packaged in a way to be used through extension mechanism is called portal extension.

There are two steps necessary to create a portal extension.

First, declare PortalConfigOwner servlet context listener in web.xml of your web application.


Then, add the servlet context name of this web application in proper place in the list of Dependencies of the PortalContainerDefinition of all the portal containers that you want to have access to its resources.

After this step your web archive will be on portal's unified classpath, and unified servlet context resource path. The later in the Dependencies list your application is, the higher priority it has when resources are loaded by portal.

Note

See 'Configuring a portal' section for example of PortalContainerDefinition, that has sample-ext at the end of its list of Dependencies.

It is possible to run several independent portal containers - each bound to a different URL context - within the same JVM instance. This kind of setup is very efficient from administration and resource consumption aspect. The most elegant way to reuse configuration for different coexisting portals is by way of extension mechanism - by inheriting resources and configuration from existing web archives, and just adding extra resources to it, and overriding those that need to be changed by including modified copies.

In order for a portal application to correctly function when deployed in multiple portals, the application may have to dynamically query the information about the current portal container. The application should not make any assumptions about the name, and other information of the current portal, as there are now multiple different portals in play.

At any point during request processing, or lifecycle event processing, your application can retrieve this information through org.exoplatform.container. ExoContainerContext. Sometimes your application needs to make sure that the proper PortalContainer - the source of ExoContainerContext - is associated with the current call.

If you ship servlets or servlet filters as part of your portal application, and if you need to access portal specific resources at any time during the processing of the servlet or filter request, then you need to make sure the servlet/filter is associated with the current container.

The proper way to do that is to make your servlet extend org.exoplatform.container.web. AbstractHttpServlet class. This will not only properly initialize current PortalContainer for you, but will also set the current thread's context classloader to one that looks for resources in associated web applications in the order specified by Dependencies configuration (as explained in Extension mechanism section).

Similarly for filters, make sure your filter class extends org.exoplatform.container.web. AbstractFilter. Both AbstractHttpServlet, and AbstractFilter have a method getContainer(), which returns the current PortalContainer. If your servlet handles the requests by implementing a service() method, you need to rename that method to match the following signature:



/**
 * Use this method instead of Servlet.service()
 */
protected void onService(ExoContainer container, HttpServletRequest req,
      HttpServletResponse res) throws ServletException, IOException;
         

You may also need to access portal information within your HttpSessionListener. Again, make sure to extend the provided abstract class - org.exoplatform.container.web. AbstractHttpSessionListener. Also, modify your method signatures as follows:



/**
 * Use this method instead of HttpSessionListener.sessionCreated()
 */
protected void onSessionCreated(ExoContainer container, HttpSessionEvent event);
/**
 * Use this method instead of HttpSessionListener.sessionDestroyed()
 */
protected void onSessionDestroyed(ExoContainer container, HttpSessionEvent event);
         

There is another method you have to implement in this case:



/**
 * Method should return true if unified servlet context,
 * and unified classloader should be made available
 */
protected boolean requirePortalEnvironment();
         

If this method returns true, current thread's context classloader is set up according to Dependencies configuration, and availability of the associated web applications. If it returns false, the standard application separation rules are used for resource loading (effectively turning off the extension mechanism). This method exists on AbstractHttpServlet and AbstractFilter as well, where there is a default implementation that automatically returns true, when it detects there is a current PortalContainer present, otherwise it returns false.

We still have to explain how to properly perform ServletContextListener based initialization, when you need access to current PortalContainer.

GateIn has no direct control over the deployment of application archives (.war, .ear files) - it is the application server that performs the deployment. For extension mechanism to work properly, the applications, associated with the portal via Dependencies configuration, have to be deployed before the portal, that depends on them, is initialized. On the other hand, these applications may require an already initialized PortalContainer to properly initialize themselves - we have a recursive dependency problem. To resolve this problem, a mechanism of initialization tasks, and task queues, was put in place. Web applications that depend on current PortalContainer for their initialization have to avoid performing their initialization directly in some ServletContextListener executed during their deployment (before any PortalContainer was initialized). Instead, a web application should package its initialization logic into an init task of appropriate type, and only use ServletContextListener to insert the init task instance into the proper init tasks queue.

An example of this is Gadgets application which registers Google gadgets with the current PortalContainer:



public class GadgetRegister implements ServletContextListener
{
   public void contextInitialized(ServletContextEvent event)
   {
      // Create a new post-init task
      final PortalContainerPostInitTask task = new PortalContainerPostInitTask() {
         public void execute(ServletContext context, PortalContainer portalContainer)
         {
            try
            {
               SourceStorage sourceStorage =
               (SourceStorage) portalContainer.getComponentInstanceOfType(SourceStorage.class);
               ...
            }
            catch (RuntimeException e)
            {
               throw e;
            }
            catch (Exception e)
            {
               throw new RuntimeException("Initialization failed: ", e);
            }
         }
      };
      // Add post-init task for execution on all the portal containers
      // that depend on the given ServletContext according to 
      // PortalContainerDefinitions (via Dependencies configuration)
      PortalContainer.addInitTask(event.getServletContext(), task);
   }
}
      

The above example uses PortalContainerPostInitTask, which gets executed after the portal container has been initialized. In some situations you may want to execute initialization after portal container was instantiated, but before it was initialized - use PortalContainerPreInitTask in that case. Or, you may want to execute initialization after all the post-init tasks have been executed - use PortalContainerPostCreateTask in that case.

One more area that may need your attention are LoginModules. If you use custom LoginModules, that require current ExoContainer, make sure they extend org.exoplatform.services.security.jaas.AbstractLoginModule for proper initialization. AbstractLoginModule also takes care of the basic configuration - it recognizes two initialization options - portalContainerName, and realmName whose values you can access via protected fields of the same name.