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 database for JCR
2.3. Default Portal Configuration
2.3.1. Overview
2.3.2. Configuration
2.4. PicketLink IDM integration
2.4.1. Configuration files
2.5. Portal Navigation Configuration
2.5.1. Overview
2.5.2. Portal Navigation
2.5.3. Group Navigation
2.5.4. User Navigation
2.5.5. Tips
2.6. Authentication Token Configuration
2.6.1. What is token service
2.6.2. Implement token service's API
2.6.3. Configure token services
2.7. Predefined User Configuration
2.7.1. Overview
2.7.2. Plugin for adding users, groups and membership types
2.7.3. Membership types
2.7.4. Groups
2.7.5. Users
2.7.6. Plugin for monitoring user creation
2.8. Portal Default Permission Configuration
2.8.1. Overview
2.8.2. Overwrite Portal Default Permissions
2.9. Data Injector Configuration
2.9.1. Data Injector
2.9.2. OrganizationInitializer
2.9.3. Group Parameters
2.9.4. User Parameters
2.9.5. Automatic Navigation Creation
2.10. Skin Configuration
2.10.1. Overview
2.10.2. Skin Switching
2.10.3. Types of skins
2.10.4. Skins in Page Markups
2.10.5. Set the default skin
2.10.6. How to create a new skin
2.10.7. How to create a new window style
2.10.8. How to create new Portlet skins
2.10.9. Tips and Tricks
2.11. Javascript Configuration
2.12. Dashboard configuration
3. SSO - Single Sign On
3.1. Overview
3.1.1. Prerequisites
3.2. CAS - Central Authentication Service
3.2.1. CAS server
3.2.2. Setup the CAS client
3.2.3. Redirect to CAS
3.3. JOSSO
3.3.1. JOSSO server
3.3.2. Setup the JOSSO client
3.3.3. Setup the portal to redirect to JOSSO
3.4. OpenSSO - The Open Web SSO project
3.4.1. OpenSSO server
3.4.2. Setup the OpenSSO client
3.4.3. Setup the portal to redirect to OpenSSO
4. Foundations
4.1. GateIn Kernel
4.2. Configuring services
4.3. Configuration syntax
4.3.1. Components
4.3.2. External Plugins
4.3.3. Includes, and special URLs
4.3.4. Special variables
4.4. Configuring a portal
4.5. GateIn Extension Mechanism, and Portal Extensions
4.6. Running Multiple Portals
5. Development
5.1. Portal Lifecycle
5.1.1. Overview
5.1.2. Application Server start and stop
5.1.3. The Command Servlet
5.2. RTL (Right To Left) Framework
5.2.1. Groovy templates
5.2.2. Stylesheet
5.2.3. Images
5.2.4. Client side JavaScript
5.3. Internationalization Configuration
5.3.1. Overview
5.3.2. Locales configuration
5.3.3. ResourceBundleService
5.3.4. Navigation Resource Bundles
5.3.5. Portlets
5.4. XML Resources Bundles
5.4.1. Motivation
5.4.2. XML format
5.4.3. Portal support
5.5. Dynamic Layouts
5.5.1. Overview
5.5.2. Advanced Drag and Drop mechanism
5.6. JavaScript Inter Application Communication
5.6.1. Overview
5.6.2. Library
5.6.3. Syntax
5.6.4. Example of Javascript events usage
5.7. Upload Component
5.7.1. Upload Service
5.8. Deactivation of the Ajax Loading Mask Layer
5.8.1. Purpose
5.8.2. Synchronous issue
5.9. Accessing User Profile
6. Portlet development
6.1. AJAX Framework
6.1.1. Portlet Preparation
6.1.2. AJAX in Groovy
6.1.3. How JavaScript works
6.1.4. PortletResponse
6.1.5. PortalResponse
6.1.6. AjaxRequest
6.1.7. HttpResponseHandler
6.1.8. Manage Several Popups
6.2. Groovy Templates
6.2.1. Basic structure
6.2.2. Groovy language
6.2.3. Linking a portlet with a template
6.3. Portlet Lifecycle
6.3.1. Portlet init
6.3.2. Portlet request handler
6.3.3. ProcessAction phase
6.3.4. Render phase
6.4. Portlet Primer
6.4.1. JSR-168 and JSR-286 overview
6.4.2. Tutorials
7. Gadget development
7.1. Gadgets
7.1.1. Existing Gadgets
7.1.2. Create a new Gadget
7.1.3. Remote Gadget
7.1.4. Gadget Importing
7.1.5. Gadget Web Editing
7.1.6. Gadget IDE Editing
7.1.7. Dashboard Viewing
7.2. Setup a Gadget Server
7.2.1. Virtual servers for gadget rendering
7.2.2. Configuration
8. Web Services for Remote Portlets (WSRP)
8.1. Introduction
8.2. Level of support in GateIn
8.3. Deploying GateIn 3.0's WSRP services
8.3.1. Considerations to use WSRP when running Portal on a non-default port or hostname
8.3.2. Considerations to use WSRP with SSL
8.4. Making a portlet remotable
8.5. Consuming GateIn 3.0's WSRP portlets from a remote Consumer
8.6. Consuming remote WSRP portlets in GateIn 3.0
8.6.1. Overview
8.6.2. Configuring a remote producer walk-through
8.6.3. WSRP Producer descriptors
8.6.4. Examples
8.7. Consumers maintenance
8.7.1. Modifying a currently held registration
8.7.2. Consumer operations
8.7.3. Erasing local registration data
8.8. Configuring GateIn 3.0's WSRP Producer
8.8.1. Overview
8.8.2. Default configuration
8.8.3. Registration configuration
8.8.4. WSRP validation mode

GateIn 3.0 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 j2ee deployment archive. 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 deep-dive information about the installation and configuration of the services provide 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=

In that case, the name of the database is "jdbcjcr_${name}", ${name} should be 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 kept gatein-sample-portal.ear in $JBOSS_HOME/server/default/deploy. Note that some database wont accept '-' in a database name and you will have to delete $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 during the first startup they will be automatically created.

Also add the JDBC driver into the classpath, for instance 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 him the rights to create tables.

Then we need to add the MySQL JDBC connector in the classpath and finally edit gatein.ear/02portal.war/WEB-INF/conf/jcr/jcr-configuration with:

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

GateIn 3.0 uses the PicketLink IDM component to retain necessary identity information (user, groups, memberships, etc.). While legacy interfaces are still used (org.exoplatform.services.organization) for identity management, the wrapper implementation delegates to the 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.

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

Additionally the 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:

<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 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 name of a realm 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 following options defined as fields of object-param 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' only one group with a given name can exist in the GateIn 3.0 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 the GateIn 3.0 API as a child 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 the GateIn 3.0 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 a member 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.0 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.0. This is not true for entries added via the GateIn 3.0 management UI, however.

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.0 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.0 Group interface properties fields are persisted in JBoss Identity IDM using those attributes names: label, description

  • GateIn 3.0 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 present in it 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>

Three types of navigation are available to portal users:

These navigations are configured with 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 file defines, for the three navigations, which sets of portals, groups or users will have XML files inside the apprpriate war. Those files will be used to create an initial navigation the first time the portal is launched. That information will then be stored in the JCR and is then modifiable from the portal UI.

The portal navigation incorporates the pages that can be accessed when the user is not logged in (if the applicable permissions allow public access). For example; several portal navigations are used when a company has multiple trademarks and each trade has its own website.

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

Portal.xml

This file describes the layout and portlets that will be shown on all pages. Usually the layout contains the banner, footer, menu and breadcrumbs portlets. GateIn 3.0 is extremely configurable as every area (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 of 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.0 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 that is defined in the next XML file.

If the label #{} node label is used, the internationalization mechanism is activated and the actual label to render is taken from an associated properties file for the current locale.


<?xml version="1.0" encoding="UTF-8"?>
<node-navigation>
  <owner-type>portal</owner-type>
  <owner-id>classic</owner-id>
  <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>webexplorer</uri>
     <name>webexplorer</name>
     <label>#{portal.classic.webexplorer}</label>
     <page-reference>portal::classic::webexplorer</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 XML 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 icons or the mode.


<?xml version="1.0" encoding="ISO-8859-1"?>
<page-set>  
  <page>
    <page-id>portal::classic::homepage</page-id>
    <owner-type>portal</owner-type>
    <owner-id>classic</owner-id>
    <name>homepage</name>
    <title>Home Page</title>
    <access-permissions>Everyone</access-permissions>
    <edit-permission>*:/platform/administrators</edit-permission>
    <application>
      <instance-id>portal#classic:/web/HomePagePortlet/homepageportlet</instance-id>
      <title>Home Page portlet</title>
      <show-info-bar>false</show-info-bar>
      <show-application-state>false</show-application-state>
      <show-application-mode>false</show-application-mode>
    </application>
  </page>    
    
  <page>
    <page-id>portal::classic::webexplorer</page-id>
    <owner-type>portal</owner-type>
    <owner-id>classic</owner-id>
    <name>webexplorer</name>
    <title>Web Explorer</title>
    <access-permissions>*:/platform/users</access-permissions>
    <edit-permission>*:/platform/administrators</edit-permission>    
    <application>
      <instance-id>group#platform/users:/web/BrowserPortlet/WebExplorer</instance-id>
      <title>Web Explorer</title>
      <show-info-bar>false</show-info-bar>
    </application>  
  </page>  
</page-set>
Portlet-preferences.xml

Porlet instances can be associated with portlet-preferences that override the one defined in portlet.xml file of the portlet application 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>

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

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

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

The user workspace is located at the left hand side of the page and access is restricted to some privileged users, see Section 2.7, “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>

The plugin of type org.exoplatform.services.organization.impl.NewUserEventListener specifies which groups should join all newly created users. It notably specifies the groups and memberships to be used. It also specifies a list of users that should be excepted.


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

The permission configuration for the portal is defined in the file 02portal.war:/WEB-INF/conf/portal/portal-configuration.xml . The component UserACL is described there along with other portal component configurations.

It defines 5 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>

OrganizationInitializer is the service that allows creating a large organization with many groups and users. It also creates portal navigation and page(s) for each group and each user.

Service configuration file

<configuration>
  <component>
  <key>org.exoplatform.portal.initializer.organization.OrganizationInitializer</key>
  <type>org.exoplatform.portal.initializer.organization.OrganizationInitializer</type>
    <init-params>
      <value-param>
        <name>auto.create.group.page.navigation</name>
        <description>true or false</description>
        <value>true</value>
      </value-param>
       
      <value-param>
        <name>auto.create.user.page.navigation</name>
        <description>number of pages per user</description>
        <value>10</value>
      </value-param>
      
      <object-param>
        <name>organization</name>
        <description>description</description>
        <object type="org.exoplatform.portal.initializer.organization.OrganizationConfig">
          <field name="groups">
            <collection type="java.util.ArrayList">
              <value>
                <object type="org.exoplatform.portal.initializer.organization.OrganizationConfig$GroupsConfig">
                  <field name="group">
                    <object type="org.exoplatform.services.organization.OrganizationConfig$Group">
                      <field name="name"><string>province</string></field>
                      <field name="parentId"><string>/africa/tanzania</string></field>
                      <field name="description"><string>Tanzania's province</string></field>
                      <field name="label"><string>Province</string></field>
                    </object>                
                  </field> 
                  <field name="from"><string>1</string></field>
                  <field name="to"><string>10</string></field>
                </object>
              </value>
            </collection>
          </field>
          <field name="users">
            <collection type="java.util.ArrayList"> 
              <value>
                <object type="org.exoplatform.portal.initializer.organization.OrganizationConfig$UsersConfig">
                  <field name="user">
                    <object type="org.exoplatform.services.organization.OrganizationConfig$User">
                      <field name="userName"><string>user</string></field>
                      <field name="password"><string>GateInPlatform</string></field>
                      <field name="firstName"><string>First-Name</string></field>
                      <field name="lastName"><string>Last-Name</string></field>
                      <field name="email"><string>exo@localhost</string></field>
                      <field name="groups"><string>member:/africa</string></field>
                    </object>
                  </field>
                  <field name="from"><string>0</string></field>
                  <field name="to"><string>9</string></field>
                </object>
              </value>
            </collection>
          </field>
        </object>
      </object-param>
    </init-params>
  </component>  
</configuration>

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.

The code below illustrates the inclusion of all CSS resoucres for a new theme from the example file GateInResourcesCp060508/skin/MyPortalSkin/PortletThemes/Stylesheet.css.

/*---- 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;
}

It is recommended that users have some experience with CSS before studying GateIn 3.0 CSS.

GateIn 3.0 relies heavily on CSS to create the layout and effects for the UI. Some common techniques for customizing GateIn 3.0's CSS are explained below.

Managing Javascript scripts in an application like GateIn 3.0 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.0 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.0 can register the js files with the groovy script "WEB-INF/conf/script/groovy/JavascriptScript.groovy".

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 referenced with a module name of "eXo.core.DragDrop" which acts as a namespace. Inside the associated files, global javascript functions are used following the same namespace convention:

eXo.core.DragDrop = new DragDrop() ;

It is also possible to use the eXo.require() javascript method to lazy load and evaluate some javascript code. This is quite useful from 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.

GateIn 3.0, 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 $SSO_HOME the directory where the file is extracted.

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

This Single Sign On plugin enables seamless integration between GateIn 3.0 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, it is simpler to directly modify the sources.

To change the authentication handler to use the portal authentication handler:

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/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/cas/plugin/WEB-INF/lib/sso-cas-plugin-<VERSION>.jar and GATEIN_SSO/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.0 (for testing purposes). Edit TOMCAT_HOME/conf/server.xml and replace the 8080 port to 8888.

  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.

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

Setting up this integration happens in two distinct actions. The first part is installing or configuring a JOSSO server and the second involves setting up the portal to use the JOSSO server.

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

Setting up this integration happens in two distinct actions. The first part is installing or configuring an OpenSSO server and the second involves setting up the portal to use the OpenSSO server.

This section details setting up the OpenSSO server to authenticate against the Enterprise Portal Platform login module.

In this example the JOSSO 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.0 Authentication Plugin:

The plugin makes secure authentication callbacks to a RESTful service installed on the remote GateIn 3.0 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.0 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/opensso/plugin/WEB-INF/lib/sso-opensso-plugin-<VERSION>.jar, GATEIN_SSO/opensso/plugin/WEB-INF/lib/commons-httpclient-<VERSION>.jar, and GATEIN_SSO/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/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.

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:



<?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>
      <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 fully qualified name of the PortalContainerDefinitionPlugin -->
         <type>org.exoplatform.container.definition.PortalContainerDefinitionPlugin</type>

         ...

      </component-plugin>
   </external-component-plugins>
</configuration>
         

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:



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

A portal is defined by several attributes.

First, there is a portal 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.



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

Every portal is represented by PortalContainer instance, which contains:

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.

An example of a portal extension called sample-ext:



<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE web-app PUBLIC -//Sun MicrosystemsInc.//DTD Web Application 2.3//EN
     http://java.sun.com/dtd/web-app_2_3.dtd>
<web-app>

   <display-name>sample-ext</display-name>

   <listener>
      <listener-class>org.exoplatform.container.web.PortalContainerConfigOwner</listener-class>
   </listener>

   ...

</web-app>
      

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.

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 signitures 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) pcontainer.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.

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

All GateIn 3.0 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 5.4, “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

description The default orientation of text and images is Left-To-Right. GateIn 3.0 supports Right-To-Left orientation. Modifying text orientation is explained in Section 5.2, “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.0. 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.

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

Ajax calls are easily managed in GateIn 3.0's framework. Lines can be added to the relevant template file and java class. A simple Ajax update of a component does not require any JavaScript code to be written.

GateIn 3.0 portlets can use specific ActionListeners to receive and process Ajax calls. To set a portlet up to recieve Ajax calls, follow this procedure:

  1. Create an inner static class named with the following convention: action name followed by ActionListener.

    Example : ParentClass is the class being wrtitten in.

    static public class SaveActionListener extends EventListener<ParentClass>
    
  2. Declare this listener in the configuration of the portlet:

    listeners = ParentClass.SaveActionListener.class
    
  3. Add the correct annotation ComponentConfig, EventConfig, etc.

    For example; the configuration below is for UIAccountForm:

    ...
    
    @ComponentConfig(
      lifecycle = UIFormLifecycle.class,
      template =  "system:/groovy/webui/form/UIFormTabPane.gtmpl",
      initParams = {   
        @ParamConfig(
          name = "AccountTemplateConfigOption", 
          value = "app:/WEB-INF/conf/uiconf/account/webui/component/model/AccountTemplateConfigOption.groovy"
        ),
        @ParamConfig(
          name = "help.UIAccountFormQuickHelp",
          value = "app:/WEB-INF/conf/uiconf/account/webui/component/model/UIAccountFormQuickHelp.xhtml"
        )
      },
      events = {
        @EventConfig(listeners = UIAccountForm.SaveActionListener.class ),
        @EventConfig(listeners = UIAccountForm.ResetActionListener.class, phase = Phase.DECODE),
        @EventConfig(listeners = UIAccountForm.SearchUserActionListener.class, phase = Phase.DECODE)
      }
    )
    ...
  4. Create an execute method inside this class:

    public void execute(Event<ParentClass> event) throws Exception
    

    This method is called every time the listener gets an event from the client. Hence you can process this event in it, using the even attribute. Use it to get parameters from a form in your client, to modify the status of your portlet, etc.,

    Possible ways to use the event attribute :

  5. If your action has to update an element on your client's interface, you must call addUIComponentToUpdateByAjax() at the end of the execute method:

    event.getRequestContext().addUIComponentToUpdateByAjax(uicomponent);
    

    The target component must be provided as parameter (the component that will be updated). We will come back on this later.

  6. You must create one inner action listener class for each Ajax call you want to handle on this portlet. All these classes must be declared in the configuration annotations of the main class, otherwise you will get an error.

  7. Done. Your portlet is ready to accept Ajax calls from your client.

All javascript in GateIn 3.0 is managed by the file 02eXoresources:javascript/eXo/portal/PortalHttpRequest.js.

In this class, there are four functions/classes:

PortletResponse

PortalResponse

AjaxRequest

HttpResponseHandler

and 6 functions:

In addition to the content in this chapter, refer also to Section 6.1, “AJAX Framework” in order to better understand the communication between the Groovy Template and the portlet.

The configuration of a portlet is partly made with ComponentConfig annotations (others are ComponentConfigs, EventConfig, etc).

One of the parameters of this annotation is template. This is where the path to the template file associated with this portlet is defined.

To specify this parameter, add this statement to the configuration annotation.

For example, in src:/portlet/exoadmin/src/main/java/org/exoplatform/applicationregistry/webui/component/ is the UIApplicationForm.java file:

@ComponentConfig(
  lifecycle = UIFormLifecycle.class,
  template =  "system:/groovy/webui/form/UIFormWithTitle.gtmpl",
  events = {
    @EventConfig(listeners = UIApplicationForm.SaveActionListener.class),
    @EventConfig(phase = Phase.DECODE, listeners = UIApplicationForm.CancelActionListener.class)
  }
)

The path is in the "system" namespace. This is a reference to the portal webapp.

This webapp contains reusable groovy templates in the folder; src:/web/portal/src/main/webapp/groovy/webui/form/ .

Use the following steps to create a new template;

Component template files are composed in HTML code and groovy code blocks. There are a few things more that you need to know to fully link your portlet with your template.

To successfully link a portlet with a template, please ensure the following:

This chapter does not to refer to the Portlet API specification lifecycle but focuses on the GateIn 3.0 UI framework.

This web framework has been developed specifically for GateIn 3.0 and, while it is not necessary to use the native web framework to build portlets, all portlets packaged with GateIn 3.0 are developed using this framework.

This consistency allows portlets to use several UI components that can be used in different abstracted contexts (such as the portal itself or some portlets).

The main entry point for configuring a portlet is in the portlet.xml file located in the portlet application WAR.

Each portlet built using the GateIn 3.0 web framework must reference the PortletApplicationController .

The portlet configuration (such as the root component) is defined in configuration.xml. The path to this file is defined in the init-param "webui.configuration" of portlet.xml.

<portlet>
  <description xml:lang="EN">Content Portlet</description>
  <portlet-name>ContentPortlet</portlet-name>
  <display-name xml:lang="EN">Content Portlet</display-name>
  <portlet-class>org.exoplatform.webui.application.portlet.PortletApplicationController</portlet-class>    
    
  <init-param>
    <name>webui.configuration</name>
    <value>/WEB-INF/conf/portlet/content/ContentPortlet/webui/configuration.xml</value>
  </init-param>
</portlet>

The structure of the configuration.xml file is exactly the same as the webui-configuration.xml which was introduced in Section 5.1, “Portal Lifecycle”.

In the case of the content portlet it is formatted as:

<webui-configuration>
  <application> 
    <ui-component-root>org.exoplatform.content.webui.component.UIContentPortlet</ui-component-root>
    <state-manager>org.exoplatform.webui.application.portlet.ParentAppStateManager</state-manager>
  </application>
</webui-configuration>

The PortletApplicationController class extends the GenericPortlet class defined in the Portlet API specification.

All methods (like processAction() or render()) are delegated to the PortletApplication. The creation and caching inside the WebController object is shown in the example below:

/**
 * try to obtain the PortletApplication from the WebAppController.
 * 
 * If it does not exist a new PortletApplication object is created, init and cached in the
 * controller
 */
private PortletApplication getPortletApplication() throws Exception {
  PortalContainer container = PortalContainer.getInstance() ;
  WebAppController controller = 
    (WebAppController)container.getComponentInstanceOfType(WebAppController.class) ;
  PortletApplication application = controller.getApplication(applicationId_) ;
  if(application == null) {
    application = new PortletApplication(getPortletConfig()) ;
    application.onInit() ; 
    controller.addApplication(application) ;
  }
  return application ;
}

The code of the method in PortletApplication is described below. The business logic is shown in the javadoc:

/**
 * The processAction() method is the one modelled according to the Portlet API specification
 * 
 * The process is quite simple and here are te different steps done in the method:
 * 
 * 1) The current instance of the WebuiRequestContext (stored in a ThreadLocal in the class) is referenced
 * 2) A new request context of type PortletRequestContext (which extends the class WebuiRequestContext) is
 *    created as a child of the current context instance
 * 3) The new context is place inside the ThreadLocal and hence overides its parent one there, 
 *    only for the portlet request lifeciclye
 * 4) The method onStartRequest() is called in all the ApplicationLifecycle objects referenced in the webui 
 *    configuration XML file
 * 5) The StateManager object (in case of portlet it is an object of type ParentAppStateManager) is used to get the RootComponent
 *    also referenced in the XML configuration file
 * 6) The methods processDecode(UIApplication, WebuiRequestContext) and processAction(UIApplication, WebuiRequestContext) 
 *     are then called 
 * 7) Finally, a flag, to tell that the processAction phase was done, in the context is set to true and the parent
 *    context is restored in the Threadlocal
 */
public void processAction(ActionRequest req, ActionResponse res) throws Exception {
  WebuiRequestContext parentAppRequestContext =  WebuiRequestContext.getCurrentInstance() ;
  PortletRequestContext context = createRequestContext(req, res, parentAppRequestContext)  ;
  WebuiRequestContext.setCurrentInstance(context) ;
  try {      
    for(ApplicationLifecycle lifecycle : getApplicationLifecycle())  {
      lifecycle.onStartRequest(this, context) ;
    } 
    UIApplication uiApp = getStateManager().restoreUIRootComponent(context) ;
    context.setUIApplication(uiApp) ;
    processDecode(uiApp, context) ;
    if(!images/context.isResponseComplete() &&!images/ context.getProcessRender()) {
      processAction(uiApp, context) ;
    }
  } finally {
    context.setProcessAction(true) ;
    WebuiRequestContext.setCurrentInstance(parentAppRequestContext) ;
  }
}

The PortletRequestContext extends the WebuiRequestContext class and acts as a wrapper on all the portlet request information:

/**
 * In this method we try to get the PortletRequestContext object from the attribute map of the parent 
 * WebuiRequestContext. 
 * 
 * If it is not cached then we create a new instance, if it is cached then we init it with the correct
 * writer, request and response objects
 * 
 * We finally cache it in the parent attribute map
 * 
 */
private PortletRequestContext createRequestContext(PortletRequest req, PortletResponse res,
                                                  WebuiRequestContext parentAppRequestContext) throws IOException {
  String attributeName = getApplicationId() + "$PortletRequest" ;
  PortletRequestContext context = 
    (PortletRequestContext) parentAppRequestContext.getAttribute(attributeName) ;
  Writer w  = null ;       
  if(res instanceof  RenderResponse){
    RenderResponse renderRes = (RenderResponse)res;
    renderRes.setContentType("text/html; charset=UTF-8");      
    w = renderRes.getWriter() ; 
  }
  if(context!images/= null) {
    context.init(w, req, res) ;
  } else {
    context =  new PortletRequestContext(this, w, req, res) ;
    parentAppRequestContext.setAttribute(attributeName, context) ;
  }
  context.setParentAppRequestContext(parentAppRequestContext) ;
  return context;
}

In the PortletApplication, the line;

UIApplication uiApp = getStateManager().restoreUIRootComponent(context);

asks the StateManager defined for the portlet to get the UI root component. In the case of a portlet the root component must extend UIPortletApplication.

public class ParentAppStateManager extends StateManager {
  
  /**
   * This method simply delegate the call to the same method of the parent WebuiRequestContext
   */
  @SuppressWarnings("unchecked")
  public UIApplication restoreUIRootComponent(WebuiRequestContext context) throws Exception {
    WebuiRequestContext pcontext = (WebuiRequestContext)  context.getParentAppRequestContext() ;
    return pcontext.getStateManager().restoreUIRootComponent(context) ;
  }

Hence this is the PortalStateManager that will also handle the extraction of the root component.

public UIApplication restoreUIRootComponent(WebuiRequestContext context) throws Exception {
  context.setStateManager(this) ;
  WebuiApplication app  = (WebuiApplication)context.getApplication() ;
  
  /*
   * If the request context is of type PortletRequestContext, we extract the parent context which will
   * allow to get access to the PortalApplicationState object thanks to the session id used as the key for the
   * syncronised Map uiApplications
   */
  if(context instanceof PortletRequestContext) {
    WebuiRequestContext preqContext = (WebuiRequestContext) context.getParentAppRequestContext() ;
    PortalApplicationState state = uiApplications.get(preqContext.getSessionId()) ;
    PortletRequestContext pcontext = (PortletRequestContext) context ;
    String key =  pcontext.getApplication().getApplicationId() ;
    UIApplication uiApplication =  state.get(key) ;
    if(uiApplication!images/= null)  return uiApplication;
    synchronized(uiApplications) {
      ConfigurationManager cmanager = app.getConfigurationManager() ;
      String uirootClass = cmanager.getApplication().getUIRootComponent() ;
      Class type = Thread.currentThread().getContextClassLoader().loadClass(uirootClass) ;
      uiApplication = (UIApplication)app.createUIComponent(type, null, null, context) ;     
      state.put(key, uiApplication) ;
    }
    return uiApplication ;
  }
}

The render method business logic is quite similar to processAction().

/**
 * The render method business logic is quite similar to the processAction() one.
 * 
 * 1) A PortletRequestContext object is created (or extracted from the cache if it already exists) 
 *    and initialized
 * 2) The PortletRequestContext replaces the parent one in the WebuiRequestContext ThreadLocal object
 * 3) If the portal has already called the portlet processAction() then the call to all onStartRequest of
 *    the ApplicationLifecycle has already been made, otherwise we call them
 * 4) The ParentStateManager is also used to get the UIApplication, as we have seen it delegates the call 
 *    to the PortalStateManager which caches the UI component root associated with the current application
 * 5) the processRender() method of the UIPortletApplucaton is called
 * 6) Finally, the method onEndRequest() is called on every ApplicationLifecycle referenced in the portlet
 *    configuration XML file and the parent WebuiRequestContext is restored
 *    
 */
public  void render(RenderRequest req,  RenderResponse res) throws Exception {    
  WebuiRequestContext parentAppRequestContext =  WebuiRequestContext.getCurrentInstance() ;
  PortletRequestContext context = createRequestContext(req, res, parentAppRequestContext)  ;
  WebuiRequestContext.setCurrentInstance(context) ;
  try {
    if(!context.hasProcessAction()) {
      for(ApplicationLifecycle lifecycle : getApplicationLifecycle())  {
        lifecycle.onStartRequest(this, context) ;
      }
    }      
    UIApplication uiApp =  getStateManager().restoreUIRootComponent(context) ;
    context.setUIApplication(uiApp) ;
    if(!context.isResponseComplete()) {
      UIPortletApplication uiPortletApp = (UIPortletApplication)uiApp;
      uiPortletApp.processRender(this, context) ;
    }
    uiApp.setLastAccessApplication(System.currentTimeMillis()) ;
  } finally {
    try {
      for(ApplicationLifecycle lifecycle :  getApplicationLifecycle()) {
        lifecycle.onEndRequest(this, context) ;
      }
    } catch (Exception exception){
  	log.error("Error while trying to call onEndRequest of the portlet ApplicationLifecycle", 
  		exception);
    }
    WebuiRequestContext.setCurrentInstance(parentAppRequestContext) ;
  }
}

The following is the processRender() call made on the UIPortletApplication:

/**
 * The default processRender for an UIPortletApplication handles two cases:
 * 
 *   A. Ajax is used 
 *   ----
 *     If Ajax is used and that the entire portal should not be re rendered, then an AJAX fragment is 
 *     generated with information such as the portlet id, the portlet title, the portlet modes, the window 
 *     states as well as the HTML for the block to render
 *   
 *   B. A full render is made
 *   ----
 *      a simple call to the method super.processRender(context) which will delegate the call to all the 
 *      Lifecycle components
 *   
 */
public void  processRender(WebuiApplication app, WebuiRequestContext context) throws Exception {
  WebuiRequestContext pContext = (WebuiRequestContext)context.getParentAppRequestContext();
  if(context.useAjax() &&!images/pContext.getFullRender()) {
    Writer w =  context.getWriter() ;
    
    Set<UIComponent> list = context.getUIComponentToUpdateByAjax() ;
    if(list!images/= null) {
      if(getUIPopupMessages().hasMessage()) context.addUIComponentToUpdateByAjax(getUIPopupMessages()) ;
      for(UIComponent uicomponent : list) {
        renderBlockToUpdate(uicomponent, context, w) ;
      }
      return ;
    }
  }
  super.processRender(context) ;    
}

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.0 is no exception. In fact, GateIn 3.0 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.0 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.0. A sample portlet called SimplestHelloWorld is located in the examples directory at the root of your GateIn 3.0 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.0 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.

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.0 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 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 3.0 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 3.0 provides a Medium level of support for WSRP, except that we only handle HTML markup (as GateIn 3.0 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 Portal 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).

GateIn 3.0 doesNOT, 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 adding a remotable element to the jboss-portlet.xml deployment descriptor for that portlet. If a jboss-portlet.xml file does not exist, one must be added to the WEB-INF folder of the web application containing the portlet.

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


It is also possible to specify that all the portlets declared within a given jboss-portlet.xml file have a specific "remotable" status by default. This is done by adding a single remotable element to the root portlet-app element. Usually, this feature will be used to remotely expose several portlets without having to specify the status for all the declared portlets. Let's look at an example:


In the example above, we defined two portlets with a default "remotable" status set to true. This means that all portlets defined in this descriptor are, by default, exposed remotely by GateIn 3.0's WSRP producer. Note, however, that it is possible to override the default behavior by adding a remotable element to a portlet description. In that case, the "remotable" status defined by the portlet takes precedence over the default. In the example above, the RemotelyExposedPortlet inherits the "remotable" status defined by default since it does not specify a remotable element in its description. TheNotRemotelyExposedPortlet, however, overrides the default behavior and is not remotely exposed. Note that in the absence of a top-level remotable element, portlets are NOT remotely exposed.

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

GateIn 3.0'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}/portal-wsrp/MarkupService?wsdl. You can access the endpoint URLs at:

  • http://{hostname}:{port}/portal-wsrp/ServiceDescriptionService
  • http://{hostname}:{port}/portal-wsrp/MarkupService
  • http://{hostname}:{port}/portal-wsrp/RegistrationService
  • http://{hostname}:{port}/portal-wsrp/PortletManagementService

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 3.0. We will configure access to BEA'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 accomplish with a producer descriptor.

As of Portal 2.6, a configuration portlet is provided to configure access to remote WSRP Producers grahically. You can access it at http://{hostname}:{port}/portal/auth/portal/admin/WSRP or by logging in as a Portal administrator and clicking on the WSRP tab in the Admin portal. If all went well, you should see something similar to this:

This screen presents all the configured producers 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. Deactivating it will remove it from the list of available portlet providers. 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.

Next, we create a new Consumer which we will callbea. Type "bea" 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 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 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 and that it expects a value for the registration property namedregistration/consumerRole:

Enter "public" as the value for the registration property and press "Save & Refresh" once more. You should now see something similar to:

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

A producer is configured, by default, by retrieving the producer's information via a WSDL URL. There are rare cases, however, where URLs need to be provided for each of the producer's end points. You can do exactly that by unchecking the "Use WSDL?" checkbox, as is the case for the self producer:

A WSRP Producer descriptor is an XML file which name ends in -wsrp.xml and which can be dropped in the deploy directory of the JBoss application server or nested in .sar files. It is possible to configure access to several different producers within a single descriptor or use one file per producer, depending on your needs. An XML Schema for the WSRP Producer descriptor format can be found at jboss-portal.sar/portal-wsrp.sar/xsd/jboss-wsrp-consumer_2_6.xsd, while a (legacy) DTD can be found atjboss-portal.sar/portal-wsrp.sar/dtd/jboss-wsrp-consumer_2_6.dtd.

Let's now look at which information needs to be provided to configure access to a remote producer.

First, we need to provide an identifier for the producer we are configuring so that we can refer to it afterwards. This is accomplished via the mandatory id attribute of the <wsrp-producer> element.

GateIn 3.0 also needs to learn about the remote producer's endpoints to be able to connect to the remote web services and perform WSRP invocations. Two options are currently supported to provide this information:

Both the id attribute and either <endpoint-config> or <endpoint-wsdl-url> elements are required for a functional remote producer configuration.

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

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 Portal consumer can register with the remote producer when required.

Registration configuration is done via the <registration-data> element. Since GateIn 3.0 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 3.0 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 self producer as found in default-wsrp.xml with a cache expiring every five minutes:


Here is an example of a WSRP descriptor with a 2 minute caching time and manual definition of the endpoint URLs:


Here is an example of a WSRP descriptor with endpoint definition via remote WSDL file, 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 assert which level of service to provide to consumers based on the values of given registration properties.

It is therefore sometimes necessary to modify the registration that concretizes the service agreement between a consumer and a producer. An example of easily available producer offering different level of services is BEA's public producer. We configured access to that producer in Section 8.6.2.1, “Using the configuration portlet”. If you recall, the producer was requiring registration and required a value to be provided for the registration/consumerRole property. The description of that property indicated that three values were possible:public, partner or insider each corresponding to a different service level. We registered using the public service level. This gave us access to three portlets as shown here:

Suppose now that we would like to upgrade our service level to the "insider" level. We will need to tell BEA's 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 bea producer and modify the value of the registration/consumerRole to insider instead ofpublic:

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:

You can now check the list of available portlets from the bea provider and verify that new portlets are now available:

It can also happen that a producer administrator decided to require more information from registered consumers. In this case, invoking operations on the producer will fail with an OperationFailedFault. GateIn 3.0 will attempt to help you in this situation. Let's walk through an example using the self producer. Let's assume that registration is required without any registration properties (as is the case by default). If you go to the configuration screen for this producer, you should see:

Now suppose that the administrator of the producer now requires a value to be provided for an email registration property. We will actually see how to do perform this operation in GateIn 3.0 when we examine how to configure Portal's producer inSection 8.8, “Configuring GateIn 3.0'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 email 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

As of WSRP 1, it is 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.

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 8.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 3.0 2.6.3 introduces 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, whichRegistrationPropertyValidator), along with required registration property description for which consumers must provide acceptable values to successfully register.

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 8.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 inSection 8.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.jboss.portal.registration.RegistrationPolicy and org.jboss.portal.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.