Red Hat JBoss Portal 6.0

Reference Guide

For use with Red Hat JBoss Portal 6.0.

Edition 6.0.0

Red Hat Documentation Group

Legal Notice

Copyright © 2013 Red Hat, Inc.
This document is licensed by Red Hat under the Creative Commons Attribution-ShareAlike 3.0 Unported License. If you distribute this document, or a modified version of it, you must provide attribution to Red Hat, Inc. and provide a link to the original. If the document is modified, all Red Hat trademarks must be removed.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, JBoss, MetaMatrix, Fedora, the Infinity Logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat Software Collections is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack Logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.

Abstract

This Reference Guide is a high-level usage document. It deals with more advanced topics than the Installation and User Guides, adding new content or taking concepts discussed in the earlier documents further. It aims to provide supporting documentation for advanced users of the product. Its primary focus is on advanced use of the product and it assumes an intermediate or advanced knowledge of the technology and terms.
Preface
1. Document Conventions
1.1. Typographic Conventions
1.2. Pull-quote Conventions
1.3. Notes and Warnings
2. Getting Help and Giving Feedback
2.1. Do You Need Help?
2.2. Give us Feedback
1. Introduction
1.1. File Name Conventions
1.2. Related Links
2. JBoss Enterprise Application Platform 6 Integration
2.1. Primary Directories and Files
2.2. JBoss Portal Platform Subsystem
2.3. Standalone Mode
I. Portal Development
3. Skinning the Portal
3.1. Overview
3.2. Skin Components
3.3. Skin Selection
3.3.1. Skin Selection Through the User Interface
3.3.2. Setting the Default Skin within the Configuration Files
3.4. Skins in Page Markups
3.5. The Skin Service
3.5.1. Skin configuration
3.5.2. Resource Request Filter
3.6. The Default Skin
3.7. Creating New Skins
3.7.1. Creating a New Portal Skin
3.7.2. Creating a New Window Style
3.7.3. How to Create New Portlet Skins
3.7.4. Create New Portlet Specification CSS Classes
3.8. Tips and Tricks
3.8.1. Easier CSS Debugging
3.8.2. Some CSS Techniques
4. Portal Life-cycle
4.1. Overview
4.2. Application Server start and stop
4.2.1. Advanced WCI Registration
4.3. The Command Servlet
5. Default Portal Configuration
5.1. Overview
5.2. Configuration
5.3. Delete Portal Definitions using Component Plug-ins
5.4. Set the Info Bar Behavior
5.5. Disabling a Portal Container
6. Portal Default Permission Configuration
6.1. Overview
6.2. Overwrite Portal Default Permissions
7. Portal Navigation Configuration
7.1. Overview
7.2. Portal Navigation
7.3. Group Navigation
7.4. User Navigation
8. Data Import Strategy
8.1. Introduction
8.2. Data Import Strategy
8.2.1. Portal Configuration
8.2.2. Page Data
8.2.3. Navigation Data
9. Internationalization Configuration
9.1. Overview
9.2. Locales Configuration
9.3. ResourceBundleService
9.4. Navigation Resource Bundles
9.5. Portlets
9.5.1. Standard Portlet Resource Keys
9.5.2. Debugging Resource Bundle Usage
9.5.3. Translate the Language Selection Form
10. Localization Configuration
10.1. Pluggable Locale Policy
10.1.1. LocalePolicy API
10.1.2. Default LocalePolicy
10.1.3. Custom LocalePolicy
10.1.4. LocalePolicy Configuration
10.1.5. Keeping non-bridged resources in sync with current Locale
11. XML Resources Bundles
11.1. Overview
11.2. XML format
11.3. Portal Support
12. Right To Left (RTL) Framework
12.1. Groovy templates
12.2. Stylesheet
12.3. Images
12.4. Client Side JavaScript
13. JavaScript Inter Application Communication
13.1. Overview
13.2. Library
13.3. Syntax
13.4. Example of JavaScript events usage
14. Navigation Controller
14.1. Description
14.2. Controller in Action
14.2.1. Controller
14.2.2. Building Controller
14.2.3. Controller Configuration (controller.xml)
14.3. Integrate to JBoss Portal Platform WebUI framework
14.3.1. Routing
14.3.2. Rendering
II. Portlet development
15. Portlet Primer
15.1. JSR-168 and JSR-286 overview
15.1.1. Portal Pages
15.1.2. Rendering Modes
15.1.3. Window States
15.2. Tutorials
15.2.1. Deploying your first portlet
15.2.2. JavaServer Pages Portlet Example
16. Shared portlet.xml
16.1. Global Metadata Elements
17. JBoss Portlet Bridge
17.1. Portlet Bridge
17.2. JBoss Portlet Bridge
17.3. Portlet application
17.4. Extensions
17.5. Examples
17.6. Bridge Configuration
17.6.1. portlet.xml
17.7. Render Policy Parameters
17.8. Facelets Configuration
17.8.1. web.xml
17.9. JSP Only Configuration
17.9.1. web.xml
17.10. RichFaces Local and Remote Portlet Support
17.11. Sending and Receiving Events
17.11.1. Sending Events
17.11.2. Receiving Events
17.11.3. Public Render Parameters
17.11.4. Portlet Session
17.12. Resource serving
17.13. Serving JSF Resources in a Portlet
17.13.1. Expression Language Reference
17.13.2. Configuration
17.14. Developing Portlets with the Bridge
17.14.1. Implementing Portlet Bridge
17.14.2. Portlet tags
17.14.3. Excluding Attributes from the Bridge Request Scope
17.14.4. Prevent Resources Being Added to Portal Page Head
17.14.5. JSF facelet view
17.14.6. Error handling
17.14.7. Switching Portlet Modes
17.14.8. Navigating to a mode's last viewId
17.14.9. Using Wildcards to Identify the Rule Target
17.14.10. Clearing The View History When Changing Portlet Modes
17.14.11. Communication Between Your Portlets
17.14.12. Linking to a Facelets page within the same portlet
17.14.13. Redirecting to an external page or resource
17.14.14. Using Provided EL Variables
III. Authentication and Authorization
18. Introduction to Authentication and Authorization
18.1. Authentication Overview
18.2. Login Modules
18.2.1. Existing Login Modules
18.2.2. Creating Your Own Login Module
18.2.3. Authenticator and RolesExtractor
18.3. Different Authentication Workflows
18.3.1. RememberMe Authentication
18.3.2. Authorization Overview
19. Password Encryption
19.1. Hashing and Salting of Passwords in PicketLink IDM
19.1.1. Choosing CredentialEncoder Implementation
19.2. Password Encryption of Rememberme Passwords
20. Predefined User Configuration
20.1. Plug-in for Adding Users, Groups and Membership Types
20.2. Membership Types
20.3. Groups
20.4. Users
20.5. Plug-in for Monitoring User Creation
21. Authentication Token Configuration
21.1. The Token Service
21.2. Implementing the Token Service API
21.3. Configuring Token Services
22. PicketLink IDM Integration
22.1. Configuration Files
22.1.1. PicketlinkIDMServiceImpl
22.1.2. PicketlinkIDMOrganizationServiceImpl
23. Organization API
24. Accessing User Profile
25. Create Users and Groups without Organization API
25.1. Enable Initializer
25.2. Operations
25.3. Using configuration directives
25.4. Using JMX Console
25.5. Using REST Interface
26. Single Sign-On
26.1. Overview and Configuration Assumptions
26.2. Central Authentication Service (CAS)
26.2.1. Authentication Process
26.2.2. Logout Process
26.2.3. CAS Configuration Overview
26.2.4. Modifying the CAS server
26.2.5. Install Apache Tomcat Server
26.2.6. Modifying the Portal
26.2.7. Build and Deploy the CAS
26.3. Java Open Single Sign-On (JOSSO)
26.3.1. Authentication Process
26.3.2. JOSSO 1.8
26.3.3. JOSSO 2.2
26.4. OpenAM
26.4.1. Login and Logout Workflow
26.4.2. Obtaining OpenAM
26.4.3. OpenAM Server Setup
26.4.4. JBoss Portal Platform Setup as OpenAM Client
26.4.5. Cross-domain Authentication with OpenAM
26.5. Simple and Protected GSSAPI Negotiation Mechanism (SPNEGO)
26.5.1. SPNEGO Server Configuration
26.5.2. Client Configuration
26.5.3. JBoss Portal Platform Configuration
26.5.4. SPNEGO Configuration Testing
26.5.5. Additional Configuration
26.6. Single Sign-On in a Cluster
26.6.1. Default Configuration
26.6.2. Clustered Single Sign-On in a Shared DNS Domain
26.6.3. Reauthentication
27. LDAP Integration
27.1. LDAP in Read-only Mode
27.2. LDAP as Default Store
27.3. Examples
28. Security Assertion Markup Language (SAML2)
28.1. What is SAML2
28.2. What is an Assertion
28.3. What is an Identity Provider (IDP)
28.4. What is a Service Provider (SP)
28.5. SAML2 Authentication Overview
28.6. The platform as SAML2 SP and SAML2 IDP
28.7. Disable SAML2 Single logout
28.8. Implementing Keystores
28.9. Setup with Picketlink IDP using REST callback
28.10. Integration with Salesforce and Google Apps
28.10.1. Scenario One
28.10.2. Scenario Two
28.10.3. Scenario Three
28.10.4. Tips and Tricks
IV. Web Services for Remote Portlets (WSRP)
29. Web Services for Remote Portlets (WSRP)
29.1. Introduction
29.2. Level of support in JBoss Portal Platform
29.3. Deploying JBoss Portal Platform's WSRP services
29.3.1. Considerations to use WSRP when running JBoss Portal Platform on a non-default port or hostname
29.4. Securing WSRP
29.4.1. WSRP over SSL with HTTPS endpoints
29.4.2. WSRP and WS-Security
29.4.3. Credentials
29.5. Making a portlet remotable
29.6. Consuming WSRP portlets from a remote Consumer
29.7. Consuming remote WSRP portlets in JBoss Portal Platform
29.7.1. Overview
29.7.2. Configuring a remote producer using the configuration portlet
29.7.3. Configuring access to remote producers via XML
29.7.4. Adding remote portlets to categories
29.7.5. Adding remote portlets to pages
29.8. Consumers maintenance
29.8.1. Modifying a currently held registration
29.8.2. Consumer operations
29.8.3. Importing and exporting portlets
29.8.4. Erasing local registration data
29.9. Configuring JBoss Portal Platform's WSRP Producer
29.9.1. Overview
29.9.2. Default configuration
29.9.3. Registration configuration
29.9.4. WSRP validation mode
29.10. Working with WSRP Extensions
29.10.1. Overview
29.10.2. Example implementation
V. Advanced Development Foundations
30. The eXo Kernel
30.1. eXo Kernel
30.1.1. Kernel configuration namespace
30.2. Configuration Retrieval
30.3. Advanced concepts for the PortalContainers
30.3.1. Add new configuration files from a WAR file
30.3.2. Creating your PortalContainers from a WAR file
30.3.3. Defining a PortalContainer with its dependencies and its settings
30.3.4. PortalContainer settings
30.3.5. Adding dynamically settings and/or dependencies to a PortalContainer
30.3.6. Disable dynamically a portal container
30.4. Runtime configuration profiles
30.4.1. Profiles activation
30.4.2. Profiles configuration
30.5. Component request life cycle
30.5.1. Component request life cycle contract
30.5.2. Request life cycle
30.6. Configuring Services
30.6.1. Configuration syntax
30.7. Specific Services
30.7.1. ListenerService
30.7.2. Understanding the ListenerService
30.7.3. Job Schedule
30.7.4. The data source provider
30.8. Configuring a portal container
30.9. System property configuration
30.9.1. Properties <init-param>
30.9.2. Properties URL <init-param>
30.9.3. System Property configuration of the properties URL
30.9.4. Variable Syntaxes
30.10. The Extension Mechanism and Portal Extensions
30.10.1. Running Multiple Portals
30.11. Manageability
30.11.1. Introduction
30.11.2. Managed framework API
30.11.3. JMX Management View
30.11.4. Example
VI. The Java Content Repository (JCR)
31. Introduction
31.1. Concepts
32. Implementation
32.1. Workspace Data Model
33. JCR configuration
33.1. Portal configuration
33.1.1. JCR Configuration
33.1.2. Repository service configuration (JCR repositories configuration)
33.1.3. Workspace configuration:
33.1.4. Workspace data container configuration:
33.1.5. Value Storage plug-in configuration (for data container):
33.1.6. Initializer configuration (optional):
33.1.7. Cache configuration:
33.1.8. Query Handler configuration:
33.1.9. Lock Manager configuration:
34. Multi-language Support
34.1. Oracle
34.2. DB2
34.3. MySQL
34.4. PostgreSQL
35. Configuring Search
35.1. Global Search Index
35.2. IndexingConfiguration
35.3. Advanced features
36. Configuring the JDBC Data Container
36.1. Introduction
36.2. Multi-database Configuration
36.3. Single-database Configuration
36.3.1. Configuration without DataSource
36.3.2. Dynamic Workspace Creation
36.4. Simple and Complex queries
36.5. Force Query Hints
36.6. Notes for Microsoft Windows users
37. External Value Storages
37.1. Introduction
37.2. Tree File Value Storage
37.3. Disabling value storage
38. Workspace Data Container
39. Configuring Cluster
39.1. Launching Cluster
39.1.1. Configuring JCR to use external configuration
39.2. Requirements
39.2.1. Environment requirements
39.2.2. Configuration requirements
40. Configuring JBoss Cache
40.1. Indexer, lock manager and data container configuration
40.2. JGroups configuration
40.3. Sharing JBoss Cache instances
40.4. Shipped JBoss Cache configuration templates
40.4.1. Data container template
40.4.2. Lock manager template
40.4.3. Query handler (indexer) template
41. LockManager
41.1. CacheableLockManagerImpl
41.1.1. Simple JBoss Cache Configuration
41.1.2. Template JBoss Cache Configuration
41.1.3. Lock Migration
42. Configuring QueryHandler
42.1. Indexing in clustered environment
42.2. Configuration
42.2.1. Query-handler configuration overview
42.2.2. Cluster-ready indexing
42.2.3. Local Index Recovery Filters
42.2.4. JBoss-Cache template configuration
42.3. Asynchronous Re-indexing
42.3.1. On startup indexing
42.3.2. Hot Asynchronous Workspace Re-indexing using JMX
42.3.3. Notices
42.4. Advanced tuning
42.4.1. Lucene tuning
43. JBossTransactionsService
43.1. Introduction
43.2. Configuration
44. JCR Query Use-cases
44.1. Introduction
44.2. Query Lifecycle
44.2.1. Query Creation and Execution
44.2.2. Query Result Processing
44.2.3. Scoring
44.3. Tips and tricks
44.3.1. XPath queries containing node names starting with a number
45. Searching Repository Content
45.1. Introduction
45.2. Bi-directional RangeIterator
45.3. Fuzzy Searches
45.4. SynonymSearch
45.5. Highlighting
45.5.1. DefaultXMLExcerpt
45.5.2. DefaultHTMLExcerpt
45.5.3. Usage
45.6. SpellChecker
45.6.1. Usage
45.7. Similarity
46. Full Text Search And Affecting Settings
46.1. Lucene Analyzers
46.2. Property Indexing
46.3. Different Analyzers
47. WebDAV
47.1. Introduction
47.2. WebDAV Configuration
47.3. Corresponding WebDAV and JCR actions
47.4. WebDAV Considerations
48. FTP
48.1. Introduction
48.2. Configuration Parameters
49. Use External Backup Tool
49.1. Repository Suspending
49.2. Backup
49.3. Repository Resuming
50. eXo JCR statistics
50.1. Statistics on the Database Access Layer
50.2. Statistics on the JCR API accesses
50.3. Statistics Manager
51. Checking repository integrity and consistency
51.1. JMX-based consistency tool
52. JCR Performance Tuning Guide
52.1. Introduction
52.2. JCR Performance and Scalability
52.2.1. Cluster configuration
52.2.2. JCR Clustered Performance
52.3. Performance Tuning Guide
52.3.1. JBoss Enterprise Application Platform 6 Tuning
52.3.2. JCR Cache Tuning
52.3.3. Clustering
53. eXo JCR with JBoss Portal Platform
53.1. How to use a Managed DataSource under JBoss Enterprise Application Platform 6
53.1.1. Configurations Steps
A. Revision History

Preface

1. Document Conventions

This manual uses several conventions to highlight certain words and phrases and draw attention to specific pieces of information.
In PDF and paper editions, this manual uses typefaces drawn from the Liberation Fonts set. The Liberation Fonts set is also used in HTML editions if the set is installed on your system. If not, alternative but equivalent typefaces are displayed. Note: Red Hat Enterprise Linux 5 and later include the Liberation Fonts set by default.

1.1. Typographic Conventions

Four typographic conventions are used to call attention to specific words and phrases. These conventions, and the circumstances they apply to, are as follows.
Mono-spaced Bold
Used to highlight system input, including shell commands, file names and paths. Also used to highlight keys and key combinations. For example:
To see the contents of the file my_next_bestselling_novel in your current working directory, enter the cat my_next_bestselling_novel command at the shell prompt and press Enter to execute the command.
The above includes a file name, a shell command and a key, all presented in mono-spaced bold and all distinguishable thanks to context.
Key combinations can be distinguished from an individual key by the plus sign that connects each part of a key combination. For example:
Press Enter to execute the command.
Press Ctrl+Alt+F2 to switch to a virtual terminal.
The first example highlights a particular key to press. The second example highlights a key combination: a set of three keys pressed simultaneously.
If source code is discussed, class names, methods, functions, variable names and returned values mentioned within a paragraph will be presented as above, in mono-spaced bold. For example:
File-related classes include filesystem for file systems, file for files, and dir for directories. Each class has its own associated set of permissions.
Proportional Bold
This denotes words or phrases encountered on a system, including application names; dialog box text; labeled buttons; check-box and radio button labels; menu titles and sub-menu titles. For example:
Choose SystemPreferencesMouse from the main menu bar to launch Mouse Preferences. In the Buttons tab, select the Left-handed mouse check box and click Close to switch the primary mouse button from the left to the right (making the mouse suitable for use in the left hand).
To insert a special character into a gedit file, choose ApplicationsAccessoriesCharacter Map from the main menu bar. Next, choose SearchFind… from the Character Map menu bar, type the name of the character in the Search field and click Next. The character you sought will be highlighted in the Character Table. Double-click this highlighted character to place it in the Text to copy field and then click the Copy button. Now switch back to your document and choose EditPaste from the gedit menu bar.
The above text includes application names; system-wide menu names and items; application-specific menu names; and buttons and text found within a GUI interface, all presented in proportional bold and all distinguishable by context.
Mono-spaced Bold Italic or Proportional Bold Italic
Whether mono-spaced bold or proportional bold, the addition of italics indicates replaceable or variable text. Italics denotes text you do not input literally or displayed text that changes depending on circumstance. For example:
To connect to a remote machine using ssh, type ssh username@domain.name at a shell prompt. If the remote machine is example.com and your username on that machine is john, type ssh john@example.com.
The mount -o remount file-system command remounts the named file system. For example, to remount the /home file system, the command is mount -o remount /home.
To see the version of a currently installed package, use the rpm -q package command. It will return a result as follows: package-version-release.
Note the words in bold italics above — username, domain.name, file-system, package, version and release. Each word is a placeholder, either for text you enter when issuing a command or for text displayed by the system.
Aside from standard usage for presenting the title of a work, italics denotes the first use of a new and important term. For example:
Publican is a DocBook publishing system.

1.2. Pull-quote Conventions

Terminal output and source code listings are set off visually from the surrounding text.
Output sent to a terminal is set in mono-spaced roman and presented thus:
books        Desktop   documentation  drafts  mss    photos   stuff  svn
books_tests  Desktop1  downloads      images  notes  scripts  svgs
Source-code listings are also set in mono-spaced roman but add syntax highlighting as follows:
package org.jboss.book.jca.ex1;

import javax.naming.InitialContext;

public class ExClient
{
   public static void main(String args[]) 
       throws Exception
   {
      InitialContext iniCtx = new InitialContext();
      Object         ref    = iniCtx.lookup("EchoBean");
      EchoHome       home   = (EchoHome) ref;
      Echo           echo   = home.create();

      System.out.println("Created Echo");

      System.out.println("Echo.echo('Hello') = " + echo.echo("Hello"));
   }
}

1.3. Notes and Warnings

Finally, we use three visual styles to draw attention to information that might otherwise be overlooked.

Note

Notes are tips, shortcuts or alternative approaches to the task at hand. Ignoring a note should have no negative consequences, but you might miss out on a trick that makes your life easier.

Important

Important boxes detail things that are easily missed: configuration changes that only apply to the current session, or services that need restarting before an update will apply. Ignoring a box labeled 'Important' will not cause data loss but may cause irritation and frustration.

Warning

Warnings should not be ignored. Ignoring warnings will most likely cause data loss.

2. Getting Help and Giving Feedback

2.1. Do You Need Help?

If you experience difficulty with a procedure described in this documentation, visit the Red Hat Customer Portal at http://access.redhat.com. Through the customer portal, you can:
  • search or browse through a knowledgebase of technical support articles about Red Hat products.
  • submit a support case to Red Hat Global Support Services (GSS).
  • access other product documentation.
Red Hat also hosts a large number of electronic mailing lists for discussion of Red Hat software and technology. You can find a list of publicly available mailing lists at https://www.redhat.com/mailman/listinfo. Click on the name of any mailing list to subscribe to that list or to access the list archives.

2.2. Give us Feedback

If you find a typographical error, or know how this guide can be improved, we would love to hear from you. Submit a report in Bugzilla against the product JBoss Enterprise Portal Platform 6 and the component docs-Reference_Guide. The following link will take you to a pre-filled bug report for this product: http://bugzilla.redhat.com/.
Fill out the following template in Bugzilla's Description field. Be as specific as possible when describing the issue; this will help ensure that we can fix it quickly.
Document URL:


Section Number and Name:


Describe the issue:


Suggestions for improvement:


Additional information:


Be sure to give us your name so that you can receive full credit for reporting the issue.

Chapter 1. Introduction

JBoss Portal Platform is based on the GateIn Portal project. The aim is to provide an intuitive user-friendly portal, and a framework to address the needs of today's Web 2.0 applications.
Front Page

Figure 1.1. Front Page


This book provides deep-dive information about installation and configuration of the services provided by JBoss Portal Platform.

1.1. File Name Conventions

The following naming conventions are used in file paths to improve their readability. Each convention is styled so that it stands out from the rest of the text:
CAS_DIR
The installation root of the Central Authentication Service (CAS) single sign-on framework. This directory is an arbitrary location chosen when CAS is downloaded and installed.
ID_HOME
The JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/organization/ directory, which contains identity-related configuration resources.
This convention is mainly used in Chapter 27, LDAP Integration.
JPP_DIST
The installation root of the JBoss Portal Platform instance. For example, if the JBoss Portal Platform distribution archive is extracted to the /opt/jboss/JPP/ directory, the JPP_DIST directory is /opt/jboss/JPP.
This directory contains the jboss-jpp-6.0, gatein-management and gatein-sso directories, and is used extensively in sections that contain configuration stored in these directories.
JPP_HOME
The JPP_DIST/jboss-jpp-6.0 directory, which contains the application server and the configuration files necessary to run JBoss Portal Platform.
This directory contains the gatein, modules and standalone directories.
TOMCAT_HOME
The installation root of the Apache Tomcat server. Apache Tomcat is a simple Java-based web server that can host servlets or JSP applications. It is not a part of JBoss Portal Platform, however, it is used in various examples in this guide to host single sign-on authentication services.
This convention is mainly used in Chapter 26, Single Sign-On.

1.2. Related Links

Technical documentation
Technical documentation, including an Installation Guide, and a User Guide can be found at https://access.redhat.com/knowledge/docs/JBoss_Portal_Platform/
Non-technical documentation
Links to non-technical documents are available by clicking "Portals the Red Hat way", or "Launch fast with Maven Quick Starts".
Videos
A link to videos related to the JBoss Portal Platform is also included on the front page from the Videos tab.
These videos are prepared by GateIn Portal development team members using development versions of JBoss Portal Platform, and therefore should be used only as reference examples when configuring your production system.

Chapter 2. JBoss Enterprise Application Platform 6 Integration

JBoss Enterprise Application Platform 6 is a very compact, modular, and extensible application server, based on concurrent, and isolated class loading layer called JBoss Modules , and on fully parallel kernel called Modular Service Container (MSC) . On top of these there is a standalone server with a notion of subsystems, and centralized configuration management.
Most notable features of the platform are parallel deployment of application archives, and fine grained class isolation. The result is a combination of great performance, and multi-tenant coexistence of different applications without cross-influences between them.

2.1. Primary Directories and Files

There are several locations in JBoss Enterprise Application Platform 6 where JBoss Portal Platform adds code or configuration:
JPP_HOME/gatein
Contains the expanded gatein.ear deployment archive, which contains all the portal resources packaged as multiple web application archives.
The libraries are available as modules under JPP_HOME/modules , and are not part of the deployment archive. When implementing your portal by starting from scratch and replacing significant parts of the default portal with your own functionality, you can make changes directly in this directory to completely redefine many aspects of the portal.
JPP_HOME/gatein/extensions
Contains custom extensions, custom portals, and custom skins. Initially this directory is empty.
When building your portal as a customization of existing default portal, or when adding additional custom portals or custom skins, you would create your own portal extension archives and place them in this directory.
JPP_HOME/modules
Contains classes packaged as isolated modules. It contains both modules that come with JBoss Enterprise Application Platform 6, and additional modules added by JBoss Portal Platform.
Developers can add their own modules here. Libraries that need to be shared between different application archives should be packaged as JBoss modules, and placed here.
JPP_HOME/standalone/configuration/gatein
Contains gatein configurations, configured within the master configuration.properties file.
JPP_HOME/standalone/data/gatein
Contains JCR Lucene indexes.
JPP_HOME/standalone/deployments
A directory used by JBoss Enterprise Application Platform 6 deployment scanner to auto-detect and hot-deploy application archives. Portlet applications, web applications, and Java EE applications can be placed here in order to be deployed.
Do not place custom portal extensions, custom portals, or custom skins into this directory. Those archives deeply integrate with the JBoss Portal Platform kernel, and are not hot-deployable. They should be placed in JPP_HOME/gatein/extensions in order to be deployed as part of JBoss Portal Platform boot-time initialization.
JPP_HOME/standalone/configuration/standalone.xml
The master JBoss Enterprise Application Platform 6 configuration file. It contains JBoss Portal Platform subsystem configuration, configuration of datasources, configuration of security realms required by deployed portals, and configuration for all the other JBoss Enterprise Application Platform 6 subsystems.

2.2. JBoss Portal Platform Subsystem

JBoss Portal Platform integrates into JBoss Enterprise Application Platform 6 through a custom subsystem. The main JBoss Enterprise Application Platform 6 configuration file where subsystems are configured is located at JPP_HOME/standalone/configuration/standalone.xml .
JBoss Portal Platform subsystem is activated with the following xml snippet:
<subsystem xmlns="urn:jboss:domain:gatein:1.0">
  <portlet-war-dependencies>
     <dependency name="org.gatein.wci"/>
     <dependency name="org.gatein.pc"/>
     <dependency name="javax.portlet.api"/>
  </portlet-war-dependencies>
</subsystem>
The <portlet-war-dependencies> element specifies a list of libraries (available as modules under JPP_HOME/modules directory) that are automatically available to all portlet applications. The listed modules are required, and should not be removed. It is possible to add additional modules, which might allow you to deploy some existing portlet applications without repackaging them.
JBoss Enterprise Application Platform 6 provides mechanisms for configuring which modules are visible to a specific deployment archive. One mechanism involves adding additional attribute to deployment archive's MANIFEST.MF - Dependencies attribute in MANIFEST.MF . Another method involves adding a JBoss Enterprise Application Platform 6 specific descriptor file jboss-deployment-structure.xml to your deployment archive. For more information, refer to the "Class Loading and Modules" section of the JBoss Enterprise Application Platform 6 Developer Guide.
When the JBoss Portal Platform subsystem is active, it manages boot-time JBoss Portal Platform initialization. Boot time initialization expects to find the gatein.ear deployment archive at JPP_HOME/gatein/gatein.ear, and it looks for any additional deployment archives in JPP_HOME/gatein/extensions . These additional deployment archives can have any names, but have to be either one of .ear, .war, or .jar.
Archives placed in JPP_HOME/gatein/extensions are not hot-deployable. They are treated as extensions of default gatein.ear - meaning that this is a place for archives that integrate with JBoss Portal Platform configuration management system as they install additional JBoss Portal Platform kernel services, override default services configuration, or add/override default portal resources. All JBoss Portal Platform custom skins, custom portals, and custom extensions should be deployed to this directory.
JBoss Portal Platform's Extension mechanism is described in the "Portal Containers and Extensions" section of the JBoss Portal Platform 6 Developer Guide, and in the "Advanced Development Foundations" chapter in the JBoss Portal Platform 6 Reference Guide.

2.3. Standalone Mode

JBoss Enterprise Application Platform 6 supports two different running modes. It can run as a standalone server (a standalone mode ), or it can run as part of a server domain (a domain mode ).
JBoss Portal Platform 6 only supports standalone mode however.
The difference between the two modes is in configuration management. Domain mode provides central configuration management for multiple servers, while in standalone mode every server maintains its own local configuration.
There are no differences in available server features between the two modes, and advanced setups like clustering with session replication, and single sign-on can be configured in both standalone, and domain mode.

Part I. Portal Development

Table of Contents

3. Skinning the Portal
3.1. Overview
3.2. Skin Components
3.3. Skin Selection
3.3.1. Skin Selection Through the User Interface
3.3.2. Setting the Default Skin within the Configuration Files
3.4. Skins in Page Markups
3.5. The Skin Service
3.5.1. Skin configuration
3.5.2. Resource Request Filter
3.6. The Default Skin
3.7. Creating New Skins
3.7.1. Creating a New Portal Skin
3.7.2. Creating a New Window Style
3.7.3. How to Create New Portlet Skins
3.7.4. Create New Portlet Specification CSS Classes
3.8. Tips and Tricks
3.8.1. Easier CSS Debugging
3.8.2. Some CSS Techniques
4. Portal Life-cycle
4.1. Overview
4.2. Application Server start and stop
4.2.1. Advanced WCI Registration
4.3. The Command Servlet
5. Default Portal Configuration
5.1. Overview
5.2. Configuration
5.3. Delete Portal Definitions using Component Plug-ins
5.4. Set the Info Bar Behavior
5.5. Disabling a Portal Container
6. Portal Default Permission Configuration
6.1. Overview
6.2. Overwrite Portal Default Permissions
7. Portal Navigation Configuration
7.1. Overview
7.2. Portal Navigation
7.3. Group Navigation
7.4. User Navigation
8. Data Import Strategy
8.1. Introduction
8.2. Data Import Strategy
8.2.1. Portal Configuration
8.2.2. Page Data
8.2.3. Navigation Data
9. Internationalization Configuration
9.1. Overview
9.2. Locales Configuration
9.3. ResourceBundleService
9.4. Navigation Resource Bundles
9.5. Portlets
9.5.1. Standard Portlet Resource Keys
9.5.2. Debugging Resource Bundle Usage
9.5.3. Translate the Language Selection Form
10. Localization Configuration
10.1. Pluggable Locale Policy
10.1.1. LocalePolicy API
10.1.2. Default LocalePolicy
10.1.3. Custom LocalePolicy
10.1.4. LocalePolicy Configuration
10.1.5. Keeping non-bridged resources in sync with current Locale
11. XML Resources Bundles
11.1. Overview
11.2. XML format
11.3. Portal Support
12. Right To Left (RTL) Framework
12.1. Groovy templates
12.2. Stylesheet
12.3. Images
12.4. Client Side JavaScript
13. JavaScript Inter Application Communication
13.1. Overview
13.2. Library
13.3. Syntax
13.4. Example of JavaScript events usage
14. Navigation Controller
14.1. Description
14.2. Controller in Action
14.2.1. Controller
14.2.2. Building Controller
14.2.3. Controller Configuration (controller.xml)
14.3. Integrate to JBoss Portal Platform WebUI framework
14.3.1. Routing
14.3.2. Rendering

Chapter 3. Skinning the Portal

3.1. Overview

JBoss Portal Platform provides robust skinning support for the entire portal User Interface (UI). This includes support for skinning all of the common portal elements as well as being able to provide custom skins and window decoration for individual portlets. This has been designed with common graphic resource reuse and ease of development in mind.

3.2. Skin Components

The skin of a page is composed of three separate parts:
Portal Skin
The portal skin contains the CSS styles for the portal and its various UI components. This should include all the UI components except for the window decorators and portlet specific styles.
Window Styles
The CSS styles associated with the portlet window decorators. The window decorators contain the control buttons and borders surrounding each portlet. Individual portlets can have their own window decorator selected or be rendered without one.
Portlet Skins
The portlet skins dictate how portlets are rendered on the page. There are two main ways they can be effected:
Portlet Specification CSS Classes
The portlet specification defines a set of CSS classes that should be available to portlets. JBoss Portal Platform provides these classes as part of the portal skin. This allows each portal skin to define its own look and feel for these default values.
Portlet Skins
JBoss Portal Platform provides a means for portlet CSS files to be loaded based on the current portal skin. This allows a portlet to provide different CSS styles to better match the current portal look and feel. Portlet skins provide a much more customizable CSS experience than just using the portlet specification CSS classes.

CSS Classes

The window decorators and the default portlet specification CSS classes should be considered separate types of skinning components. They need to be included as part of the overall portal skin otherwise they will not be displayed correctly.
A portlet skin does not need to be included as part of the portal skin and can be included within the portlets web application.

3.3. Skin Selection

3.3.1. Skin Selection Through the User Interface

The easiest way to change a skin is to select it through the user interface. An administrator can change the default skin for the portal, or a logged in user can select which skin they would prefer to be displayed.
For more information about changing skins, see the JBoss Portal Platform User Guide at https://access.redhat.com/knowledge/docs/en-US/JBoss_Portal_Platform/6/html-single/User_Guide/index.html.

3.3.2. Setting the Default Skin within the Configuration Files

The default skin can also be configured using the portal configuration files. This allows the portal to have the new default skin ready for use when JBoss Portal Platform is first started.
The default skin of the portal is called Default. To change this value add a skin tag in the JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/portal/portal/classic/portal.xml configuration file.
To change the skin to MySkin you would make the following changes:
<portal-config>
    <portal-name>classic</portal-name>
    <locale>en</locale>
    <access-permissions>Everyone</access-permissions>
    <edit-permission>*:/platform/administrators</edit-permission>
    <skin>MySkin</skin>
    ...

3.4. Skins in Page Markups

A JBoss Portal Platform skin contains CSS styles for the portal's components but also shares components that may be reused in portlets. When JBoss Portal Platform generates a portal page markup, it inserts stylesheet links in the page's head tag.
There are two main types of CSS links that will appear in the head tag: a link to the portal skin CSS file and a link to the portlet skin CSS files.
Portal Skin
The portal skin will appear as a single link to a CSS file. This link will contain contents from all the portal skin classes merged into one file. This allows the portal skin to be transferred as a single file instead of multiple smaller files.
Portlet Skin
Each portlet on a page may contribute its own style. The link to the portlet skin will only appear on the page if that portlet is loaded on the current page. A page may contain many portlet skin CSS links or none.
In the code fragment below you can see the two types of links:
<head>
...
<!-- The portal skin -->
<link id="CoreSkin" rel="stylesheet" type="text/css" href="/eXoResources/skin/Stylesheet.css" />

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

CSS Classes

Window styles and the portlet specification CSS classes are included within the portal skin.

3.5. The Skin Service

The skin service manages the various types of skins. It is responsible for discovering and deploying skins into the portal.

3.5.1. Skin configuration

JBoss Portal Platform automatically discovers web archives that contain a file descriptor for skins (JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/gatein-resources.xml). This file is responsible for specifying the portal, portlet and window decorators to be deployed into the skin service.
The full schema can be found at: http://www.gatein.org/xml/ns/gatein_resources_1_2.
Below is an example of where to define a skin (MySkin) with its CSS location, and specify some window decorator skins:
<gatein-resources>
  <portal-skin>
    <skin-name>MySkin</skin-name>
    <css-path>/skin/myskin.css</css-path>
    <overwrite>false</overwrite>
  </portal-skin>
</gatein-resources>

  <!-- window style -->
  <window-style>
    <style-name>MyThemeCategory</style-name>
    <style-theme>
      <theme-name>MyThemeBlue</theme-name>
    </style-theme>
    <style-theme>
      <theme-name>MyThemeRed</theme-name>
    </style-theme>
    ...

3.5.2. Resource Request Filter

Because of JBoss Portal Platform's Right-To-Left support, all CSS files need to be retrieved through a Servlet filter and the web application needs to be configured to activate this filter. This is already done for the JPP_HOME/gatein/gatein.ear/eXoResources.war web application which contains the default skin.
Any new web applications containing skinning CSS files will need to have the following added to their web.xml :
<filter>
  <filter-name>ResourceRequestFilter</filter-name>
  <filter-class>org.exoplatform.portal.application.ResourceRequestFilter</filter-class>
  </filter>

  <filter-mapping>
  <filter-name>ResourceRequestFilter</filter-name>
  <url-pattern>*.css</url-pattern>
  </filter-mapping>

The display-name Element

The display-name element will also need to be specified in the web.xml for the skinning service to work properly with the web application.

3.6. The Default Skin

The default skin for JBoss Portal Platform is located as part of the JPP_HOME/gatein/gatein.ear/eXoResources.war. The main files associated with the skin are:
WEB-INF/gatein-resources.xml
For the default portal skin, this file contains definitions for the portal skin, the window decorations that this skin provides as well as defining some JavaScript resources which are not related to the skin. The default portal skin doesn't directly define portlet skins, these should be provided by the portlets themselves.
WEB-INF/web.xml
For the default portal skin, the web.xml of the eXoResources.war contains information which is mostly irrelevant to the portal skinning. The area of interest in this file is the resourcerequestfilter and the setting of the display-name parameter.
skin/Stylesheet.css
This file is the main portal skin stylesheet. It is the main entry point to the CSS class definitions for the skin. The main content points of this file are:
/* Skin for the main portal page */
@import url(DefaultSkin/portal/webui/component/UIPortalApplicationSkin.css);
/* Skins for various portal components */
@import url(DefaultSkin/webui/component/Stylesheet.css);
/* Window decoration skins */
@import url(PortletThemes/Stylesheet.css);
/* The portlet specification CSS classes */
@import url(Portlet/Stylesheet.css);
This method imports other CSS stylesheet files (some of which may also import further CSS stylesheets) instead of defining all the CSS classes in this one file. Splitting the CSS classes between multiple files allows new skins to reuse parts of the default skin.
To reuse a CSS stylesheet from the default portal skin you would need to reference the default skin from eXoResources. For example; to include the window decorators from the default skin within a new portal skin you would need to use this import:
@import url(/eXoResources/skin/Portlet/Stylesheet.css);

Stylesheet Merging

When the portal skin is added to the page, it merges all the CSS stylesheets into a single file.

3.7. Creating New Skins

3.7.1. Creating a New Portal Skin

New portal skins will need to be added to the portal through the skin service. Therefore, the web application which contains the skins will need to be properly configured for the skin service to discover them. This means properly configuring the ResourceRequestFilter and gatein-resources.xml.

3.7.1.1. Portal Skin Configuration

The gatein-resources.xml will need to specify the new portal skin. This will include the name of the new skin, where to locate its CSS stylesheet file and whether to overwrite an existing portal theme with the same name.
<gatein-resources>
  <portal-skin>
    <skin-name>MySkin</skin-name>
    <CSS-path>/skin/myskin.css</CSS-path>
    <overwrite>false</overwrite>
  </portal-skin>
</gatein-resources>
The default portal skin and window styles are defined in JPP_HOME/gatein/gatein.ear/eXoResources.war/WEB-INF/gatein-resources.xml.

CSS

The CSS for the portal skin needs to contain the CSS for all the window decorations and the portlet specification CSS classes.

3.7.1.2. Portal Skin Preview Icon

It is possible to see a preview of what the portal will look like when selecting a new skin. This functionality relies on the current skin being updated with skin icons for all other available skins. Otherwise it will not be able to show the previews.
It is recommended that preview icons of any other skins are included when creating a new portal skin and that the other skins are updated with your new portal skin preview.
Portal_Skin_Change

Figure 3.1. Portal_Skin_Change


The portal skin preview icon is specified through the CSS of the portal skin. In order for the current portal skin to be able to display the preview it must specify a specific CSS class and set the icon as the background.
For a portal named MySkin, it must define the following CSS class:
.UIChangeSkinForm .UIItemSelector .TemplateContainer .MySkinImage
In order for the default skin to display the skin icon for a new portal skin, the preview screen shot needs to be placed in: JPP_HOME/gatein/gatein.ear/eXoResources.war/skin/DefaultSkin/portal/webui/component/customization/UIChangeSkinForm/background.
The CSS stylesheet for the default portal needs to have the following updated with the preview icon CSS class. For a skin named MySkin, the following needs to be updated: JPP_HOME/gatein/gatein.ear/eXoResources.war/skin/DefaultSkin/portal/webui/component/customization/UIChangeSkinForm/Stylesheet.css.
.UIChangeSkinForm .UIItemSelector .TemplateContainer .MySkinImage {
  margin: auto;
  width: 329px; height:204px;
  background: url('background/MySkin.jpg') no-repeat top;
  cursor: pointer ;
}

3.7.2. Creating a New Window Style

Window styles are the CSS applied to window decorations. An administrator can decide which style of decoration should go around the window when they add a new application or gadget to a page.
Window Styles

Figure 3.2. Window Styles


3.7.2.1. Window Style Configuration

Window Styles are defined within a gatein-resources.xml file which is used by the skin service to deploy the window style into the portal. Window styles can belong in a window style category. This category and the window styles will need to be specified in resources file.
The following gatein-resources.xml fragment will add MyThemeBlue and MyThemeRed to the MyTheme category.
<window-style>
  <style-name>MyTheme</style-name>
  <style-theme>
    <theme-name>MyThemeBlue</theme-name>
  </style-theme>
  <style-theme>
    <theme-name>MyThemeRed</theme-name>
  </style-theme>
</window-style>
The windows style configuration for the default skin is configured in: JPP_HOME/gatein/gatein.ear/eXoResources.war/WEB-INF/gatein-resources.xml.

Window Styles and Portal Skins

When a window style is defined in gatein-resources.xml file, it will be available to all portlets regardless of whether the current portal skin supports the window decorator or not.
It is recommended that when a new window decorator is added that it be added to all portal skins or that all portal skins share a common stylesheet for window decorators.

3.7.2.2. Window Style CSS

In order for the skin service to display the window decorators, it must have CSS classes specifically named in relation to the window style name. The service will try and display CSS based on this naming convention. The CSS class must be included as part of the current portal skin for the window decorators to be displayed.
The location of the window decorator CSS classes for the default portal theme is located at: JPP_HOME/gatein/gatein.ear/eXoResources.war/skin/PortletThemes/Stylesheet.css.
Create the CSS file:
/*---- MyTheme ----*/
.MyTheme .WindowBarCenter .WindowPortletInfo {
    margin-right: 80px; /* orientation=lt */
    margin-left: 80px; /* orientation=rt */
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

.MyTheme .MiddleDecoratorCenter {
    background: #ffffff;
}

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

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

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

3.7.2.3. How to Set the Default Window Style

To set the default window style to be used for a portal you will need to specify the CSS classes for a theme called DefaultTheme.

Note

You do not need to specify the DefaultTheme in gatein-resources.xml.

3.7.3. How to Create New Portlet Skins

Portlets often require additional styles that may not be defined by the portal skin. JBoss Portal Platform allows portlets to define additional stylesheets for each portlet and will append the corresponding link tags to the head.
The link ID will be of the form {portletAppName}{PortletName}.
For example: ContentPortlet in content.war, will give id="contentContentPortlet".
To define a new CSS file to include whenever a portlet is available on a portal page, the following fragment needs to be added in gatein-resources.xml.
<portlet-skin>
  <application-name>portletAppName</application-name>
  <portlet-name>PortletName</portlet-name>
  <skin-name>Default</skin-name>
  <css-path>/skin/DefaultStylesheet.css</css-path>
</portlet-skin>

<portlet-skin>
  <application-name>portletAppName</application-name>
  <portlet-name>PortletName</portlet-name>
  <skin-name>OtherSkin</skin-name>
  <css-path>/skin/OtherSkinStylesheet.css</css-path>
</portlet-skin>
This will load the DefaultStylesheet.css when the Default skin is used and the OtherSkinStylesheet.css when the OtherSkin is used.

Updating Portlet Skins

If the current portal skin is not defined as one of the supported skins, then the portlet CSS class will not be loaded. It is recommended that portlet skins are updated whenever a new portal skin is created.

3.7.3.1. Define a Custom CSS File

JBoss Portal Platform 6 does not serve CSS files directly, but uses a filter as well as a skin service in order to:
This causes JBoss Portal Platform to create a non-functioning CSS link in html-src-code.

Procedure 3.1. To Resolve This:

  • Add the following files to the custom portlet application:

    WEB-INF/gatein-resources.xml:
    <portlet-skin>
            <application-name>custom</application-name>
            <portlet-name>test</portlet-name>
            <skin-name>Default</skin-name>
            <css-path>/css/main.css</css-path>
    </portlet-skin>
    

    Note:

    • The value of the application-name element needs to match the value of the display-name element in web.xml.
    • The value of the portlet-name element needs to match the value of the portlet-name element in JPP_HOME/standalone/configuration/gatein/portlet.xml.
    WEB-INF/web.xml:
    <display-name>custom</display-name>
    
    <filter>
        <filter-name>ResourceRequestFilter</filter-name>
        <filter-class>org.exoplatform.portal.application.ResourceRequestFilter</filter-class>
    </filter>
    
    <filter-mapping>
        <filter-name>ResourceRequestFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    Note:

    • The value of the display-name element needs to match the value of the application-name element in gatein-resources.xml.
    • The ResourceRequestFilter needs to be added to the custom portlet application for proper CSS file handling within the Portal container.
    WEB-INF/portlet.xml:
    <portlet-name>test</portlet-name>
    

    Note:

    The value of the portlet-name element needs to match the value of the portlet-name element in gatein-resources.xml.
    The final portlet application will be structured as illustrated below:
    custom.war
      ├── css
      │   └── main.css
      └── WEB-INF
          ├── classes
          │     [... custom portlet class ...]
          ├── gatein-resources.xml
          ├── portlet.xml
          └── web.xml
    

3.7.3.2. Change Portlet Icons

Each portlet can be registered by a unique icon in the portlet registry or page editor. This icon can be changed by adding an image to the directory of the portlet web application:
  • skin/DefaultSkin/portletIcons/portlet_name.png .
To be used correctly the icon must be named after the portlet.
For example; the icon for an account portlet named AccountPortlet would be located at:
  • skin/DefaultSkin/portletIcons/AccountPortlet.png

Portlet Icons Directory

You must use skin/DefaultSkin/portletIcons/ for the directory to store the portlet icon regardless of what skin is going to be used.

3.7.4. Create New Portlet Specification CSS Classes

The portlet specification defines a set of default CSS classes that should be available for portlets. These classes are included as part of the portal skin. Please see the portlet specification for a list of the default classes that should be available.
For the default portal skin, the portlet specification CSS classes are defined in: JPP_HOME/gatein/gatein.ear/eXoResources.war/skin/Portlet/Stylesheet.css.

3.8. Tips and Tricks

3.8.1. Easier CSS Debugging

By default, CSS files are cached and their imports are merged into a single CSS file at the server side. This reduces the number of HTTP requests from the browser to the server.
The optimization code is quite simple as all the CSS files are parsed at the server start and all the @import and url(...) references are rewritten to support a single flat file. The result is stored in a cache directly used from the ResourceRequestFilter.
Although the optimization is useful for a production environment, it may be easier to deactivate this optimization while debugging stylesheets. Set the Java system property exo.product.developing to true to disable the optimization.
For example, the property can be passed as a JVM parameter with -D option when running JBoss Portal Platform.
./bin/standalone.sh -Dexo.product.developing=true

Warning

This option may cause display bugs in some browsers.

3.8.2. Some CSS Techniques

It is recommended that users have some experience with CSS before studying JBoss Portal Platform CSS.
JBoss Portal Platform relies heavily on CSS to create the layout and effects for the UI. Some common techniques for customizing JBoss Portal Platform CSS are explained below.

3.8.2.1. Decorator Pattern

The decorator is a pattern to create a contour or a curve around an area. In order to achieve this effect you need to create nine cells. The BODY is the central area that you want to decorate. The other eight cells are distributed around the BODY cell. You can use the width, height and background image properties to achieve any decoration effect that you want.
Decorator Pattern

Figure 3.3. Decorator Pattern


<div class="Parent">
  <div class="TopLeft">
    <div class="TopRight">
      <div class="TopCenter"><span></span></div>
    </div>
  </div>
  <div class="CenterLeft">
    <div class="CenterRight">
      <div class="CenterCenter">BODY</div>
    </div>
  </div>
  <div class="BottomLeft">
    <div class="BottomRight">
      <div class="BottomCenter"><span></span></div>
    </div>
  <div>
</div>

3.8.2.2. Left Margin Left Pattern

Left margin left pattern is a technique to create two blocks side by side. The left block will have a fixed size and the right block will take the rest of the available space. When the user resizes the browser the added or removed space will be taken from the right block.
Left Margin Pattern

Figure 3.4. Left Margin Pattern


<div class="Parent">
  <div style="float: left; width: 100px">
  </div>
  <div style="margin-left: 105px;">
  <div>
  <div style="clear: left"><span></span></div>
</div>

Chapter 4. Portal Life-cycle

4.1. Overview

This chapter describes the portal life-cycle from the application server start to its stop including how requests are handled.

4.2. Application Server start and stop

A portal instance is simply a set of web applications deployed as EAR and WAR archives. Portlets are also part of an enhanced WAR called a portlet application.
JBoss Portal Platform does not require any particular setup for your portlet in most common scenarios and the web.xml file can remain without any JBoss Portal Platform specific configuration.
During deployment, JBoss Portal Platform will automatically and transparently inject a servlet into the portlet application to be able to interact with it. This feature is dependent on the underlying servlet container but will work out of the box on the proposed bundles.

4.2.1. Advanced WCI Registration

JBoss Portal Platform integrates with the web container to perform tasks such as automatic detection and registration of web applications. This is used by the portal container to detect when portlets are deployed and is accomplished through the WCI (Web Container Integration) component.
Some applications, especially Spring based portlets, may have requirements that specific servlets be started before any portlets are initialized. Although portlets and servlet initialization order are meant to be independent of each other, JBoss Portal Platform does have a way to get around these limitations imposed by these specific third party applications.
As a workaround to this issue, two new, advanced features have been integrated into the WCI component;

Advanced Features

Disabling Automatic registration
By default WCI will register all web applications and the portlet container will then analyse the registered applications and initialize any portlets contained. If you do not wish for your web application to be automatically registered by the WCI component you can disable this feature. By disabling this feature you can prevent the automatic initialization of the portlet and specify later when you want it to be initialized.
This is done by setting the gatein.wci.native.DisableRegistration context-param to true in the web.xml file of the application, as shown below:
<!-- Disable the Native Application Registration -->
   <context-param>
    <param-name>gatein.wci.native.DisableRegistration</param-name>
    <param-value>true</param-value>
  </context-param>
Manual application deployment.
If you have disabled the automatic registration of your application in the first step, the portal container will not know about any of the portlets contained and will not be able to initialize them. WCI does have a servlet which can be used to manually register the web application. Since servlets can specify when they are deployed with regards to other servlets, we can use this to specify that the web application gets registered by WCI after another servlet has already been started. This means that the a servlet, for example the Spring servlet, can be initialized before any of the portlets.
Below is an example web.xml file configured to ensure the MyCustomServlet will be initialized before the webapp is registered by WCI:
<!-- Disable the Native Application Registration -->
   <context-param>
    <param-name>gatein.wci.native.DisableRegistration</param-name>
    <param-value>true</param-value>
   </context-param>
<!-- Register the Web Application Manually -->
   <servlet>
    <servlet-name>GateInServlet</servlet-name>
    <servlet-class>org.gatein.wci.api.GateInServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
   </servlet>
<!-- Custom Servlet which will be initalised before the webapp is registered in WCI -->
  <servlet>
    <servlet-name>MyCustomServlet</servlet-name>
    <servlet-class>my.custom.Servlet</servlet-class>
    <load-on-startup>0</load-on-startup>
  </servlet>
<!-- Servlet Mapping for the Manual Registration -->
  <servlet-mapping>
    <servlet-name>GateInServlet</servlet-name>
    <url-pattern>/gateinservlet</url-pattern>
  </servlet-mapping>

4.3. The Command Servlet

The CommandServlet is called by the portlet container for requests to particular portlets, it also includes some init code when the portal is launched. This servlet (org.gatein.wci.api.GateInServlet) is automatically added during the deployment of each portlet application and mapped to /gateinservlet.
This is equivalent to adding the following into web.xml.

Servlet Configuration

As the servlet is already configured this example is for information only.
<servlet>
  <servlet-name>GateInServlet</servlet-name>
  <servlet-class>org.gatein.wci.api.GateInServlet</servlet-class>
  <load-on-startup>0</load-on-startup>
</servlet>
  
<servlet-mapping>
  <servlet-name>GateInServlet</servlet-name>
  <url-pattern>/gateinservlet</url-pattern>
</servlet-mapping>
It is possible to filter on the GateInServlet 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 is the file on which we want to know the time to serve a portlet request.
As mentioned above nothing specific to JBoss Portal Platform 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>/gateinservlet</url-pattern>
    <dispatcher>INCLUDE</dispatcher>  
  </filter-mapping>    
    
</web-app>

INCLUDE dispatcher

It is important to set INCLUDE as dispatcher as the portal will always hit the GateInServlet through a request dispatcher. Without this, the filter will not be triggered, unless direct access to a resource (such as an image) is set.

Chapter 5. Default Portal Configuration

5.1. Overview

JBoss Portal Platform's default home page URL is http://{hostname}:{port}/portal/. There may be multiple independent portal containers deployed in parallel at any given time, each of which has its root context (http://{hostname}:{port}/sample-portal/, for example).
Each portal container is internally composed of one or more 'portals'. This is because there needs to be at least one such portal available. The default portal is called 'Classic'. When accessing JBoss Portal Platform's default URL, you are automatically directed to the 'Classic' portal.
The default portal performs another important task. When starting up JBoss Portal Platform for the first time, its JCR database (where portal runtime-configurable settings are stored) will be empty . The default portal detects this and triggers automatic data initialization.

5.2. Configuration

The following example configuration can be found at: JPP_HOME/gatein/gatein.ear/portal.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> 
         ...
       </init-params>
     </component-plugin>
    </component-plugins> 
  </component>
In this example the Classic portal has been set as the default.
Notice that the NewPortalConfigListener component-plugin is used to add configuration to UserPortalConfigService, which is designed in this way to allow other components to add configuration to it.
Components, component-plugins, and init-params are explained in Chapter 30, The eXo Kernel.

5.3. Delete Portal Definitions using Component Plug-ins

In some cases, portal definitions and portal template definitions are defined but not used any more. To delete them, it is possible to add a component plug-in to JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/portal/portal-configuration.xml which detects these definitions and cleans up the portal-configuration.xml file automatically.

Example 5.1. Portal Definition Clean-up Directives

<external-component-plugins>
  <target-component>org.exoplatform.portal.config.UserPortalConfigService</target-component>
  <component-plugin>
    <name>new.portal.config.user.listener</name>
    <set-method>deleteListenerElements</set-method>
    <type>org.exoplatform.portal.config.NewPortalConfigListener</type>
    <description>this listener delete some predefined portal and templates configuration</description>
    <init-params>
      <object-param>
        <name>site.templates.location</name>
        <description>description</description>
        <object type="org.exoplatform.portal.config.SiteConfigTemplates">
          <field name="portalTemplates">
            <collection type="java.util.HashSet">
              <value>
                <string>basic</string>
              </value>
              <value>
                <string>classic</string>
              </value>
            </collection>
          </field>
        </object>
      </object-param>
      <object-param>
        <name>portal.configuration</name>
        <description>description</description>
        <object type="org.exoplatform.portal.config.NewPortalConfig">
          <field  name="predefinedOwner">
            <collection type="java.util.HashSet">
              <value><string>classic</string></value>
            </collection>
          </field>
          <field name="ownerType"><string>portal</string></field>
        </object>
      </object-param>
    </init-params>
  </component-plugin>
</external-component-plugins>

5.4. Set the Info Bar Behavior

You can set the info bar shown by default for portlets of a portal by adding an <entry> directive to the <properties> configuration of the JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/portal/portal/classic/portal.xml file.
<properties>
  <entry key="showPortletInfo">1</entry>
</properties>
There are two values for "showPortletInfo": 0 and 1. If the value is 1, the info bar of portlets is shown by default. If the value is 0, it is hidden.

5.5.  Disabling a Portal Container

Once you have created a custom portal container that suits your needs, you may wish to disable a portal container that is no longer required.

Task: Disable a portal in JBoss Portal Platform 6

Disable an unused portal container in a JBoss Portal Platform instance.

Prerequisites:

  • Nil.

Procedure 5.1. 

  1. Add the following configuration to the configuration.xml of the custom extension in order to disable a portal container:
    <!-- Comment 1 -->
    <?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_1.xsd"
        xmlns="http://www.exoplaform.org/xml/ns/kernel_1_1.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>
                <!-- (existing configuration for new portal container) -->
            </component-plugin>
            <component-plugin>
                <!-- The name of the plugin -->
                <name>Disable a PortalContainer</name>
                <!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
                <set-method>registerDisablePlugin</set-method>
                <!-- The full qualified name of the PortalContainerDefinitionDisablePlugin -->
                <type>org.exoplatform.container.definition.PortalContainerDefinitionDisablePlugin</type>
                <init-params>
                    <!-- The list of the name of the portal containers to disable -->
                    <values-param>
                        <name>names</name>
                        <value>$PORTAL_NAME</value>
                    </values-param>
                </init-params>
            </component-plugin>                
        </external-component-plugins>
    </configuration>
  2. The portal name declared in the <values-param> directive will no longer be available at http://{hostname}:{port}/portal.

Note

Disabling the default portal container is possible, but not recommended. Some functions, such as WSRP or Services Management, depend on the default portal container to be deployed. These functions will no longer work if the default portal container is disabled.

Chapter 6. Portal Default Permission Configuration

6.1. Overview

The default permission configuration for the portal is defined through the org.exoplatform.portal.config.UserACL component configuration in the JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/portal/portal-configuration.xml file.
It defines eight permissions types:
super.user
The super user has all the rights on the platform, this user is referred to as root.
portal.administrator.groups
Any member of those groups are considered administrators. Default value is /platform/administrators.
portal.administrator.mstype
Any user with that membership type would be considered administrator or the associated group. Default value is manager.
portal.creator.groups
This list defines all groups that will be able to manage the different portals. Members of this group also have the permission to create new portals. The format is membership:/group/subgroup.
navigation.creator.membership.type
Defines the membership type of group managers. The group managers have the permission to create and edit group pages and they can modify the group navigation.
guests.group
Any anonymous user automatically becomes a member of this group when they enter the public pages.
mandatory.groups
Groups that can't be deleted.
mandatory.mstypes
Membership types that can't be deleted.
<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>     
    <values-param>
        <name>mandatory.groups</name>
        <description>Groups that can not be deleted.</description>
        <value>/platform/administrators</value>
        <value>/platform/users</value>
        <value>/platform/guests</value>
    </values-param> 
    <values-param>
        <name>mandatory.mstypes</name>
        <description>Membership type that can not be deleted.</description>
        <value>member</value>           
    </values-param>         
  </init-params>   
</component>

6.2. Overwrite Portal Default Permissions

When creating custom portals and portal extensions it is possible to override the default configuration by using org.exoplatform.portal.config.PortalACLPlugin, configuring it as an external-plugin of org.exoplatform.portal.config.UserACL service:
<external-component-plugins>
   <target-component>org.exoplatform.portal.config.UserACL</target-component>
    <component-plugin>
      <name>addPortalACLPlugin</name>
      <set-method>addPortalACLPlugin</set-method>
      <type>org.exoplatform.portal.config.PortalACLPlugin</type>
      <description>setting some permission for portal</description>
      <init-params>
        <values-param>
          <name>super.user</name>
          <value>root</value>
        </values-param>
        <values-param>
          <name>portal.creation.roles</name>
          <value>*:/platform/administrators</value>
          <value>*:/organization/management/executive-board</value>
        </values-param>
      </init-params>
    </component-plugin>
</external-component-plugins> 

Chapter 7. Portal Navigation Configuration

7.1. Overview

There are three types of navigation available to portal users:
These navigators are configured using the standard XML syntax in the file: JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/portal/portal-configuration.xml.
<component>
  <key>org.exoplatform.portal.config.UserPortalConfigService</key>
  <type>org.exoplatform.portal.config.UserPortalConfigService</type>
  <component-plugins>           
    <component-plugin>
    <name>new.portal.config.user.listener</name>
    <set-method>initListener</set-method>
    <type>org.exoplatform.portal.config.NewPortalConfigListener</type>
    <description>this listener init the portal configuration</description>
      <init-params>
        <value-param>
          <name>default.portal</name>
          <description>The default portal for checking db is empty or not</description>
          <value>classic</value>
        </value-param> 
        <value-param>
          <name>page.templates.location</name>
          <description>the path to the location that contains Page templates</description>
          <value>war:/conf/portal/template/pages</value>
        </value-param>
        <object-param>
          <name>site.templates.location</name>
          <description>description</description>
          <object type="org.exoplatform.portal.config.SiteConfigTemplate">
            <field name="location">
              <string>war:/conf/portal</string>
            </field>
            <field name="portalTemplates">
              <collection type="java.util.HashSet">
                <value>
                  <string>classic</string>
                </value>
              </collection>
            </field>
            <field name="groupTemplates">
              <collection type="java.util.HashSet">
                <value>
                  <string>group</string>
                </value>
              </collection>
            </field>
            <field name="userTemplates">
              <collection type="java.util.HashSet">
                <value>
                  <string>user</string>
                </value>
              </collection>
            </field>
          </object>
        </object-param>
        <object-param>
          <name>portal.configuration</name>
          <description>description</description>
          <object type="org.exoplatform.portal.config.NewPortalConfig">
            <field  name="predefinedOwner">
              <collection type="java.util.HashSet">                
                <value><string>classic</string></value>	               
              </collection>
            </field>
            <field name="ownerType"><string>portal</string></field>
            <field name="templateLocation"><string>war:/conf/portal/</string></field>
          </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>
                <value><string>user</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>  
</component>
This XML configuration defines where in the portal's WAR to look for configuration settings, and which portals, groups, and user specific views to include in portal/group/user navigation.
The first time the portal is launched those files will be used to create an initial navigation. That information will then be stored in the JCR content repository and can be modified and managed from the portal UI.
Each portal, groups and users navigation is indicated by a configuration paragraph, for example:

Example 7.1. Navigation Configuration Paragraph Example

<object-param>
   <name>portal.configuration</name>
   <description>description</description>
   <object type="org.exoplatform.portal.config.NewPortalConfig">
      <field name="predefinedOwner">
         <collection type="java.util.HashSet">
            <value><string>classic</string></value>
         </collection>
      </field>
      <field name="ownerType">
         <string>portal</string>
      </field>
      <field name="templateLocation">
         <string>war:/conf/portal/</string>
      </field>
      <field name="importMode">
         <string>conserve</string>
      </field>
   </object>
</object-param>
predefinedOwner defines the navigation owner, portal will look for the configuration files in folder with this name, if there is no suitable folder, a default portal will be created with name is this value.
ownerType defines the type of portal navigation. It may be a portal, group, or user.
templateLocation defines the classpath to all portal configuration files
importMode is the mode for navigation import. There are 4 types of import mode:
  • conserve: Import data when it does not exist, otherwise do nothing.
  • insert: Import data when it does not exist, otherwise performs a strategy that adds new data only.
  • merge: Import data when it does not exist, update data when it exists.
  • rewrite: Overwrite data whatsoever.

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

7.2. Portal Navigation

The portal navigation incorporates the pages that can be accessed even when a user is not logged in (assuming the applicable permissions allow public access). For example; several portal navigations could be used when a company has multiple trademarks, and websites are set up for each of them.
The Classic portal is configured by three XML files in the JPP_HOME/gatein/gatein.ear/portal.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. JBoss Portal Platform is extremely configurable as every view element (even the banner and footer) is a portlet.
<?xml version="1.0" encoding="ISO-8859-1"?>
<portal-config
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_2 http://www.gatein.org/xml/ns/gatein_objects_1_2"
    xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_2">
  <portal-name>classic</portal-name>
  <locale>en</locale>
  <access-permissions>Everyone</access-permissions>
  <edit-permission>*:/platform/administrators</edit-permission>
  <skin>NewSkin</skin>
  <properties>
    <entry key="sessionAlive">onDemand</entry>
  </properties>
    
 	<portal-layout>
	  <portlet-application>
      <portlet>
        <application-ref>web</application-ref>
        <portlet-ref>BannerPortlet</portlet-ref>
        <preferences>
          <preference>
            <name>template</name>
            <value>par:/groovy/groovy/webui/component/UIBannerPortlet.gtmpl</value>
            <read-only>false</read-only>
          </preference>
        </preferences>
      </portlet>
      <access-permissions>Everyone</access-permissions>
	    <show-info-bar>false</show-info-bar>
	  </portlet-application>

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

  </portal-layout>
  
</portal-config>
It is also possible to apply a nested container that can also contain portlets. Row, column or tab containers are then responsible for the layout of their child portlets.
Each application references a portlet using the id portal#{portalName}:/{portletWarName}/{portletName}/{uniqueId}.
Use the page-body tag to define where JBoss Portal Platform should render the current page.
The defined classic portal is accessible to "Everyone" (at /portal/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 and uses nested node tags. Each node references a page defined in pages.xml file.
To create node labels for each language, use the label tag with the xml:lang attribute with the value of the relevant locale.
Otherwise, to have the node label localized by resource bundle files, use the #{...} syntax: the enclosed property name serves as a key that is automatically passed to internationalization mechanism which replaces the literal property name with a localized value from the associated properties file matching the current locale.
<?xml version="1.0" encoding="UTF-8"?>
<node-navigation
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_2 http://www.gatein.org/xml/ns/gatein_objects_1_2"
    xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_2">
  <priority>1</priority>
  <page-nodes>
     <node>
        <uri>home</uri>
        <name>home</name>
        <label>#{portal.classic.home}</label>
        <page-reference>portal::classic::homepage</page-reference>      
     </node>   
     <node>
      <name>sitemap</name>
        <label xml:lang="en">SiteMap</label>
        <label xml:lang="fr">SiteMap</label>
        <label xml:lang="es">Mapa del Sitio</label>
        <label xml:lang="de">Seitenübersicht</label>
        <label xml:lang="it">Mappa del Sito</label>
        <label xml:lang="nl">Sitemap</label>
        <label xml:lang="pt-BR">Mapa do Site</label>
        <label xml:lang="ja">サイトマップ</label>
        <label xml:lang="ne">साईटम्याप</label>
        <label xml:lang="ru">SiteMap</label>
        <label xml:lang="ar">خريطة الموقع</label>
        <label xml:lang="ko">사이트맵</label>
        <label xml:lang="vi">Sơ đồ</label>
        <label xml:lang="zh">网站地图</label>
        <label xml:lang="zh-TW">網站導覽</label>
        <visibility>DISPLAYED</visibility>
        <page-reference>portal::classic::sitemap</page-reference>
     </node>
  </page-nodes>
</node-navigation>
This navigation tree can have multiple views inside portlets (such as the breadcrumbs portlet) that render the current view node, the site map or the menu portlets.

Warning

For top nodes, the uri and the name of your navigation nodes must have the same value. For other nodes the uri is a relative path.
For example; contentmanagement/fileexplorer where 'contentmanagement ' is the name of the parent node and 'fileexplorer' is the name of the node (<name>fileexplorer</name> ).
pages.xml
This configuration file structure is very similar to portal.xml and it can also contain container tags (some usage examples of container tags can be found in JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/portal/portal/sharedlayout.xml).
Each application can decide whether to render the portlet border, the window state, the icons, or the portlet mode.

7.3. Group Navigation

Group navigations are dynamically added to the user navigation at login. This allows users to see the pages assigned to any groups they belong to in the menu.
The group navigation menu is configured by two XML files (navigation.xml and pages.xml). The syntax used in these files is the same as those covered in Section 7.2, “Portal Navigation”.
They are located in JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/portal/group/group-name-path/ directory (For example; portal.war/WEB-INF/conf/portal/group/platform/administrators/).

7.4. User Navigation

User navigation is the set of nodes and pages that are owned by a user. They are part of the user's dashboard.
Two files configure the user navigation (navigation.xml and pages.xml). They are located in the directoryJPP_HOME/gatein.ear/portal.war/WEB-INF/conf/portal/user/{userName}.
The example below shows a dashboard with all of the default gadgets included, as well as an extra currency converter gadget sourced from Google Gadgets.
<?xml version="1.0" encoding="ISO-8859-1"?>
<gadgets
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_2 http://www.gatein.org/xml/ns/gadgets_1_0"
    xmlns="http://www.gatein.org/xml/ns/gadgets_1_0">

  <gadget name="Todo">
    <path>/gadgets/Todo/Todo.xml</path>
  </gadget>
    
  <gadget name="Calendar">
    <path>/gadgets/Calendar/Calendar.xml</path>
  </gadget>
    
  <gadget name="Calculator">
    <path>/gadgets/Calculator/Calculator.xml</path>
  </gadget>
    
  <gadget name="rssAggregator">
    <path>/gadgets/rssAggregator/rssAggregator.xml</path>
  </gadget>

  <gadget name="Currency">
    <url>http://www.donalobrien.net/apps/google/currency.xml</url>
  </gadget>
  
  <gadget name="ServiceMangement">
    <path>/gadgets/ServiceManagement/ServiceManagement.xml</path>
  </gadget>
</gadgets>

Chapter 8. Data Import Strategy

8.1. Introduction

In the Portal extension mechanism, developers can define an extension that Portal data can be customized by. There are several cases when it is useful to be able to define how to customize the Portal data; for example modifying, overwriting or just inserting data into the data defined by the Portal. Therefore, JBoss Portal Platform also defines several modes for each case so that the developer only need to clarify the use-case and configure the extensions.
This section shows you how data are changes in each mode.

8.2. Data Import Strategy

The 'Portal Data' term referred to in this section can be classified into three types of object data:
  • Portal Configuration
  • Page Data
  • Navigation Data
Each type of object data has some differences in the import strategy.
The modes for the import strategy include the following:
  • CONSERVE
  • MERGE
  • INSERT
  • OVERWRITE
Each mode indicates how the portal data is imported. The import mode value is set whenever NewPortalConfigListener is initiated. If the mode is not set, the default value will be used. The default value is configurable as a UserPortalConfigService initial parameter. For example, the configuration below means that the default value is MERGE.
<component>
  <key>org.exoplatform.portal.config.UserPortalConfigService</key>
  <type>org.exoplatform.portal.config.UserPortalConfigService</type>
  <component-plugins>
  ............
  </component-plugins>
  <init-params>
    <value-param>
      <name>default.import.mode</name>
      <value>merge</value>
    </value-param>
  </init-params>
</component>

The way the import strategy works with the import mode will be clearly demonstrated in next sections for each type of data.

8.2.1. Portal Configuration

PortalConfig defines the portal name, permission, layout and some properties of a site. These information are configured in the portal.xml, group.xml or user.xml, depending on the site type. The PortalConfig importer performs a strategy that is based on the mode defined in NewPortalConfigListener, including CONSERVE, INSERT, MERGE or OVERWRITE. Let's see how the import mode affects the process of portal data performance:
  • CONSERVE: There is nothing to be imported. The existing data will be kept without any changes.
  • INSERT: When the portal configuration does not exist, create the new portal defined by the portal configuration definition. Otherwise, do nothing.
  • MERGE and OVERWRITE have the same behavior. The new portal configuration will be created if it does not exist or update portal properties defined by the portal configuration definition.

8.2.2. Page Data

The import mode affects the page data import as the same as PortalConfig.

Note

If the Import mode is CONSERVE or INSERT, the data import strategy acts as if in the MERGE mode in the first data initialization of the Portal.

8.2.3. Navigation Data

The navigation data import strategy is processed at the import mode level as follows:
  • CONSERVE: If the navigation exists, leave it untouched. Otherwise, import data.
  • INSERT: Insert the missing description data, but add only new nodes. Other modifications remain untouched.
  • MERGE: Merge the description data, add missing nodes and update existing nodes.
  • OVERWRITE: Always destroy the previous data and recreate it.
In the JBoss Portal Platform navigation structure, each navigation can be referred to a tree which each node links to a page content. Each node contains some description data, such as label, icon, page reference, and more. Therefore, JBoss Portal Platform provides a way to insert or merge new data to the initiated navigation tree or a sub-tree.
The merge strategy performs the recursive comparison of child nodes between the existing persistent nodes of a navigation and the transient nodes provided by a descriptor:
  1. Start with the root nodes (which is the effective root node or another node if the parent URI is specified).
  2. Compare the set of child nodes and insert the missing nodes in the persistent nodes.
  3. Proceed recursively for each child having the same name.
Let's see the example with two navigation nodes in each import mode. In this case, there are two navigation definitions:
<node-navigation>
  <page-nodes>
    <node>
      <name>foo</name>
      <icon>foo_icon_1</icon>
      <node>
        <name>juu</name>
        <icon>juu_icon</icon>
      </node>
    </node>
    <node>
      <name>daa</name>
      <icon>daa_icon</icon>
    </node>
  </page-nodes>
</node-navigation>
Data Import Strategy Navigation1

Figure 8.1. Data Import Strategy Navigation1


<node-navigation>
  <page-nodes>
    <node>
      <name>foo</name>
      <icon>foo_icon_2</icon>
    </node>
    <node>
      <name>daa</name>
      <icon>daa_icon</icon>
    </node>
  </page-nodes>
</node-navigation>
Data Import Strategy Navigation2

Figure 8.2. Data Import Strategy Navigation2


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

Import Mode Cases

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

Figure 8.3. Data Import Strategy Navigation1


Case 2: INSERT
If a node does not exist, the importer will add new nodes to the navigation tree. You will see the following result:
Data Import Strategy Navigation_Insert

Figure 8.4. Data Import Strategy Navigation_Insert


Hereafter, the node 'bar' is added to the navigation tree, because it does not exist in the initiated data. Other nodes are kept in the import process.
Case 3: MERGE
The MERGE mode indicates that a new node is added to the navigation tree, and updates the node data (such node label and node icon in the example) if it exists.
Data Import Strategy Navigation_Merge

Figure 8.5. Data Import Strategy Navigation_Merge


Case 4: OVERWRITE
Everything will be destroyed and replaced with new data if the OVERWRITE mode is used.
Data Import Strategy Navigation_2

Figure 8.6. Data Import Strategy Navigation_2


Chapter 9.  Internationalization Configuration

9.1. Overview

Assumed Knowledge

JBoss Portal Platform is fully configurable for internationalization, however users should have a general knowledge of Internationalization in Java products before attempting these configurations.
Oracle hosts a comprehensive guide to internationalizing Java products at http://docs.oracle.com/javase/tutorial/i18n/TOC.html.
All JBoss Portal Platform 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 sub-folder 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; in the Spanish file:
javax.portlet.title=Portlet de navegaci\u00f3n
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 to be ISO 8859-1 encoded, while the XML file can define its encoding. As a result it's easier for a human being to read a translation in XML instead of having to decode and encode the property file.
For more information refer to: Chapter 11, XML Resources Bundles

9.2. Locales Configuration

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 JPP_HOME/gatein/gatein.ear/portal.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 (portal.war:/WEB-INF/conf/common/locales-config.xml) contains the following code:

Example 9.1. The locales-config.xml File Explanation

<?xml version="1.0" encoding="UTF-8"?>
<locales-config>
  <locale-config>
    <locale>en</locale>
    <output-encoding>UTF-8</output-encoding>
    <input-encoding>UTF-8</input-encoding>
    <description>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>
    <orientation>rt</orientation>
  </locale-config>
</locales-config>
locale: The locale has to be defined as per the codes defined here. In this example "ar" is Arabic.
output-encoding: deals with character encoding. It is recommended that UTF-8 be used.
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(..).
description: A description of the language.
orientation: Although the default orientation of text and images is Left-To-Right, JBoss Portal Platform supports Right-To-Left orientation. Modifying text orientation is explained in Chapter 12, Right To Left (RTL) Framework.

9.3. ResourceBundleService

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

Example 9.2. The common-configuration.xml file explained

<component>
  <key>org.exoplatform.services.resources.ResourceBundleService</key>
  <type>org.exoplatform.services.resources.impl.SimpleResourceBundleService</type>
  <init-params>
    <!-- Comment #1 -->
    <values-param>
      <name>classpath.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>
    <!--Comment #2 -->      
    <values-param>
      <name>init.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>      
    <!-- Comment #3 -->
    <values-param>
      <name>portal.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>
Comment #1: resources whose package name starts with the name specified in the classpath.resources parameter are loaded from the file system. Detailed information can be found in a later section of this chapter.
Comment #2: resources related to portal, group, and user reference bundles
Comment #3: the portal.resource.names parameter 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 JBoss Portal Platform. All these keys are located in the same bundle, which is separated from the navigation resource bundles.

9.4. Navigation Resource Bundles

There is a resource bundle for each navigation. A navigation can exist for user, groups, and portal.
Example 9.2, “The common-configuration.xml file explained” shows bundle definitions for the navigation of the classic portal and of four different groups. Each of these resource bundles occupies a different sphere, they are independent of each other and they are not included in the portal.resource.names parameter.
The properties for a group must be in the JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/classes/locale/navigation/group/ folder. For example, JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/classes/locale/navigation/group/organization/management/executive-board_en.properties.
The folder and file names must correspond to the group hierarchy. The group name "executive-board" is followed by the ISO 639 code.
Each language defined in LocalesConfig must have a resource file defined. If the name of a group is changed the name of the folder and/or files of the correspondent navigation resource bundles must also be changed.
Content of executive-board_en.properties:
organization.title=Organization
organization.newstaff=New Staff
organization.management=Management
This resource bundle is only accessible for the navigation of the organization.management.executive-board group.

9.5. Portlets

Portlets are independent applications and deliver their own resource files.
All shipped portlet resources are located in the locale/portlet sub-folder. The ResourceBundleService parameter classpath.resources defines this sub-folder.

Procedure 9.1. Add Spanish Translation to the GadgetPortlet

  1. Create the file GadgetPortlet_es.properties in JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/classes/locale/portlet/gadget/GadgetPortlet.
  2. In portlet.xml, add Spanish as a supported-locale ('es' is the two letter code for Spanish). The resource-bundle is already declared and is the same for all languages :
    <supported-locale>en</supported-locale>
    <supported-locale>es</supported-locale>
    <resource-bundle>locale.portlet.gadget.GadgetPortlet</resource-bundle>
See the portlet specification for more details about portlet internationalization.

9.5.1. Standard Portlet Resource Keys

The portlet specification defines three standard keys: Title, Short Title and Keywords. Keywords is formatted as a comma-separated list of tags.
javax.portlet.title=Breadcrumbs Portlet
 javax.portlet.short.title=Breadcrumbs
 javax.portlet.keywords=Breadcrumbs, Breadcrumb

9.5.2. Debugging Resource Bundle Usage

When translating an application it can sometimes be difficult to find the right key for a given property.
You can start the Portal in debug mode and use the Magic locale from the list of available portal languages to assist in finding the correct translated key value.
This special locale translates a key to the same value. For example, the translated value for the "organization.title" key is the value "organization.title". Selecting Magic locale allows use of the portal and its applications with all the keys visible. This makes it easier to discover the correct key for a given label in the portal page.

Procedure 9.2. Accessing the Magic Locale

  1. Start the portal in debug mode by executing the following command-line argument:
    [USER@HOST jboss-jpp-6.0]$ ./bin/standalone.sh -Dexo.product.developing=true
  2. Open http://localhost:8080/portal/classic to display the Portal Platform landing page.
  3. Click Change Language.
  4. Select ma from the list of available languages to activate the Magic locale.

9.5.3. Translate the Language Selection Form

Language Selection Screen
Image displaying the available languages in the portal, with French selected.

Figure 9.1. Language Selection Screen


When choosing a language from the Language Select screen, the user is presented with a list of languages on the left side in the current chosen language. On the right side, the same language is translated into its own language.
The local language values are obtained from the JDK API java.util.Locale.getDisplayedLanguage() and java.util.Locale.getDisplayedCountry() (if needed). Not all languages may be translated, and the languages available can also depend on the JVM currently used.
It is possible to override these values by editing the locale.portal.webui resource bundle.

Procedure 9.3. Overriding Default JDK API Language Values

  1. Edit the JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/classes/locale/portal/webui_xx_yy.properties file where xx_yy represents the country code of the language you wish to translate.
  2. In that file, add or modify a key such as Locale.xx_yy with the xx_yy value being the translated string.
  3. Edit JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/classes/locale/portal/webui_fr.properties where fr is the country code for French, and add the following key into it:
    Locale.zh_TW=Chinois traditionnel
When the portal is next restarted, the localized language will be updated in the user interface when a user tries to change the current language.

Chapter 10. Localization Configuration

10.1. Pluggable Locale Policy

Every request processed by every portlet is invoked within a context of the current Locale.
The current Locale can be retrieved by calling the getLocale() method of javax.portlet.PortletRequest interface.
The exact algorithm for determining the current Locale is not specified by Portlet Specification. Portlet containers implement this the way they deem most appropriate.
In JBoss Portal Platform, each portal instance has a default language which can be used to present content for new users. Another option is to use each user’s browser language preference, provided it matches one of the available localizations that JBoss Portal Platform supports, and only fallback to the portal's default language if no match is found.
Every user, while visiting a portal, has an option to change the language of the user interface by using a Language chooser. The choice can be remembered for the duration of the session, or it can be remembered for a longer period using a browser cookie, or, for registered and logged-in users, it can be saved into the user’s profile.
As there is more than one way to determine the Locale to be used for displaying a portal page, the mechanism for determining the current Locale of the request is pluggable in JBoss Portal Platform, and the exact algorithm can be customized.

10.1.1. LocalePolicy API

Customization is achieved by using LocalePolicy API, which is a simple API consisting of one interface, and one class:
  • org.exoplatform.services.resources.LocalePolicy interface
  • org.exoplatform.services.resources.LocaleContextInfo class
The LocalePolicy interface defines a single method that is invoked on the installed LocalePolicy service implementation:
public interface LocalePolicy
   {
      public Locale determineLocale(LocaleContextInfo localeContext);
   }
The Locale returned by determineLocale() method is the Locale that will be returned to portlets when they call javax.portlet.PortletRequest.getLocale() method.
The returned Locale has to be one of the locales supported by portal, otherwise it will fall back to the portal default Locale.
The supported locales are listed in JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/common/locales-config.xml file as described in Section 9.2, “Locales Configuration” .
The determineLocale() method takes a parameter of type LocaleContextInfo, which represents a compilation of preferred locales from different sources; user’s profile, portal default, browser language settings, current session, browser cookie.
All these different sources of Locale configuration or preference are used as input to LocalePolicy implementation that decides which Locale should be used.

10.1.2. Default LocalePolicy

By default, org.exoplatform.portal.application.localization.DefaultLocalePolicyService, an implementation of LocalePolicy, is installed to provide the default behavior. This, however, can easily be extended and overridden. A completely new implementation can also be written from scratch.
DefaultLocalePolicyService treats logged-in users slightly differently than anonymous users. Logged-in users have a profile that can contain language preference, while anonymous users do not.
Here is an algorithm used for anonymous users.

Procedure 10.1. An algorithm for anonymous users

  1. Iterate over LocaleContextInfo properties in the following order:
    • cookieLocales
    • sessionLocale
    • browserLocales
    • portalLocale
  2. Get each property's value. If it's a collection, get the first value.
  3. If value is one of the supported locales return it as a result.
  4. If the value is not in the supported locales set, try to remove country information and check if a language matching locale is in the list of supported locales. If so, return it as a result.
  5. Otherwise, continue with the next property.
If no supported locale is found the return locale eventually defaults to portalLocale.
The algorithm for logged-in users is virtually the same except that the first Locale source checked is user's profile.

Procedure 10.2. An algorithm for logged-in users

  1. Iterate over LocaleContextInfo properties in the following order:
    • userProfile
    • cookieLocales
    • sessionLocale
    • browserLocales
    • portalLocale

10.1.3. Custom LocalePolicy

The easiest way to customize the LocalePolicy is to extend DefaultLocalePolicyService. A study of the source code is required. JavaDocs provide thorough information on this.
Most customizations will involve simply overriding one or more of its protected methods.
An example of a customization is an already provided NoBrowserLocalePolicyService. By overriding just one method, it skips any use of browser language preference.
public class NoBrowserLocalePolicyService extends DefaultLocalePolicyService
   {
      /**
       * Override super method with no-op.
       *
       * @param context locale context info available to implementations in order to determine appropriate Locale
       * @return null
       */
      @Override
      protected Locale getLocaleConfigFromBrowser(LocaleContextInfo context)
      {
         return null;
      }
   }

10.1.4. LocalePolicy Configuration

The LocalePolicy framework is enabled for portlets by configuring LocalizationLifecycle class in portal's webui configuration file: JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/webui-configuration.xml:
<application-life-cycle-listeners>
   ...
   <listener>org.exoplatform.portal.application.localization.LocalizationLifecycle</listener>
</application-life-cycle-listeners>
The default LocalePolicy implementation is installed as an eXo Kernel portal service via JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/portal/web-configuration.xml.
The following excerpt is responsible for installing the service:
<component>
    <key>org.exoplatform.services.resources.LocalePolicy</key>
    <type>org.exoplatform.portal.application.localization.DefaultLocalePolicyService</type>
</component>
Besides implementing LocalePolicy, the service class also needs to implement org.picocontainer.Startable interface in order to get installed.

10.1.5. Keeping non-bridged resources in sync with current Locale

All the resources in portals that are not portlets themselves, but are accessed through portlets - reading data through PortletRequest, and writing to PortletResponse - are referred to as 'bridged'. Any resources that are accessed directly, bypassing portal filters and servlets, are referred to as non-bridged.
Non-bridged servlets, and .jsps have no access to PortalRequest. They do not use PortletRequest.getLocale() to determine current Locale. Instead, they use ServletRequest.getLocale() which is subject to precise semantics defined by Servlet specification - it reflects browser's language preference.
In other words, non-bridged resources do not have a notion of current Locale in the same sense that portlets do. The result is that when mixing portlets and non-bridged resources there may be a localization mismatch, an inconsistency in the language used by different resources composing your portal page.
This problem is addressed by LocalizationFilter. This is a filter that changes the behavior of ServletRequest.getLocale() method so that it behaves the same way as PortletRequest.getLocale().
That way even localization of servlets, and .jsps accessed in a non-bridged manner can stay in sync with portlet localization.
LocalizationFilter is installed through the portal's web.xml file: JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/web.xml.
<filter>
   <filter-name>LocalizationFilter</filter-name>
   <filter-class>org.exoplatform.portal.application.localization.LocalizationFilter</filter-class>
</filter>

...

<filter-mapping>
    <filter-name>LocalizationFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>ERROR</dispatcher>
</filter-mapping>
There is a minor limitation with this mechanism in that it is unable to determine the current portal,and consequently, its default language. As a result the portalLocale defaults to English, but can be configured to something else by using the filter's PortalLocale init param. For example:
<filter>
   <filter-name>LocalizationFilter</filter-name>
   <filter-class>org.exoplatform.portal.application.localization.LocalizationFilter</filter-class>
   <init-param>
      <param-name>PortalLocale</param-name>
      <param-value>fr_FR</param-value>
   </init-param>
</filter>
By default, LocalizationFilter is applied very broadly to cover all the resources automatically.
JBoss Portal Platform uses some non-bridged .jsps that require LocalizationFilter, so narrowing the mapping to *.jsp is enough for JBoss Portal Platform to function correctly.
Additionally deployed portlets, and portal applications, however, may require broader mapping to cover their non-bridged resources.
Narrowing the mapping might improve performance. This is something to consider, when optimizing for speed.

Chapter 11. XML Resources Bundles

11.1. Overview

Resource bundles are usually stored in property files. However, as property files are plain files, issues with the encoding of the file may arise. The XML resource bundle format has been developed to provide an alternative to property files.
  • The XML format declares the encoding of the file. This avoids use of the native2ASCII program which can interfere with encoding.
  • Property files generally use ISO 8859-1 character encoding which does not cover the full unicode charset. As a result, languages such as Arabic would not be natively supported.
  • Tooling for XML files is better supported than the tooling for Java property files and thus the XML editor copes well with the file encoding.

11.2. XML format

The XML format is very simple and has been developed based on the 'do not Repeat Yourself' (DRY) principle. Usually resource bundle keys are hierarchically defined and we can leverage the hierarchic nature of the XML for that purpose. Here is an example of turning a property file into an XML resource bundle file:
UIAccountForm.tab.label.AccountInputSet = ...
UIAccountForm.tab.label.UIUserProfileInputSet = ...
UIAccountForm.label.Profile = ...
UIAccountForm.label.HomeInfo= ...
UIAccountForm.label.BusinessInfo= ...
UIAccountForm.label.password= ...
UIAccountForm.label.Confirmpassword= ...
UIAccountForm.label.email= ...
UIAccountForm.action.Reset= ...
<?xml version="1.0" encoding="UTF-8"?>
<bundle>
  <UIAccountForm>
    <tab>
      <label>
        <AccountInputSet>...</AccountInputSet>
        <UIUserProfileInputSet>...</UIUserProfileInputSet>
      </label>
    </tab>
    <label>
      <Profile>...</Profile>
      <HomeInfo>...</HomeInfo>
      <BusinessInfo>...</BusinessInfo>
      <password>...</password>
      <Confirmpassword>...</Confirmpassword>
      <email>...</email>
    </label>
    <action>
      <Reset>...</Reset>
    </action>
  </UIAccountForm>
</bundle>

11.3. Portal Support

In order to be loaded by the portal at runtime (actually the resource bundle service), the name of the file must be the same as a property file and it must use the .xml suffix.
For example; for the Account Portlet to be displayed in Arabic, the resource bundle would be AccountPortlet_ar.xml rather than AccountPortlet_ar.properties.

Chapter 12. Right To Left (RTL) Framework

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() { ... }

12.1. Groovy templates

Orientation is defined by implicit variables passed into the groovy binding context:
Orientation
The current orientation as an Orientation.
isLT
The value of orientation.isLT().
isRT
The value of orientation.isRT().
dir
The string 'ltr' if the orientation is LT or the string 'rtl' if the orientation is RT.

12.2. Stylesheet

The skin service handles stylesheet rewriting to accommodate the orientation.
It works by appending -lt or -rt to the stylesheet name.
For instance: JPP_HOME/gatein/gatein.ear/portal.war/templates/skin/portal/webui/component/UIFooterPortlet/DefaultStylesheet-rt.css will return the same stylesheet as JPP_HOME/gatein/gatein.ear/portal.war/templates/skin/portal/webui/component/UIFooterPortlet/DefaultStylesheet.css but processed for the RT orientation. The -lt suffix is optional.
Stylesheet authors can annotate their stylesheet to create content that depends on the orientation.
In the example below we need to use the orientation to modify the float attribute that will make the horizontal tabs either float on left or on right:

Example 12.1. Example 1

float: left; /* orientation=lt */
float: right; /* orientation=rt */
font-weight: bold;
text-align: center;
white-space: nowrap;

The LT produced output will be:
float: left; /* orientation=lt */
font-weight: bold;
text-align: center;
white-space: nowrap;
The RT produced output will be:
 
float: right; /* orientation=rt */
font-weight: bold;
text-align: center;
white-space: nowrap;
In this example we need to modify the padding according to the orientation:

Example 12.2. Example 2

color: white;
line-height: 24px;
padding: 0px 5px 0px 0px; /* orientation=lt */
padding: 0px 0px 0px 5px; /* orientation=rt */

The LT produced output will be:
 
color: white;
line-height: 24px;
padding: 0px 5px 0px 0px; /* orientation=lt */
The RT produced output will be:
 
color: white;
line-height: 24px;
padding: 0px 0px 0px 5px; /* orientation=rt */

12.3. Images

Sometimes it is necessary to create an RT version of an image that will be used from a template or from a stylesheet. However symmetric images can be automatically generated, avoiding the necessity to create a mirrored version of an image and further maintenance costs.
The web resource filter uses the same naming pattern as the skin service. When an image ends with the -rt suffix the portal will attempt to locate the original image and create a mirror of it.
For instance: requesting the image JPP_HOME/gatein/gatein.ear/eXoResources.war/skin/DefaultSkin/webui/component/UITabSystem/UITabs/background/NormalTabStyle-rt.gif returns a mirror of the image JPP_HOME/gatein/gatein.ear/eXoResources.war/skin/DefaultSkin/webui/component/UITabSystem/UITabs/background/NormalTabStyle.gif.

Note

It is important to consider whether the image to be mirrored is symmetrical as this will impact its final appearance.
Here is an example combining stylesheet and images:
line-height: 24px; 
background: url('background/NavigationTab.gif') no-repeat right top; /* orientation=lt */
background: url('background/NavigationTab-rt.gif') no-repeat left top; /* orientation=rt */
padding-right: 2px; /* orientation=lt */
padding-left: 2px; /* orientation=rt */

12.4. Client Side JavaScript

The eXo.core.I18n object provides the following parameters for orientation:
getOrientation()
Returns either the string lt or rt
getDir()
Returns either the string ltr or rtl
isLT()
Returns true for LT
isRT()
Returns true of RT

Chapter 13. JavaScript Inter Application Communication

13.1. Overview

JavaScript Inter Application Communication is designed to allow applications within a page to exchange data. This library is made for broadcasting messages on topic.
It is based on three functions:
  • Subscribe.
  • Publish.
  • Unsubscribe.
A subscription to a topic will receive any subtopic messages. For example, an application subscribed to "/eXo/application" will receive messages sent on the "/eXo/application/map" topic. A message sent on "/eXo", however, would not be received.

Subscription Topics

/eXo
This topic contains all the events generated by the platform.
/eXo/portal/notification
A message is sent on this topic will prompt a pop-up notification in the top right of the screen.

13.2. Library

The Inter Application Communication library is found in JPP_HOME/gatein/gatein.ear/eXoResources.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() { ... }

13.3. Syntax

The three messaging functions require particular objects and definitions in their syntax:
Subscribe
The subscribe function is used to subscribe a callback to a topic. It uses the following parameters:
topic
The topic that will be listened for.
func
The name of the object function to call when a message is received on the topic. It has to be a function that takes an Object parameter. The event received will have this format:
{
  senderId:senderId,
  message:message,
  topic: topic
}
Publish
The publish function is used to publish an event to the other subscribed applications through the given channels. Its parameters are:
senderId
This is a string that identifies the sender.
topicName
The topic that the message will be published.
message
This is the message body to be delivered to the subscribers to the topic.
Unsubscribe
The unsubscribe function is used to unsubscribe a callback to a topic. The required parameters are:
topic
The topic that will is to be unsubscribed from.
id
This is the context object.

13.4. Example of JavaScript events usage

<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<portlet:defineObjects/>
<div>
  <p>
    Received messages:
    <div id="received_<portlet:namespace/>">
         
    </div>
  </p>
   
  <p>
    Send message:
    <input type="text" id="msg_<portlet:namespace/>"/> <a href="#" onclick="send_<portlet:namespace/>();">send</a>
  </p>
</div>

<script type="text/javascript">
   
  Function.prototype.bind = function(object) {
    var method = this;
      return function() {
      method.apply(object, arguments);
    }
  }
   
  function send_<portlet:namespace/>() {
    var msg = document.getElementById("msg_<portlet:namespace/>").value;
    eXo.core.Topic.publish("<portlet:namespace/>", "/demo", msg);
  }
   
  function Listener_<portlet:namespace/>(){
      
  }
   
  Listener_<portlet:namespace/>.prototype.receiveMsg = function(event) {
    document.getElementById("received_<portlet:namespace/>").innerHTML = 
      document.getElementById("received_<portlet:namespace/>").innerHTML + "<br />* " + 
      event.senderId + ": " + event.message;
  }
   
  function init_<portlet:namespace/>() {
    var listener_<portlet:namespace/> = new Listener_<portlet:namespace/>();
    eXo.core.Topic.subscribe("/demo", listener_<portlet:namespace/>.receiveMsg.bind(listener_<portlet:namespace/>));
  }
   
  init_<portlet:namespace/>();
</script>

Chapter 14. Navigation Controller

14.1. Description

The navigation controller is a major enhancement of JBoss Portal Platform that has several goals:
  • Provide non ambiguous URLs for portal managed resources such as navigation. Previously different resources were possible for a single URL, even worse, the set of resources available for a URL was depending on one's private navigation (groups and dashboard)
  • Decouple the http request from the portal request. Previously both were tightly coupled, for instance the URL for a site had to begin with /public/{sitename} or /private/{sitename}. The navigation controller provides a flexible and configurable mapping.
  • Provide more friendly URL and give a degree of freedom for portal administrators by letting them configure how http request should look.

14.2. Controller in Action

14.2.1. Controller

The WebAppController is the component of JBoss Portal Platform that processes http invocations and transforms them into a portal request. It has been improved with the addition of a request mapping engine (controller) whose role is to make the decoupling of the http request and create a portal request.
The mapping engine performs two essential tasks:
  • It creates a Map<QualifiedName, String> from an incoming http request.
  • It renders a Map<QualifiedName, String> as an http URL.
The goal of the controller (mapping engine) is to decouple the request processed by JBoss Portal Platform from the incoming HTTP request.
Indeed a request contains data that determines how the request will be processed and such data can be encoded in various places in the request such as the request path or a query parameter. The controller allows JBoss Portal Platform route a request according to a set of parameters (a map) instead of the servlet request.
The controller configuration is declarative in an XML file, allowing easy reconfiguration of the routing table and it is processed into an internal data structure that is used to perform resolution (routing or rendering)

14.2.2. Building Controller

The controller configuration that contains the routing rules is loaded from a file named controller.xml that is retrieved in the JBoss Portal Platform configuration directory. Its location is determined by the gatein.controller.config property.
WebAppController loads and initializes the mapping engine.
<!-- conf/portal/controller-configuration.xml of portal.war -->
<component>
 <type>org.exoplatform.web.WebAppController</type>
  <init-params>
   <value-param>
    <name>controller.config</name>
    <value>${gatein.portal.controller.config}</value>
   </value-param>
  </init-params>
</component>
JBoss Portal Platform's extension project can define their own routing table, because of the extension mechanism.
The controller.xml can be changed and reloaded at runtime. This helps testing different configurations easily (configuration loading operations) and provides more insight into the routing engine (the findRoutes operation).
The WebAppController is annotated with @Managed annotations and is bound under the view=portal,service=controller JMX name and under the "portalcontroller" REST name.
It provides the following attributes and operations
  • Attribute configurationPath: the read only configuration path of the controller xml file
  • Operation loadConfiguration: load a new configuration file from a specified xml path
  • Operation reloadConfiguration: reload the configuration file
  • Operation findRoutes: routes the request argument through the controller and returns a list of all parameter map resolution. The argument is a request uri such as /g/:platform:administrators/administration/registry. It returns a string representation (List<Map>) of the matched routes.

14.2.3. Controller Configuration (controller.xml)

Most of the controller configuration cares about defining rules (Routing table - contains routes object) that will drive the resolution. Routes are processed during the controller initialization to give a tree of nodes. For every node the following is true:
  • It is related to its parent with a matching rule that can either be an exact string matching or a regular expression matching.
  • It is associated with a set of parameters.
A parameter is defined by a qualified name. There are three types of parameters: Route, Path, and Request.

14.2.3.1.  Route parameters

Route parameters define a fixed value associate with a qualified name.
  • Routing: route parameters allow the controller to distinguish branches easily and route the request accordingly.
  • Rendering: the system will select a route to render an URL if all route parameters are always matched.
Example:
<route path="/foo">
 <route-param qname="gtn:handler">
  <value>portal</value>
 </route-param>
</route>
This configuration matches the request path "/foo" to the map (gtn:handler=portal). Conversely it renders the (gtn:handler=portal) map as the "/foo" URL. In this example we see two concepts
  • exact path matching ("/foo")
  • route parameters ("gtn:handler")

14.2.3.2.  Path parameters - Regular expression support

Path parameters allow to associate a portion of the request path with a parameter. Such parameter will match any non empty portion of text except the / character (that is the [^/]+ regular expression) otherwise they can be associated with a regular expression for matching specific patterns. Path parameters are mandatory for matching since they are part of the request path, however it is allowed to write regular expression matching an empty value.
  • Routing: route is accepted if the regular expression is matched.
  • Rendering: selection occurs when the regular expression matches the parameter.
Encoding
Path parameters may contain '/' character which is a reserved char for URI path. This case is specially handled by the navigation controller by using a special character to replace '/' literals. By default the character is the semi colon : and can be changed to other possible values (see controller XML schema for possible values) to give a greater amount of flexibility.
This encoding is applied only when the encoding performed for parameter having a mode set to the default-form value, for instance it does not happen for navigation node URI (for which / are encoded literally). The separator escape char can still be used but under it's percent escaped form, so by default a path parameter value containing : would be encoded as %3A and conversely the %3A value will be decoded as :.
Example:
<route path="/{gtn:path}">
</route>
No pattern defined, used the default one [^/]+
Routing and Rendering
Path "/foo"   <--> the map (gtn:path=foo)

Path "/foo:bar" <--> the map (gtn:path=foo/bar)
If the request path contains another "/" char it will not work,default encoding mode is: default-form. For example:"/foo/bar" --> not matched, return empty parameter map
However this could be solved with the following configuration:
<route path="/{gtn:path}">
 <path-param encoding="preserve-path" qname="gtn:path">
  <pattern>.*</pattern>
 </path-param>
</route>
  1. The .* declaration allows to match any char sequence.
  2. The preserve-path encoding tells the engine that the "/" chars should be handled by the path parameter itself as they have a special meaning for the router. Without this special encoding, "/" would be rendered as the ":" character and conversely the ":" character would be matched as the "/" character.

14.2.3.3. Request parameters

Request parameters are matched from the request parameters (GET or POST). The match can be optional as their representation in the request allows it.
  • Routing
    • route is accepted when a required parameter is present and matched in the request.
    • route is accepted when an optional parameter is absent or matched in the request.
  • Rendering:
    • selection occurs for required parameters when the parameter is present and matched in the map.
    • selection occurs for optional parameters when the parameter is absent or matched in the map.
Example:
<route path="/">
 <request-param name="path" qname="gtn:path"/>
</route>
Request parameters are declared by a request-param element and by default will match any value. A request like "/?path=foo" is mapped to the (gtn:path=foo) map. The name attribute of the request-param tag defines the request parameter value. This element accepts more configuration
  • a value or a pattern child element to match a constant or a pattern
  • a control-mode attribute with the value optional or required to indicate if matching is mandatory or not
  • a value-mapping attribute with the possible values canonical, never-empty, never-null can be used to filter values after matching is done. For instance a parameter configured with value-mapping="never-empty" and matching the empty string value will not put the empty string in the map.

14.2.3.4. Route precedence

The order of route declaration is important as it influences how rules are matched. Sometimes the same request could be matched by several routes and the routing table is ambiguous.
<route path="/foo">
 <route-param qname="gtn:handler">
  <value>portal</value>
 </route-param>
</route>
<route path="/{gtn:path}">
 <path-param encoding="preserve-path" qname="gtn:path">
  <pattern>.*</pattern>
 </path-param>
</route>
In that case, the request path "/foo" will always be matched by the first rule before the second rule. This can be misleading since the map (gtn:path=foo) would be rendered as "/foo" as well and would not be matched by the first rule. Such ambiguity can happen, it can be desirable or not.

14.2.3.5. Route nesting

Route nesting is possible and often desirable as it helps to
  • factor common parameters in a common rule
  • perform more efficient matching as the match of the common rule is done once for all the sub routes
<route path="/foo">
 <route-param qname="gtn:handler">
  <value>portal</value>
 </route-param>
 <route path="/bar">
  <route-param qname="gtn:path">
   <value>bar</value>
  </route-param>
 </route>
 <route path="/juu">
  <route-param qname="gtn:path">
   <value>juu</value>
  </route-param>
 </route>
</route>
  • The request path "/foo/bar" is mapped to the (gtn:handler=portal,gtn:path=bar) map
  • The request path "/foo/juu" is mapped to the (gtn:handler=portal,gtn:path=juu) map
  • The request path "/foo" is not mapped as non leaf routes do not perform matches.

14.2.3.6. Custom error pages

When an HTML error code is returned as a response to a URL request (e.g. 404 Not Found), JBoss Portal Platform redirects users to a default error page where the error code is displayed together with an explanatory message. To replace the default error page with a customized one, the configuration explained below needs to be performed.

Procedure 14.1. Configuring redirection to custom error pages

  1. Create the actual error pages and place them in the JPP_HOME/gatein/gatein.ear/portal.war/ directory.
  2. For each error code that shall have its custom error page, add the <error-page> element to the JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/web.xml file. This element specifies what page is displayed when the particular error code is returned.
    The sample code below ensures that the my404.html page is displayed when the 404 error code is returned.
    <error-page>
      <error-code>404</error-code>
      <location>/my404.html</location>
    </error-page>
    
  3. Specify the error page locations as static resources in the JPP_HOME/standalone/configuration/gatein/controller.xml file. The code sample below demonstrates this configuration for the /my404.html path.
    <route path="/my404.html">
      <route-param qname="gtn:handler">
     <value>staticResource</value>
      </route-param>
    </route>
    
    Without this configuration, the portal tries to resolve /my404.html as a name of a portal or another resource. This results in unwanted redirection to /portal/my404.html. It is therefore necessary to configure the error page locations as static resources.
  4. When all the previous steps are performed, users are redirected to the specified custom error pages when the respective error codes are returned as a response to a URL request.

14.3. Integrate to JBoss Portal Platform WebUI framework

14.3.1. Routing

JBoss Portal Platform defines a set of parameters in its routing table, for each client request, the mapping engine processes the request path and returns the defined parameters with their values as a Map<QualifiedName, String>
gtn:handler
The gtn:handler name is one of the most important qualified names as it determines which handler will take care of the request processing just after the controller has determined the parameter map. The handler value is used to make a lookup in the handler map of the controller. A handler is a class that extends the WebRequestHandler class and implements the execute(ControllerContext) method. Several handlers are available by default:
  • portal: process aggregated portal requests
  • upload / download: process file upload and file download
  • legacy: handle legacy URL redirection (see Legacy handler section)
  • default: http redirection to the default portal of the container
  • staticResource: serve static resources like image, css or javascript... files in portal.war (see Static Resource Handler section)
gtn:sitetype / gtn:sitename / gtn:path
The qualified names drive a request for the portal handler. They are used to determine which site to show and which path to resolve against a navigation. For instance the (gtn:sitetype=portal,gtn:sitename=classic,gtn:path=home) instruct the portal handler to show the home page of the classic portal site.
gtn:lang
language in the URL for the portal handler. The language can be specified in URL so that the user can bookmark the URL in the particular locale or change the locale by modifying the URL address.
gtn:componentid / gtn:action / gtn:objectid
webui parameters used by the portal handler for managing webui component URLs for portal applications. These are not used for portlet applications.

14.3.2. Rendering

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

14.3.2.1.  Portal URL

PortalURL has a similar role at the portal level: its main role is to abstract the creation of a URL for a resource managed by the portal.
public abstract class PortalURL<R, U extends PortalURL<U>>
{
  ...
}
The PortalURL declaration may seem a bit strange at first sight with two generic types U and R and the circular recursion of the U generic parameter, but it's because most of the time you will not use the PortalURL object but instead subclasses.
  • The R generic type represents the type of the resource managed by the portal
  • The U generic type is also described as self bound generic type. This design pattern allows a class to return subtypes of itself in the class declaring the generic type. Java Enums are based on this principle (class Enum<E extends Enum<E>>)
A portal URL has various methods but certainly the most important method is the toString() method that generates a URL representing that will target the resource associated with the URL. The remaining methods are getter and setter for mutating the URL configuration, those options will affect the URL representation when it is generated.
  • resource: the mandatory resource associated with the URL
  • locale: the optional locale used in the URL allowing the creation of bookmarkable URL containing a language
  • confirm: the optional confirm message displayed by the portal in the context of the portal UI
  • ajax: the optional Ajax option allowing an Ajax invocation of the URL
Obtaining a PortalURL
PortalURL objects are obtained from RequestContext instance such as the PortalRequestContext or the PortletRequestContext. Usually those are obtained thanks to getCurrentInstance method of the RequestContext class:
RequestContext ctx = RequestContext.getCurrentInstance();
PortalURL is created with the createURL method that has a resource type as its input parameter. A resource type is usually a constant and a type-safe object that allow to retrieve PortalURL subclasses:
RequestContext ctx = RequestContext.getCurrentInstance();
PortalURL<R, U> URL = ctx.createURL(type);
In reality you will use a concrete type constant and have instead more concrete code like:
RequestContext ctx = RequestContext.getCurrentInstance();
NodeURL URL = ctx.createURL(NodeURL.TYPE);

Note

The NodeURL.TYPE is actually declared as new ResourceType<NavigationResource, NodeURL>() that can be described as a type literal object emulated by a Java anonymous inner class. Such literals were introduced by Neil Gafter as Super Type Token and popularized by Google Guice as Type Literal. It's an interesting way to create a literal representing a kind of Java type.

14.3.2.2.  Node URL

The class NodeURL is a subclass of the PortalURL specialized for navigation node resources:
public class NodeURL extends PortalURL<NavigationResource, NodeURL>
{
  ...
}
The good news is that the NodeURL does not carry any generic type of its super class, which means that a NodeURL is type safe and one does not have to worry about generic types.
Using a NodeURL is pretty straightforward:
NodeURL URL = RequestContext.getCurrentInstance().createURL(NodeURL.TYPE);
URL.setResource(new NavigationResource("portal", "classic, "home"));
String s = URL.toString();
The NodeURL subclass contains specialized setter to make its usage even easier:
UserNode node = ...;
NodeURL URL = RequestContext.getCurrentInstance().createURL(NodeURL.TYPE);
URL.setNode(node);
String s = URL.toString();

14.3.2.3.  Component URL

The ComponentURL subclass is another specialization of PortalURL that allows the creation of WebUI components URLs. ComponentURL is commonly used to trigger WebUI events from client side:
<% def componentURL = uicomponent.event(...); /*or uicomponent.URL(...) */ %>
 <a href=$componentURL>Click me</a>
Normally you should not have to deal with it as the WebUI framework has already an abstraction for managing URL known as URLBuilder. The URLBuilder implementation delegates URL creation to ComponentURL objects.

14.3.2.4. Portlet URL

Portlet URLs API implementation delegates to the portal ComponentURL (via the portlet container SPI). It is possible to control the language in the URL from a PortletURL object by setting a property named gtn:lang:
  • when the property value is set to a value returned by Locale#toString() method for locale objects having a non null language value and a null variant value, the URL generated by the PortletURL#toString() method will contain the locale in the URL.
  • when the property value is set to an empty string, the generated URL will not contain a language. If the incoming URL was carrying a language, this language will be erased.
  • when the property value is not set, it will not affect the generated URL.
PortletURL URL = resp.createRenderURL();
URL.setProperty("gtn:lang", "fr");
writer.print("<a href='" + URL + "'>French</a>");

14.3.2.5. WebUI URL Builder

This internal API for creating URL works as before and delegates to the PortletURL API when the framework is executed in a portlet and to a ComponentURL API when the framework is executed in the portal context. The API has been modified to take in account the language in URL with two properties on the builder:
  • locale: a locale for setting on the URL
  • removeLocale: a boolean for removing the locale present on the URL

14.3.2.6. Groovy Templates

Within a Groovy template the mechanism is the same, however a splash of integration has been done to make creation of NodeURL simpler. A closure is bound under the nodeurl name and is available for invocation anytime. It will simply create a NodeURL object and return it:
UserNode node = ...;
NodeURL URL = nodeurl();
URL.setNode(node);
String s = URL.toString();
The closure nodeurl is bound to Groovy template in WebuiBindingContext
// Closure nodeurl()
put("nodeurl", new Closure(this)
{
 @Override
 public Object call(Object[] args)
 {
  return context.createURL(NodeURL.TYPE);
 }
});

Part II. Portlet development

Table of Contents

15. Portlet Primer
15.1. JSR-168 and JSR-286 overview
15.1.1. Portal Pages
15.1.2. Rendering Modes
15.1.3. Window States
15.2. Tutorials
15.2.1. Deploying your first portlet
15.2.2. JavaServer Pages Portlet Example
16. Shared portlet.xml
16.1. Global Metadata Elements
17. JBoss Portlet Bridge
17.1. Portlet Bridge
17.2. JBoss Portlet Bridge
17.3. Portlet application
17.4. Extensions
17.5. Examples
17.6. Bridge Configuration
17.6.1. portlet.xml
17.7. Render Policy Parameters
17.8. Facelets Configuration
17.8.1. web.xml
17.9. JSP Only Configuration
17.9.1. web.xml
17.10. RichFaces Local and Remote Portlet Support
17.11. Sending and Receiving Events
17.11.1. Sending Events
17.11.2. Receiving Events
17.11.3. Public Render Parameters
17.11.4. Portlet Session
17.12. Resource serving
17.13. Serving JSF Resources in a Portlet
17.13.1. Expression Language Reference
17.13.2. Configuration
17.14. Developing Portlets with the Bridge
17.14.1. Implementing Portlet Bridge
17.14.2. Portlet tags
17.14.3. Excluding Attributes from the Bridge Request Scope
17.14.4. Prevent Resources Being Added to Portal Page Head
17.14.5. JSF facelet view
17.14.6. Error handling
17.14.7. Switching Portlet Modes
17.14.8. Navigating to a mode's last viewId
17.14.9. Using Wildcards to Identify the Rule Target
17.14.10. Clearing The View History When Changing Portlet Modes
17.14.11. Communication Between Your Portlets
17.14.12. Linking to a Facelets page within the same portlet
17.14.13. Redirecting to an external page or resource
17.14.14. Using Provided EL Variables

Chapter 15. Portlet Primer

15.1. JSR-168 and JSR-286 overview

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 JBoss Portal Platform is no exception. In fact, JBoss Portal Platform 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 .
JBoss Portal Platform is fully JSR-286 compliant. Any JSR-168 or JSR-286 portlet operates as it is mandated by the respective specifications inside the portal.

15.1.1. Portal Pages

A portal can be considered as a series of web pages with different areas within them. Those areas contain different windows and each window contains a portlet:
The diagram below visually represents this nesting:
Portal Specification

Figure 15.1. Portal Specification


15.1.2. Rendering Modes

A portlet can have different view modes. Three modes are defined by the JSR-286 specification:
View
Generates markup reflecting the current state of the portlet.
Edit
Allows a user to customize the behavior of the portlet.
Help
Provides information to the user as to how to use the portlet.

15.1.3. Window States

Window states are an indicator of how much page space a portlet consumes on any given page. The three states defined by the JSR-286 specification are:
Normal
A portlet shares this page with other portlets.
Minimized
A portlet may show very little information, or none at all.
Maximized
A portlet may be the only portlet displayed on this page.

15.2. Tutorials

The tutorials contained in this chapter are targeted toward portlet developers. It is also recommended 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

15.2.1. Deploying your first portlet

This section describes how to deploy a portlet in JBoss Portal Platform.
An example portlet called SimplestHelloWorld is available in the /jboss-jpp-VERSION-src/portal/examples/portlets/ directory of the JBoss Portal Platform sources package.

15.2.1.1. Compiling

To compile and package the application:
  1. Navigate to the application directory and execute:
    mvn package
    
  2. If the example is successfully packaged, the result will be available in: gatein-simplest-helloworld-6.0.0.GA.war .
  3. Copy the package file into JPP_HOME/standalone/deployments.
  4. Start the portal (if it is not already running).
  5. Add the new portlet to the Application Registry.
  6. Create a new portal page and add the portlet to it.

15.2.1.2. Package Structure

Like other Java EE applications, JBoss Portal Platform portlets are packaged in WAR files. A typical portlet WAR file can include servlets, resource bundles, images, HTML, JavaServer Pages (JSP), and other static or dynamic files.
The following is an example of the directory structure of the SimplestHelloWorld portlet.

Example 15.1. Portlet Directory Structure Explanation

|-- SimplestHelloWorld-0.0.1.war
|   `-- WEB-INF
|       |-- classes
|       |   `-- org
|       |       `-- jboss
|       |           `-- portal
|       |               `-- portlet
|       |                   `-- samples
|       |                       `-- SimplestHelloWorldPortlet.class
|       |-- portlet.xml
|       `-- web.xml
SimplestHelloWorldPortlet.class: The compiled Java class implementing javax.portlet.Portlet (through javax.portlet.GenericPortlet).
portlet.xml: This is the mandatory descriptor file for portlets. It is used during deployment.
web.xml: This is the mandatory descriptor for web applications.

15.2.1.3. Portlet Class

Below is the Java source for an example portlet named SimplestHelloWorldPortlet.

Example 15.2. SimplestHelloWorldPortlet Explanation

package org.jboss.portal.portlet.samples;

import java.io.IOException;
import java.io.PrintWriter;
// Comment #1
import javax.portlet.GenericPortlet;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class SimplestHelloWorldPortlet extends GenericPortlet
{
   // Comment #2
   public void doView(RenderRequest request, RenderResponse response) throws IOException
   {
      // Comment #3
      PrintWriter writer = response.getWriter();
      // Comment #4
      writer.write("Hello World !");
      // Comment #5
      writer.close();
   }
}
Comment #1: All portlets must implement the javax.portlet.Portlet interface. The GenericPortlet class provides a default implementation for the Portlet interface.
The javax.portlet.GenericPortlet class implements the render method to dispatch to abstract mode-specific methods. This makes it easier to support the standard portlet modes.
GenericPortlet also provides a default implementation for the processAction, init and destroy methods. It is recommended to extend GenericPortlet for most cases.
Comment #2: If only the view mode is required, then only the doView method needs to be implemented. The GenericPortlet render implementation calls our implementation when the view mode is requested.
Comment #3: To obtain a writer that can produce content, use the response.GetWriter() method from the RenderResponse object.
Comment #4: Write the markup to display.
Comment #5: Closing the writer.

Markup Fragments

Portlets are responsible for generating markup fragments, as they are included on a page and are surrounded by other portlets. This means that a portlet outputting HTML must not output any markup that cannot be found in a <body> element.

15.2.1.4. Application Descriptors

JBoss Portal Platform requires certain descriptors to be included in a portlet WAR file. These descriptors are defined by the Java 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.

Example 15.3. The portlet.xml File Explanation

<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>
   	  <!-- Comment #1 -->
      <portlet-name>SimplestHelloWorldPortlet</portlet-name>
      <!-- Comment #2 -->
      <portlet-class>
	org.jboss.portal.portlet.samples.SimplestHelloWorldPortlet
      </portlet-class>
      <!-- Comment #3 -->
      <supports>
        <mime-type>text/html</mime-type>
      </supports>
      <!-- Comment #4 -->
      <portlet-info>
          <title>Simplest Hello World Portlet</title>
      </portlet-info>
   </portlet>
</portlet-app>
Comment #1: Define the portlet name. It does not have to be the class name.
Comment #2: The Fully Qualified Name (FQN) of your portlet class must be declared here.
Comment #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.
Comment #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 .

15.2.2. JavaServer Pages Portlet Example

This section discusses:
  1. Adding more features to the previous example.
  2. Using a JSP page to render the markup.
  3. Using the portlet tag library to generate links to the portlet in different ways.
  4. Using the other standard portlet modes.
Compiling the example
The example used in this section is available in the directory of the JBoss Portal Platform sources package.
Compile the example as so:

Procedure 15.1. Compile JavaServer Pages Portlet

  1. Obtain the JBoss Portal Platform sources package from the Customer Support portal.
  2. Move to /jboss-jpp-VERSION-src/portal/examples/portlets/jsphellouser
  3. Execute mvn package.
  4. Copy jsphellouser/target/gatein-jsp-hellouser-6.0.0.GA.war to the deploy directory of JBoss Application Server.
  5. Add the new portlet to the Application Registry.
  6. Create a new portal page and add the portlet to it.
Create New Portal Page and Add Portlet

Figure 15.2. Create New Portal Page and Add Portlet


15.2.2.1. Package Structure

The package structure in this tutorial does not differ greatly from the previous example, with the exception of adding some JSP files which are detailed later.
The JSPHelloUser portlet contains the mandatory portlet application descriptors. The following is an example of the directory structure of the JSPHelloUser portlet:
gatein-jsp-hellouser->1.0.0-GA-SNAPSHOT.war
    |-- META-INF
    |   |-- MANIFEST.MF
    |-- WEB-INF
    |   |-- classes
    |   |   `-- org
    |   |       `-- jboss
    |   |           `-- portal
    |   |               `-- portlet
    |   |                   `-- samples
    |   |                       `-- JSPHelloUserPortlet.class
    |   |-- portlet.xml
    |   `-- web.xml
    `-- jsp
        |-- edit.jsp
        |-- hello.jsp
        |-- help.jsp
        `-- welcome.jsp

15.2.2.2. Portlet Class

The code below is from the jsphellouser/src/main/java/org/jboss/portal/portlet/samples/JSPHelloUserPortlet.java Java source. It is split in different pieces.

Example 15.4. JSPHelloUserPortlet Explanation

package org.jboss.portal.portlet.samples;
 
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
{
// Comment #1    
   public void doView(RenderRequest request, RenderResponse response)
       throws PortletException, IOException
   {
// Comment #2
      String sYourName = (String) request.getParameter("yourname");
      if (sYourName != null)
      {
         request.setAttribute("yourname", sYourName);
// Comment #3
         PortletRequestDispatcher prd = 
            getPortletContext().getRequestDispatcher("/jsp/hello.jsp");
// Comment #4
         prd.include(request, response);
      }
      else
      {
//Code split between lines. Direct copy will result in parse errors.
         PortletRequestDispatcher prd = getPortletContext().
          getRequestDispatcher("/jsp/welcome.jsp");
         prd.include(request, response);
      }
   }
...
Comment #1: Override the doView method (as in the first tutorial).
Comment #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.
Comment #3: Get a request dispatcher on a file located within the web archive.
Comment #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 a 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.

Example 15.5. processAction Explanation

...
// Comment #1
         public void processAction(ActionRequest aRequest, ActionResponse aResponse) throws PortletException, IOException, UnavailableException
   {
// Comment #2
      String sYourname = (String) aRequest.getParameter("yourname");
// Comment #3
      aResponse.setRenderParameter("yourname", sYourname);
   }
...
Comment #1: processAction is the method from GenericPortlet to override for the action phase.
Comment #2: Here the parameter is retrieved through an action URL.
Comment #3: The value of yourname is kept to make it available in the rendering phase. The previous line simply copies an action parameter to a render parameter for this example.

15.2.2.3. JSP files and the Portlet Tag Library

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.

Example 15.6. Landing Page Explanation

<!-- Comment #1 -->
<%@ taglib uri="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/>
<!-- Comment #2 -->
<a href="<portlet: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.
<!-- Comment #3 -->
<portlet:renderURL var="myRenderURL">
    <portlet:param name="yourname" value='John Doe'/>
</portlet:renderURL>
<br/>
<!-- Comment #4 -->
<a href="<%= myRenderURL %>">John Doe</a></div>
 
<br/>
 
<div class="portlet-font">Method 3: We use a form:<br/>
<!-- Comment #5 -->
<portlet:actionURL var="myActionURL"/>
<!-- Comment #6 -->
<form action="<%= 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>
Comment #1: The portlet taglib. This needs to be declared.
Comment #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.
Comment #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).
Comment #4: The variable myRenderURL is used like any other JSP variable.
Comment #5: The third method mixes form submission and action request. Again, a temporary variable is used to put the created URL into.
Comment #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.
Render Phase Process

Figure 15.3. Render Phase Process


Chapter 16. Shared portlet.xml

The Java Portlet Specification introduces PortletFilter as a standard approach to extend the behaviors of portlet objects. For example, a filter can transform the content of portlet requests and portlet responses.
According to the Portlet Specification, there are normally three steps in setting up a portlet filter:
  1. Implement a PortletFilter object.
  2. Define the filter in portlet application deployment descriptor.
  3. Define the filter mapping in portlet definitions.
While the first two steps are quite straightforward, the third requires developers or administrators to replicate the filter mapping in many portlet definitions. This can be tedious and opens the potential for input errors. The global portlet feature is designed to mitigate these concerns.
Global portlet metadata is declared in the portlet.xml file and conforms with the Portlet 2.0 XSD.
<portlet-app version="1.0" xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
</portlet-app>
The path to the global portlet.xml is the value of gatein.portlet.config in the configuration.properties.xml file. By default, the file path is JPP_HOME/standalone/configuration/gatein/portlet.xml

16.1. Global Metadata Elements

The global JPP_HOME/standalone/configuration/gatein/portlet.xml file conforms, with some restrictions, to the portlet deployment descriptor schema defined in the Portlet Specification. In this file, the following elements are supported:
  1. Portlet Mode
  2. Window State
Portlet Filter
Portlet filter mappings declared in the global portlet.xml file are applied across portlet applications.
With the XML configuration below, the filter ApplicationMonitoringFilter is involved in request handling on any deployed portlet.
<filter>
<filter-name>org.exoplatform.portal.application.ApplicationMonitoringFilter</filter-name>
<filter-class>org.exoplatform.portal.application.ApplicationMonitoringFilter</filter-class>
  <life-cycle>ACTION_PHASE</life-cycle>
  <life-cycle>RENDER_PHASE</life-cycle>
  <life-cycle>EVENT_PHASE</life-cycle>
  <life-cycle>RESOURCE_PHASE</life-cycle>
</filter>
Application Monitoring Filter supports four life-cycle phases in the order below:
  1. ACTION_PHASE
  2. EVENT_PHASE
  3. RENDER_PHASE
  4. RESOURCE_PHASE
The Application Monitoring Filter records statistic information about deployed portlets. The filter alternates the actual monitoring mechanism in WebUI Framework.

Chapter 17. JBoss Portlet Bridge

17.1. Portlet Bridge

A portlet bridge is a mediator that allows non-native application frameworks to run in a portal environment, independent to the underlying portlet API, or portlet concept. This functionality provides a developer flexibility to continue writing applications in a preferred language, and allows a controlled transition to new technologies.

17.2. JBoss Portlet Bridge

The JBoss Portlet Bridge is an implementation of the JSR-329 specification. It supports the JSF 2.0 runtime within a JSR 168 or 286 portlet. Version 3.x of the bridge is compatible with JSF 2.0 and RichFaces 4.x.
JBoss Portlet Bridge allows JSF applications to run using supported JBoss frameworks such as RichFaces. Seam 3.2 support is not yet available for JSF2, however support may be included in a future release.
The bridge is used to execute Faces requests on behalf of the portlet. During each request, the Faces environment is setup and handled by the bridge.
Part of this implementation acts as a Faces controller, much like the FacesServlet does in the direct client request environment.
The other part of this implementation is provided by implementing a variety of (standard) Faces extensions.

17.3. Portlet application

A portlet application is defined as a single web archive (WAR).
All portlets that are part of the same WAR are considered to form part of the same portlet application.

17.4. Extensions

Portlet extensions sit atop the portlet bridge framework. They extend the functionality of other JBoss portlet applications, and are critical in JSF portlet development.

17.5.  Examples

Portlet Bridge provides example JSF2 portlets, which provide a good starting point for including the framework in portlets, as well as demonstrating the functionality available in Portlet Bridge.

Important

The provided examples are not officially supported by Red Hat.
JSF2 Portlet
This example provides a basic JSF2 portlet demonstrating ajax functionality. This example provides a solid foundation for basic JSF2 functionality.
RichFaces 4 Simple
This example demonstrates a simple RichFaces 4 form with ajax submission and an extended data table showing the submitted data. This example provides a solid foundation for basic RichFaces 4 functionality.
RichFaces 4 Showcase
This example demonstrates a RichFaces 4 application, with portlet-specific changes. This example is worth deploying for advanced RichFaces 4 functionality, and shows the full spectrum of what can be achieved with RichFaces 4 and Portlet Bridge

17.6. Bridge Configuration

17.6.1. portlet.xml

To have access to all portlet 2 specification features, you must declare the portlet schema definition as prescribed in the JPP_HOME/standalone/configuration/gatein/portlet.xml file. The <init-param> directives are explained in more detail if required below the portlet.xml code.
The XML below describes the basic directives required for a JSF2 portlet. Some directives are optional, and are called-out in the file.
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
  version="2.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
	<portlet>
   <portlet-name>yourPortletName</portlet-name>
   <portlet-class>
      javax.portlet.faces.GenericFacesPortlet
   </portlet-class>
<!-- MANDATORY -->
   <init-param>
      <name>javax.portlet.faces.defaultViewId.view</name>
      <value>/welcome.xhtml</value>
   </init-param>
<!-- OPTIONAL  -->
   <init-param>
      <name>javax.portlet.faces.defaultViewId.edit</name>
      <value>/jsf/edit.xhtml</value>
   </init-param>
<!-- OPTIONAL -->
   <init-param>
      <name>javax.portlet.faces.defaultViewId.help</name>
      <value>/jsf/help.xhtml</value>
   </init-param>
<!-- OPTIONAL - Preserves request parameters received as part of an portal ActionRequest  -->
   <init-param>
      <name>javax.portlet.faces.preserveActionParams</name>
      <value>true</value>
   </init-param>
<!-- OPTIONAL - explicitly set GenericFacesPortlet to override bridge event dispatch  -->
	 <init-param>
  	  <name>javax.portlet.faces.autoDispatchEvents</name>
      <value>true</value>
	 </init-param>
<!-- OPTIONAL - Receive Event - specifies whether the portlet can receive bridge events. In this case, a BookingEventHandler event  -->
   <init-param>
     <name>javax.portlet.faces.bridgeEventHandler</name>
     <value>org.jboss.example.booking.BookingEventHandler</value>
   </init-param>

<!-- OPTIONAL -->
   <supported-publishing-event>
      <qname xmlns:jbp="urn:jboss:portal:samples:event">[namespace]:[Event_name]</qname>
   </supported-publishing-event>
<!-- OPTIONAL -->
   <event-definition>
      <qname xmlns:jbp="urn:jboss:portal:samples:event">[namespace]:[event_name]</qname>
      <value-type>[the appropriate class to map the application type to]</value-type>
   </event-definition>
<!-- OPTIONAL - Receive Event -->
	 <supported-processing-event>
   		<qname xmlns:jbp="urn:jboss:portal:samples:event">[namespace]:[event_name]</qname>
	 </supported-processing-event>
</portlet>

<init-param> directives

preserveActionParams
Specifies how request parameters are maintained during a portal action request.

Important

When the entire portlet is written in JSF, this parameter must be set to true. If your portlet uses a mix of view technologies, set this parameter to false, or do not specify it.
When set to true, the bridge must maintain any request parameters assigned during the portlet's bridge request scope action request. When set to false, or not defined, the action's request parameters are only maintained for the duration of the portlet request scope.
autoDispatchEvents
Explicitly specifies that GenericFacesPortlet overrides event handling to dispatch all events to the Bridge.
GenericFacesPortlet overrides event handling by default, without supplying this directive. However, this parameter can be set to true so the behavior is explicitly stated in the configuration file.

Other directives

<supported-publishing-event>
Specifies that the portlet can publish an event to the portal.
The value of the <qname> parameter specifies a namespace (for example, jbp:), and the name to use when publishing the event (for example, BookingEvent).
<event-definition>
Specifies how the event namespace and publishing name link to an actual type within a portlet application.
The directive requires a <qname> element, and a <value-type> element.

17.7. Render Policy Parameters

Different JSF View Declaration Languages require different behavior from the JBoss Portlet Bridge when it comes to rendering views. Instructing the Bridge on which policy to use is done using the javax.portlet.faces.RENDER_POLICY <context-param> directive in web.xml.

RenderPolicy Options

ALWAYS_DELEGATE
Indicates the bridge should not render the view itself, but rather always delegate the rendering.
NEVER_DELEGATE
Indicates the bridge should always render the view itself and never delegate.
DEFAULT
Directs the bridge to first delegate the render. If an exception is thrown, the bridge renders the view based on its own logic. If the configuration parameter is not present or has an invalid value the bridge renders using default behavior as it would if DEFAULT was set.

17.8. Facelets Configuration

The following web.xml setting is only for Facelets based applications.

17.8.1. web.xml

<context-param>
  <param-name>javax.portlet.faces.RENDER_POLICY</param-name>
  <param-value>ALWAYS_DELEGATE</param-value>
</context-param>

17.9. JSP Only Configuration

The following web.xml setting is only for JSP based applications. Download the demonstration application here.

17.9.1. web.xml

<context-param>
  <param-name>javax.portlet.faces.RENDER_POLICY </param-name>
  <param-value>NEVER_DELEGATE</param-value>
</context-param>

17.10. RichFaces Local and Remote Portlet Support

Table 17.1, “RichFaces Feature Status” outlines the current status of RichFaces features when used in both local and remote portlets.

Table 17.1. RichFaces Feature Status

RichFaces Component Supported as Local Portlet Supported as Remote Portlet using WSRP
a4j:commandButton Yes Yes
a4j:commandLink Yes Yes
a4j:jsFunction Yes Yes
a4j:push Yes Yes
a4j:poll Yes Yes
a4j:queue No No
a4j:status Yes Yes
a4j:keepAlive Yes Yes
a4j:include Yes Yes
a4j:loadStyle No No
a4j:loadScript Yes Yes
a4j:ajaxValidator Yes Yes
a4j:beanValidator Yes Yes
a4j:graphValidator Yes Yes
a4j:mediaOutput No Yes
a4j:outputPanel Yes Yes
a4j:log Yes Yes
rich:dataTable Yes No
a4j:dataFilterSlider Yes Yes
rich:dataGrid Yes No
rich:dataList Yes Yes
rich:datascroller No No
rich:extendedDataTable Yes No
rich:repeat Yes Yes
rich:scrollableDataTable Yes Yes
drag-drop support Yes Yes
rich:contextMenu No No
rich:dropDownMenu Yes Yes
rich:tree Yes Yes
rich:modalPanel Yes Yes
rich:paint2d Yes Yes
rich:panel Yes Yes
rich:panelBar Yes Yes
rich:panelMenu Yes Yes
rich:progressBar Yes Yes
rich:separator Yes Yes
rich:simpleTogglePanel Yes Yes
rich:spacer Yes Yes
rich:tabPanel Yes Yes (except tab deletion)
rich:togglePanel Yes Yes
rich:toolBar Yes Yes
rich:toolTip Yes Yes
rich:calendar Yes No
rich:colorPicker Yes Yes
rich:comboBox Yes Yes
rich:editor No No
rich:fileUpload No No
rich:inplaceSelect Yes Yes
rich:inplaceInput Yes Yes
rich:inputNumberSpinner Yes Yes
rich:inputNumberSlider Yes Yes
rich:suggestionBox Yes Yes
rich:listShuttle Yes Yes
rich:orderingList Yes Yes
rich:pickList Yes Yes

17.11. Sending and Receiving Events

The bridge considers a portlet event a model event, which means the event is targeted to the applications data model, not its view.
Because JSF events primarily concern its view, the bridge processes the portlet events manually. Provisions are made to ensure that any model changes resulting from processing the event are updated in the view.
Because event payloads are arbitrarily complex however, the manual data processing is left primarily to the portlet application to support.

17.11.1. Sending Events

The following configuration uses information relating to a generic Booking Event portlet. The concepts contained within each example can be applied to other event sending scenarios.

Procedure 17.1. Sending Events

  1. Define the autoDispatchEvents <init-param> in portlet.xml to specify the GenericFacesPortlet should override event handling and dispatch all events to the bridge.

    Important

    If the application is written entirely in JSF (as opposed to a mix of view technologies), this <init-param> must be set to true.
    <init-param>
      <name>javax.portlet.faces.autoDispatchEvents</name>
      <value>true</value>
    </init-param>
  2. Define the <supported-publishing-event> in portlet.xml to enable the portlet to publish an event to the portal.
    <supported-publishing-event>
      <qname xmlns:jbp="urn:jboss:portal:samples:event">jbp:BookingEvent</qname>
    </supported-publishing-event>
  3. Define the <event-definition> directive in portlet.xml to specify how the event namespace (<qname>) is linked to an actual type within the application.
    <event-definition>
      <qname xmlns:jbp="urn:jboss:portal:samples:event">jbp:BookingEvent</qname>
      <value-type>org.jboss.example.booking.BookingEvent</value-type>
    </event-definition>

Example 17.1. Event type example

To publish an event, define an event type that represents the actual object that will be attached to the event.
The type requires the @XmlRootElement annotation to allow the event to be serialized into a JAXB object for publishing.
@XmlRootElement
public class BookingEvent implements Serializable {
 
  private String id;
  public static final QName QNAME = new QName("urn:jboss:portal:samples:event", "BookingEvent");
 
  public BookingEvent(String id) {
      this.id = id;
  }
 
  public String getId() {
    return id;
  }
}

Example 17.2. Dispatch event example

To dispatch an event to other portlets within the portal, one approach is to use a bean method triggered by an action.
Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
  if (response instanceof StateAwareResponse) {
    String id = "an id";
    StateAwareResponse stateResponse = (StateAwareResponse) response;
    stateResponse.setEvent(BookingEvent.QNAME, new BookingEvent(id));
  }

17.11.2. Receiving Events

The following configuration uses information relating to a generic Booking Event portlet. The concepts contained within each example can be applied to other event receiving scenarios.

Procedure 17.2. 

  1. Define the bridgeEventHandler <init-param> directive in portlet.xml to specify the portlet can receive an event from Portlet Bridge.
    <init-param>
      <name>javax.portlet.faces.bridgeEventHandler</name>
      <value>org.jboss.example.booking.BookingEventHandler</value>
    </init-param>
  2. Define the <supported-processing-event> directive in portal.xml to specify the portlet can also receive an event from the portal.
    <supported-processing-event>
      <qname xmlns:jbp="urn:jboss:portal:samples:event">jbp:BookingEvent</qname>
    </supported-processing-event>
To process a received event within a portlet, specify in the portlet code that the event handler implements the BridgeEventHandler.
public class BookingEventHandler implements BridgeEventHandler {
  public EventNavigationResult handleEvent(FacesContext context, Event event) {
    // Process event payload as appropriate
  }
}

17.11.3. Public Render Parameters

Public Render Parameters (or PRPs) are one of the most powerful and simple Portlet 2.0 features. Several portlets (JSF or otherwise) can share the same PRPs. This feature can be used to present a cohesive UI to the user across all portlets on the page. An example would be using an employee ID to display relative data.
The bridge automates public render parameter processing.
A public render parameter can be mapped to an object's accessor (get/set method) designed to handle a String representation of the value through a Faces ValueExpression.
Unlike Events, PRPs can only be used to set/get a string value, and not an object.
When a new public render parameter value is received in a request, the bridge sets the value by calling the ValueExpression's setValue().
The bridge maps a render parameter to a backing bean using settings in the faces-config.xml and portlet.xml files.
At the end of a request, if the current value of any mapped public render parameter does not match the current incoming value, the bridge sets the new value in an outgoing public render parameter (if feasible in the given phase).

17.11.3.1. PRP portlet configuration

The following configuration uses information relating to a generic Booking Event portlet. The concepts contained within each example can be applied to other PRP portlet configuration.
PRPs must be configured in both the portlet that sends (sets) the PRP, and the portlet that retrieves (gets) the PRP.
<portlet>
<!-- Unnecessary configuration information removed for clarity -->
  <supported-public-render-parameter>hotelName</supported-public-render-parameter>
</portlet>
 
<public-render-parameter>
  <identifier>hotelName</identifier>
  <qname xmlns:j="http://jboss.org/params">j:hotelName</qname>
</public-render-parameter>
In the portlet that retrieves the PRP, a PRP handler must be specified as an <init-param> directive.
<init-param>
  
<name>javax.portlet.faces.bridgePublicRenderParameterHandler</name>
  <value>org.jboss.example.booking.BookingPRPHandler</value>
</init-param>

17.11.3.2. Application configuration

The following configuration uses information relating to a generic Booking Event portlet. The concepts contained within each example can be applied to other PRP portlet configuration.
17.11.3.2.1. Set Parameter
To set the PRP, create a Bean method for the portlet setting the parameter that is triggered from a UI action, and sets the PRP onto the response.
Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
if (response instanceof StateAwareResponse) {
  StateAwareResponse stateResponse = (StateAwareResponse) response;
  stateResponse.setRenderParameter("hotelName", "Name of Hotel");
}
17.11.3.2.2. Retrieve parameter
The retrieving portlet requires a Bean with a getter/setter for the parameter, to enable Portlet Bridge to set the parameter.
public class BookingPRP {
  private String hotelName;
 
  public String getHotelName() {
    return hotelName;
  }
 
  public void setHotelName(String hotelName) {
    this.hotelName = hotelName;
  }
}
To set the value on the bean, the Bridge needs to be informed which PRP to retrieve and which Bean to set it on.
If the getter/setter Bean is defined with a EL name of bookingPRP, the following configuration is required in faces-config.xml.
<application>
    <application-extension>
       <bridge:public-parameter-mappings>
          <bridge:public-parameter-mapping>
             <parameter>[bookingMapPortlet]:[hotelName]</parameter>
             <model-el>#{bookingPRP.hotelName}</model-el>
           </bridge:public-parameter-mapping>
        </bridge:public-parameter-mappings>
     </application-extension>
</application>

Important

Specifying the portlet name restricts model updates to the specified portlet only. If the portlet name is omitted and only the render parameter specified, every portlet within the web application that supports that render parameter will have its model updated.
[bookingMapPortlet]
The arbitrary name of the portlet, as set in portlet.xml
[hotelName]
The arbitrary name of the render parameter.
The handler specified in the portlet.xml requires a bean to process the updates to the model, as a result of the Public Render Parameter.
public class BookingPRPHandler implements BridgePublicRenderParameterHandler {
  public void processUpdates(FacesContext context) {
    ELContext elContext = context.getELContext();
    BookingPRPBean bean = (BookingPRPBean) elContext.getELResolver().getValue(elContext, null, "bookingPRP");
 
    if(null != bean) {
      System.out.println("******processUpdates from BookingPRPHandler: " + bean.getHotelName());
    }
  }
}

17.11.4. Portlet Session

To share data with other portlets within the same portlet application, use name/value pairs within the PortletSession.
Object objSession = FacesContext.getCurrentInstance().getExternalContext().getSession(false);
  try {
    if (objSession instanceof PortletSession) {
      PortletSession portalSession = (PortletSession)objSession;
      portalSession.setAttribute("your parameter name", "parameter value", PortletSession.APPLICATION_SCOPE);
    }
  }
In the JSP or Facelets page, the value string can be retrieved using the following code.
#{httpSessionScope['your parameter name']}

17.12. Resource serving

When using resources from a JSF portlet, it is important to ensure that they are accessed in a way that allows JBoss Portlet Bridge to construct an appropriate Portal URL to the resource being requested.
The correct way to reference a resource is to locate it within the web application, in the / resources directory. This placement allows the resource to be retrieved using JSF2 resource handling.
#{resource['/stylesheet.css']}

Important

#{resource} is particularly important when using @import to retrieve content within CSS files.
In this instance, stylesheet.css would be present in the root of the /resources directory, because it does not specify a resource library. For resources that do specify a library, these must be placed in the library sub-directory.

17.13. Serving JSF Resources in a Portlet

The bridge deals with portlet served resources in one of two ways:
  • If the request is for a non-JSF resource, the bridge handles the request by acquiring a request dispatcher and forwarding the request to the named resource.
  • If the request is for a JSF resource, the bridge runs the full JSF life-cycle ensuring that data is processed and the resource (markup) is rendered.

17.13.1. Expression Language Reference

Some examples demonstrating how to use EL and a simple bean are provided in the ResourceBean.java file of the Richfaces Portlet example.
Additional information about EL is located in Section 17.14.14, “Using Provided EL Variables”
package org.richfaces.demo.common;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

import javax.faces.bean.ManagedBean;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.portlet.MimeResponse;
import javax.portlet.ResourceURL;

/**
 * @author <a href="http://community.jboss.org/people/kenfinni">Ken Finnigan</a>
 */
@ManagedBean(name = "portletRes")
public class PortletResource implements Map<String, String> {

    @Override
    public void clear() {
    }

    @Override
    public boolean containsKey(Object arg0) {
        return true;
    }

    @Override
    public boolean containsValue(Object arg0) {
        return true;
    }

    @Override
    public Set<java.util.Map.Entry<String, String>> entrySet() {
        return Collections.emptySet();
    }

    @Override
    public String get(Object resourceKey) {
        FacesContext context = FacesContext.getCurrentInstance();
        String resourceUrl = null;

        if (null != resourceKey) {
            ExternalContext extCon = context.getExternalContext();
            MimeResponse response = (MimeResponse) extCon.getResponse();
            ResourceURL resUrl = response.createResourceURL();
            resUrl.setResourceID(resourceKey.toString());
            resourceUrl = resUrl.toString();
        }
        return resourceUrl;
    }

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public Set<String> keySet() {
        return Collections.emptySet();
    }

    @Override
    public String put(String arg0, String arg1) {
        return null;
    }

    @Override
    public void putAll(Map<? extends String, ? extends String> arg0) {
    }

    @Override
    public String remove(Object arg0) {
        return null;
    }

    @Override
    public int size() {
        return 0;
    }

    @Override
    public Collection<String> values() {
        return Collections.emptySet();
    }

}

17.13.2. Configuration

When you have the normal "/images", "/styles" and other resource folders in your web application, you can use the following EL expression to serve them in your JSF application.
#{resource['/img/the-path-to-my-image.png']}
Copy the ResourceBean.java code described in Section 17.13.1, “Expression Language Reference”, and add an entry to the faces-config.xml for the bean.
<managed-bean>
   <managed-bean-name>resource</managed-bean-name>
   <managed-bean-class>org.richfaces.demo.common.ResourceBean</managed-bean-class>
   <managed-bean-scope>application</managed-bean-scope>
</managed-bean>

17.14. Developing Portlets with the Bridge

The following information demonstrates common development tasks described by the 329 specification.

17.14.1.  Implementing Portlet Bridge

Portlet Bridge supports JSF2 and RichFaces 4 portlets. There are subtle differences in the way these portlets require Portlet Bridge to be implemented.
JSF2 and RichFaces 4 portlets must be JSF2 portlets for this version of the Portlet Bridge to function as expected. There are no other specific requirements.

17.14.1.1. Artifact Dependencies

A JSF2 portlet can inherit the correct dependencies by configuring the Maven repository. This will include all dependencies for all Portlet Bridge JSF2 scenarios.

Procedure 17.3. Artifact Dependencies

  1. Add the following pom.xml dependencies to inherit the correct JSF2 artifacts.
    <dependency>
        <groupId>org.jboss.portletbridge</groupId>
        <artifactId>portletbridge-api</artifactId>
        <version>3.1.2.Final</version>
    </dependency>
    <dependency>
        <groupId>org.jboss.portletbridge</groupId>
        <artifactId>portletbridge-impl</artifactId>
        <version>3.1.2.Final</version>
        <scope>runtime</scope>
    </dependency>
  2. For RichFaces 4 portlets, add the following additional dependency to inherit the additional RichFaces dependencies.
    <dependency>
        <groupId>org.jboss.portletbridge</groupId>
        <artifactId>portletbridge-extension-richfaces</artifactId>
        <version>3.1.2.Final</version>
        <scope>runtime</scope>
    </dependency>

17.14.1.2. Depchain Dependencies

It is possible to reduce the pom.xml configuration to a single dependency, using the Portlet Bridge Depchain pom.xml configuration.

Procedure 17.4. JSF2 Depchain Dependencies

  1. Add the following dependencies to inherit the correct artifacts using the pom.xml Depchain.
    <dependency>
        <groupId>org.jboss.portletbridge</groupId>
        <artifactId>jsf2-depchain</artifactId>
        <version>3.1.2.Final</version>
        <type>pom</type>
    </dependency>
  2. For RichFaces 4 portlets, add the following additional dependency to inherit the correct artifacts using the pom.xml Depchain.
    <dependency>
        <groupId>org.jboss.portletbridge</groupId>
        <artifactId>jsf2-depchain</artifactId>
        <version>3.1.2.Final</version>
        <type>pom</type>
    </dependency>
    <dependency>
        <groupId>org.jboss.portletbridge</groupId>
        <artifactId>richfaces4-depchain</artifactId>
        <version>3.1.2.Final</version>
        <type>pom</type>
    </dependency>

17.14.1.3. Deploying to the portal

JBoss Portal Platform injects Portlet Bridge functionality into all JSF2 portlets by default, therefore portlets developed for the portal do not need to include the Portlet Bridge as part of the application.
If the Portlet Bridge API is included in a JSF2 or RichFaces 4 portlet, the provided scope must be correctly declared.

Procedure 17.5. Setting the Provided Scope for JSF2 and RichFaces Portlets that use Portlet Bridge API

  1. Add the following pom.xml dependencies to declare the JSF2 provided scope.
    <dependency>
        <groupId>org.jboss.portletbridge</groupId>
        <artifactId>portletbridge-api</artifactId>
        <version>3.1.2.Final</version>
        <scope>provided</scope>
    </dependency>
  2. For RichFaces 4 portlets, add the following additional dependency to declare the RichFaces 4 runtime scope.

    Note

    The RichFaces 4 extension is not included in the portal by default.
    <dependency>
        <groupId>org.jboss.portletbridge</groupId>
        <artifactId>portletbridge-extension-richfaces</artifactId>
        <version>portletbridge-extension-richfaces-[VERSION]</version>
        <scope>runtime</scope>
    </dependency>
    Where [ARTIFACT VERSION] is the portletbridge-extension-richfaces version in JPP_HOME/modules/org/jboss/portletbridge/api/main

17.14.1.4. Disable Automatic Portlet Bridge Injection

In some cases, it is appropriate to prevent JBoss Portal Platform from injecting Portlet Bridge into the application. For example, if a different version of Portlet Bridge is required for the application.

Procedure 17.6. Disabling automatic Portlet Bridge injection

  • Add the following pom.xml dependency to prevent JBoss Portal Platform injecting the packaged Portlet Bridge implementation into a portlet application.

    Note

    If a portlet application specifies either WAR_BUNDLES_JSF or JSF_CONFIG_NAME context parameters in the pom.xml, Portlet Bridge will not be automatically injected.
    <context-param>
        <param-name>org.gatein.portletbridge.WAR_BUNDLES_PORTLETBRIDGE</param-name>
        <param-value>true</param-value>
    </context-param>

17.14.2. Portlet tags

Portlet Bridge supports the following tags from section PLT.26 of the Portlet 2.0 Spec (JSR 286):
  • actionURL
  • renderURL
  • resourceURL
  • namespace
  • param
  • property
When using the tag library, the following namespace needs to be added to the facelet page of the application.
xmlns:pbr="http://jboss.org/portletbridge"

Example 17.3. renderURL example

Generate a Portlet Render URL that enables switching between Portlet modes.
<pbr:renderURL var="renderUrl" portletMode="edit">
</pbr:renderURL>
<h:outputLink value="#{renderUrl}">Edit Mode</h:outputLink>

Example 17.4. namespace example

This portlet tag is particularly useful for prefixing JavaScript functions within a given portlet. This ensures that the JavaScript function name does not clash with a name from another portlet, or from the same portlet displayed on the same page multiple times.
An example for defining a JavaScript method is:
<script type='text/javascript'>
  function <pbr:namespace />DoSomething() {
  }
</script>

17.14.3. Excluding Attributes from the Bridge Request Scope

When your application uses request attributes on a per request basis and you do not want that particular attribute to be managed in the extended bridge request scope, you must use the following configuration in your faces-config.xml.
In the code sample below, any attribute namespaced as foo.bar, or any attribute beginning with foo.baz.(wild-card) will be excluded from the bridge request scope and only be used for that application's request.
<application>
	<application-extension>
		<bridge:excluded-attributes>
			<bridge:excluded-attribute>foo.bar</bridge:excluded-attribute>
				<bridge:excluded-attribute>foo.baz.*</bridge:excluded-attribute>
			</bridge:excluded-attributes>
	</application-extension>
</application>

17.14.4. Prevent Resources Being Added to Portal Page Head

Portlet Bridge will add any JSF Resources for the portlet into the <head> section of the portal page the portlet is on, providing the portlet container supports marking up the head of a page.
To prevent Portlet Bridge from adding resources into the <head> section of the portal page, override the default behavior by declaring the following directive in the portlet's web.xml file.
<context-param>
  <param-name>org.jboss.portletbridge.markupHead.enabled</param-name>
  <param-value>false</param-value>
</context-param>

17.14.5. JSF facelet view

When creating a JSF Facelet view document, it is common to see content wrapped with the following code.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets" >
<!-- Unnecessary content removed for clarity -->
</html>
Because a single portlet only reflects a potentially small portion of the HTML markup for a page, a JSF portlet returning the above markup for each portlet can be distracting, and potentially problematic.
The recommended way to wrap the content of a JSF Facelet view document for a portlet is to use the <f:view> element. Using <f:view> results in the relevant portlet mark-up being returned to the page as required.
<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets" >
<!-- Unnecessary content removed for clarity -->
</f:view>

Note

While not specifically relevant to portlet, it is important to include <h:head> and <h:body> elements to allow JSF to process the facelet correctly.

17.14.6. Error handling

To display error pages for specific exceptions in a JSF portlet, the following code is required in the web.xml file.
<error-page>
  <exception-type>javax.servlet.ServletException</exception-type>
  <location>/faces/error.xhtml</location>
</error-page>
<error-page>
  <exception-type>javax.faces.application.ViewExpiredException</exception-type>
  <location>/faces/error.xhtml</location>
</error-page>
The above error page definitions are appropriate for a JSF portlet that has a FacesServlet mapping such as /faces/.
If the FacesServlet mapping was **.jsf, the location would be /error, /error.jsf or /error.xhtml.
There is no requirement when using FacesServlet suffix mapping to append an extension, the name of the view is all that is required for the page to be found

Note

Remember to add a link from any error page to the normal flow of the JSF application, otherwise the same error page will be constantly displayed.

17.14.7. Switching Portlet Modes

A PortletMode represents a distinct render path within an application. There are three standard modes: view, edit, and help.
The bridge's ExternalContext.encodeActionURL recognizes the query string parameter javax.portlet.faces.PortletMode and uses this parameter's value to set the portlet mode on the underlying portlet actionURL or response. Once processed it then removes this parameter from the query string.

Example 17.5. /edit.xhtml navigation rule

This navigation rule causes one to render the /edit.xhtml viewId in the portlet edit mode.
<navigation-rule>
  <from-view-id>/register.xhtml</from-view-id>
  <navigation-case>
    <from-outcome>edit</from-outcome>
    <to-view-id>/edit.xhtml?javax.portlet.faces.PortletMode=edit</to-view-id>
  </navigation-case>
</navigation-rule>

17.14.8. Navigating to a mode's last viewId

By default a mode change will start in the mode's default view without any (prior) existing state. One common portlet pattern when returning to a mode left after entering another mode (e.g.. view -> edit -> view) is to return to the last view (and state) of this original mode.
The bridge will explicitly encode the necessary information so that when returning to a prior mode it can target the appropriate view and restore the appropriate state.
The session attributes maintained by the bridge are intended to be used by developers to navigate back from a mode to the last location and state of a prior mode. As such, a developer needs to describe a dynamic navigation: "From view X return to the last view of mode Y".
This is most easily expressed via an EL expression. For example:

Example 17.6. /register.xhtml viewId navigation rule

This navigation rule causes one to render the /edit.xhtml viewId in the portlet edit mode.
<navigation-rule>
  <from-view-id>/edit.xhtml*</from-view-id>
  <navigation-case>
    <from-outcome>view</from-outcome>
    <to-view-id>#{sessionScope['javax.portlet.faces.viewIdHistory.view']}</to-view-id>
  </navigation-case>
</navigation-rule>

17.14.9. Using Wildcards to Identify the Rule Target

When using values from session scoped attributes, or viewIds which may contain query string parameters, it may be necessary to use the wild-card syntax when identifying the rule target.
In Section 17.14.8, “Navigating to a mode's last viewId” the <to-view-id> expression returns a viewId of the form
/viewId?javax.portlet.faces.PortletMode=view&....
Without wild-carding, when a subsequent navigation occurs from this new view, the navigation rules would not resolve because there would not be an exact match.
Similarly, the edit.jspx <from-view-id> is wild-carded because there are navigation rules that target it that use a query string:
<to-view-id> /edit.jspx?javax.portlet.faces.PortletMode=edit </to-view-id>
Developers are encouraged to use such wild-carding to ensure they execute properly in the broadest set of bridge implementations.

17.14.10. Clearing The View History When Changing Portlet Modes

By default the bridge remembers the view history when you switch to a different portlet mode (like "Help" or "Edit"). You can use the following parameter in your portlet.xml to use the default viewId each time you switch modes.
<init-param>
	<name>javax.portlet.faces.extension.resetModeViewId</name>
	<value>true</value>
</init-param>

17.14.11. Communication Between Your Portlets

There are four different ways to send messages, events, and parameters between portlets which are contained in different ears/wars or contained in the same war.
Having two portlets in the same war or having them separated does not affect the Portlet Container because each portlet has a different HttpSession.
The recommended way to share a parameter or event payload between two or more portlets with the Portlet 2.0 specification are the Section 17.11.3, “Public Render Parameters” and Section 17.11, “Sending and Receiving Events” mechanisms.
This allows you to decouple your application from surgically managing objects in the PortletSession.APPLICATION_SCOPE.
However, if these do not meet your use case or you have a different strategy, you can use one of the following methods.

17.14.11.1. Storing Components in PortletSession.APPLICATION_SCOPE

Sometimes it is beneficial to store your Seam components in the portlet APPLICATION_SCOPE.
By default, these objects are stored in the PORTLET_SCOPE but with the annotation below, this class can be pulled out of the PortletSession and its values used in other portlets across different Seam applications.
@PortletScope(PortletScope.ScopeType.APPLICATION_SCOPE)
Then you would pull the stateful object from the session:
YourSessionClass yourSessionClass = (YourSessionClass)getRenderRequest().getAttribute("javax.portlet.p./default/seamproject/seamprojectPortletWindow?textHolder");
This method is demonstrated in this video: Lesson 2: Portlet 1.0 Advanced Seam and RichFaces

17.14.11.2. Using the PortletSession

If you only need to access the PortletSession to share a parameter or value across multiple portlets, you can use the following:
Object objSession = FacesContext.getCurrentInstance().getExternalContext().getSession(false);
try
{
	if (objSession instanceof PortletSession)
	{
		PortletSession portalSession = (PortletSession)objSession;
		portalSession.setAttribute("your parameter name","parameter value",PortletSession.APPLICATION_SCOPE);
		...
Then, in your JSP or Facelets page, you can use:
#{httpSessionScope['your parameter name']}

17.14.12. Linking to a Facelets page within the same portlet

To link any JSF/Facelets page within a portlet web application, use the following code.
<h:outputLink value="#{facesContext.externalContext.requestContextPath}/home.xhtml">
	<f:param name="javax.portlet.faces.ViewLink" value="true"/>
	navigate to the test page
</h:outputLink>

17.14.13. Redirecting to an external page or resource

To link to a non JSF view, jboss.org for example, you can use the following code.
<h:commandLink actionListener="#{yourBean.yourListenr}">
	<f:param name="javax.portlet.faces.DirectLink" value="true"/>
	navigate to the test page
</h:commandLink>
In the backing bean, a redirect() must be called.
public class YourBean {
  public void yourListener() {
    FacesContext.getCurrentInstance().getExternalContext().redirect("http://www.jboss.org");
  }
}

17.14.14. Using Provided EL Variables

All EL variables found in the JSR-329 (Portlet 2.0) specification are available in the JBoss Portlet Bridge.

Table 17.2. 

portalConfig
Object of type javax.portlet.PortletConfig
actionRequest
Object of type
javax.portlet.ActionRequest
(only accessible when processing an ActionRequest)
actionResponse
Object of type
javax.portlet.ActionResponse
(only accessible when processing an ActionResponse)
eventRequest
Object of type
javax.portlet.EventRequest
(only accessible when processing an EventRequest)
eventResponse
Object of type
javax.portlet.EventResponse
(only accessible when processing an EventRequest)
renderRequest
Object of type
javax.portlet.RenderRequest
(only accessible when processing an RenderResponse)
renderResponse
Object of type
javax.portlet.RenderResponse
(only accessible when processing an RenderResponse)
resourceRequest
Object of type
javax.portlet.ResourceRequest
(only accessible when processing an ResourceRequest)
resourceResponse
Object of type
javax.portlet.ResourceResponse
(only accessible when processing an ResourceResponse)
portletSession
Current PortletSession object
portletSessionScope
Map of PortletSession attributes in PORTLET_SCOPE. JSP Expression returns immutable Map, but Faces Expression returns mutable Map.
httpSessionScope
Mutable Map of PortletSession attributes in APPLICATION_SCOPE
portletPreferences
Current PortletPreferences object
portletPreferencesValues
Immutable Map containing entries equivalent to PortletPreferences.getMap()
mutablePortletPreferencesValues
Mutable Map of type Map<String, javax.portlet.faces.preference.Preference>. This EL variable provides read/write access to each portlet preference.

Example 17.7. 

This code allows you to edit the portlet preferences on the UI
<h:form>
	<h:inputText id="pref" required="true" value="#{mutablePortletPreferencesValues['userName'].value}" />
	<h:commandButton actionListener="#{myBean.savePref}" value="Save Preferences" />
</h:form>
In the backing bean, call the PortletPreferences.store() method.
Object request = FacesContext.getCurrentInstance().getExternalContext().getRequest();
PortletRequest portletRequest = (PortletRequest)request;
if (request instanceof PortletRequest) {
	try {
		PortletPreferences portletPreferences = portletRequest.getPreferences();
		portletPreferences.store();

Part III. Authentication and Authorization

Table of Contents

18. Introduction to Authentication and Authorization
18.1. Authentication Overview
18.2. Login Modules
18.2.1. Existing Login Modules
18.2.2. Creating Your Own Login Module
18.2.3. Authenticator and RolesExtractor
18.3. Different Authentication Workflows
18.3.1. RememberMe Authentication
18.3.2. Authorization Overview
19. Password Encryption
19.1. Hashing and Salting of Passwords in PicketLink IDM
19.1.1. Choosing CredentialEncoder Implementation
19.2. Password Encryption of Rememberme Passwords
20. Predefined User Configuration
20.1. Plug-in for Adding Users, Groups and Membership Types
20.2. Membership Types
20.3. Groups
20.4. Users
20.5. Plug-in for Monitoring User Creation
21. Authentication Token Configuration
21.1. The Token Service
21.2. Implementing the Token Service API
21.3. Configuring Token Services
22. PicketLink IDM Integration
22.1. Configuration Files
22.1.1. PicketlinkIDMServiceImpl
22.1.2. PicketlinkIDMOrganizationServiceImpl
23. Organization API
24. Accessing User Profile
25. Create Users and Groups without Organization API
25.1. Enable Initializer
25.2. Operations
25.3. Using configuration directives
25.4. Using JMX Console
25.5. Using REST Interface
26. Single Sign-On
26.1. Overview and Configuration Assumptions
26.2. Central Authentication Service (CAS)
26.2.1. Authentication Process
26.2.2. Logout Process
26.2.3. CAS Configuration Overview
26.2.4. Modifying the CAS server
26.2.5. Install Apache Tomcat Server
26.2.6. Modifying the Portal
26.2.7. Build and Deploy the CAS
26.3. Java Open Single Sign-On (JOSSO)
26.3.1. Authentication Process
26.3.2. JOSSO 1.8
26.3.3. JOSSO 2.2
26.4. OpenAM
26.4.1. Login and Logout Workflow
26.4.2. Obtaining OpenAM
26.4.3. OpenAM Server Setup
26.4.4. JBoss Portal Platform Setup as OpenAM Client
26.4.5. Cross-domain Authentication with OpenAM
26.5. Simple and Protected GSSAPI Negotiation Mechanism (SPNEGO)
26.5.1. SPNEGO Server Configuration
26.5.2. Client Configuration
26.5.3. JBoss Portal Platform Configuration
26.5.4. SPNEGO Configuration Testing
26.5.5. Additional Configuration
26.6. Single Sign-On in a Cluster
26.6.1. Default Configuration
26.6.2. Clustered Single Sign-On in a Shared DNS Domain
26.6.3. Reauthentication
27. LDAP Integration
27.1. LDAP in Read-only Mode
27.2. LDAP as Default Store
27.3. Examples
28. Security Assertion Markup Language (SAML2)
28.1. What is SAML2
28.2. What is an Assertion
28.3. What is an Identity Provider (IDP)
28.4. What is a Service Provider (SP)
28.5. SAML2 Authentication Overview
28.6. The platform as SAML2 SP and SAML2 IDP
28.7. Disable SAML2 Single logout
28.8. Implementing Keystores
28.9. Setup with Picketlink IDP using REST callback
28.10. Integration with Salesforce and Google Apps
28.10.1. Scenario One
28.10.2. Scenario Two
28.10.3. Scenario Three
28.10.4. Tips and Tricks

Chapter 18. Introduction to Authentication and Authorization

18.1. Authentication Overview

Authentication in JBoss Portal Platform is based on JAAS and by default it is a standard J2EE FORM based authentication.
JBoss Portal Platform supports the following authentication methods:
Authentication workflow consists of HTTP requests and redirects which include handshakes. Currently only Servlet 3.0 containers are supported, so authentication is triggered programmatically from Servlet API.
In JPP_DIST/gatein/gatein.ear/portal.war/WEB-INF/web.xml, authentication is triggered by accessing a secured URL /dologin:
<security-constraint>
  <web-resource-collection>
    <web-resource-name>user authentication</web-resource-name>
      <url-pattern>/dologin</url-pattern>
        <http-method>POST</http-method>
        <http-method>GET</http-method>
    </web-resource-collection>
    <auth-constraint>
      <role-name>users</role-name>
    </auth-constraint>
    <user-data-constraint>
      <transport-guarantee>NONE</transport-guarantee>
    </user-data-constraint>
</security-constraint>
This means that access to URLs (such as http://localhost:8080/portal/dologin) will directly trigger J2EE authentication in the case that the user is not already logged in.
Access to this URL means that the user needs to be in the JAAS group users, otherwise they can authenticate but will receive an HTTP error: 403 Forbidden.
In the next part of the file we can see that authentication is FORM based and it starts by redirection to /login URL, which is mapped to LoginServlet.
<login-config>
  <auth-method>FORM</auth-method>
  <realm-name>gatein-domain</realm-name>
  <form-login-config>
    <form-login-page>/login</form-login-page>
    <form-error-page>/login</form-error-page>
  </form-login-config>
</login-config>
LoginServlet redirects the user to the login page placed in JPP_DIST/gatein/gatein.ear/portal.war/login/jsp/login.jsp.
Default Login Form on the login.jsp Page

Figure 18.1. Default Login Form on the login.jsp Page


Changes to the appearance of this login page can be made in this JSP file. Alternatively you can create an extension and override this page via extension if you don't want to edit it directly. You can also change images or CSS placed in JPP_DIST/gatein/gatein.ear/login/skin.
After a user submits the login form, the LoginServlet will store credentials and trigger WCI login, which delegates to Servlet API (method HttpServletRequest.login(String username, String password) available in Servlet 3.0) and additionally triggers WCI Authentication listeners. Login through Servlet API delegates to JAAS.

18.2. Login Modules

From the WCI servlet API login, the user is redirected to JAAS authentication. JBoss Portal Platform uses its own security domain (gatein-domain) with a set of predefined login modules. Login module configuration for gatein-domain is contained in the JPP_HOME/standalone/configuration/standalone.xml file.
Below is the default login modules stack:
<security-domain name="gatein-domain" cache-type="default">
  <authentication>
    <login-module code="org.gatein.sso.integration.SSODelegateLoginModule" flag="required">
      <module-option name="enabled" value="${gatein.sso.login.module.enabled}" />
      <module-option name="delegateClassName" value="${gatein.sso.login.module.class}" />
      <module-option name="portalContainerName" value="portal" />
      <module-option name="realmName" value="gatein-domain" />
      <module-option name="password-stacking" value="useFirstPass" />
    </login-module>
    <login-module code="org.exoplatform.services.security.j2ee.JBossAS7LoginModule" flag="required">
      <module-option name="portalContainerName" value="portal"/>
      <module-option name="realmName" value="gatein-domain"/>
    </login-module>
  </authentication>
</security-domain>
New login modules can be added or the stack completely replaced with custom modules.
Authentication starts with the login method of each login module being invoked. After all login methods are invoked, the authentication is continued by invoking the commit method on each login module. Both login and commit methods can throw LoginException. If it happens, then the whole authentication ends unsuccessfully, which in turn invokes the abort method on each login module. By returning "false" from the login method, you can ensure that the login module is ignored. This is not specific to JBoss Portal Platform but it is generic to JAAS. See http://docs.oracle.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html for more information about login modules in general.

18.2.1. Existing Login Modules

Here is a brief description of existing login modules:

Modules

SSODelegateLoginModule
It is useful only if SSO authentication is enabled (disabled by default). SSO authentication can be enabled through properties in configuration.properties file and it delegates the work to another real login module for SSO integration. If SSO is disabled, SSODelegateLoginModule is simply ignored. See Section 26.2, “ Central Authentication Service (CAS)” properties for more details. If SSO is used and SSO authentication succeeds, the special Identity object is created and saved into shared state map (Map, which is shared between all login modules), so that this Identity object can be used by JBossAS7LoginModule or other login modules in the JAAS chain.
JBossAS7LoginModule
The most important login module, which is normally used to perform the whole authentication by itself. First it checks if an Identity object has been already created and saved into the sharedState map by previous login modules (like SSODelegateLoginModule, CustomMembershipLoginModule or SharedStateLoginModule). If not, it triggers real authentication of the user with usage of the Authenticator interface and it will use Authentication.validateUser(Credential[] credentials) which performs real authentication of username and password against OrganizationService and the portal identity database. See Section 18.2.3, “Authenticator and RolesExtractor” for details about Authenticator and about Identity objects.
In the JBossAS7LoginModule.commit method, the Identity object is registered to IdentityRegistry, which will be used later for authorization. Also some JAAS principals (UserPrincipal and RolesPrincipal) are assigned to our authenticated Subject. This is needed for JBoss Enterprise Application server, so that it can properly recognize the name of the logged user and its roles on an application server level.
Some other login modules are not active by default, but can be added if needed.
CustomMembershipLoginModule
Special login module, which can be used to add a user to existing groups during a successful login of this user. The group name is configurable and by default is /platform/users group. This login module is not used because in normal environment, users are already in the /platform/users group. It is useful only for some special setups like read-only LDAP, where groups of an LDAP user are taken from the LDAP tree so that users may not be in the /platform/users group, which is needed for successful authorization.
Note that the CustomMembershipLoginModule cannot be the first login module in the LoginModule chain because it assumes that the Identity object is already available in the shared state. So there are two possible cases. For a non-SSO case, you may need to chain this login module with other login modules, which can be used to establish Identity and add it into shared state. Those login modules can be InitSharedStateLoginModule and SharedStateLoginModule. For an SSO case, you can add CustomMembershipLoginModule between SSODelegateLoginModule and JBossAS7LoginModule.
InitSharedStateLoginModule
It can read credentials from JAAS callback and add them into shared state. It's intended to be used in JAAS chain before SharedStateLoginModule
SharedStateLoginModule
It reads the username and password from sharedState map from attributes javax.security.auth.login.name and javax.security.auth.login.password. Then it calls Authenticator.validateUser(Credential[] credentials), to perform authentication of username and password against OrganizationService and portal identity database. The result of successful authentication is an Identity object, which is saved to the sharedState map.
Configuration example with CustomMembershipLoginModule and disabled SSO:
         <login-module code="org.exoplatform.web.security.InitSharedStateLoginModule" flag="required">
            <module-option name="portalContainerName" value="portal"/>
            <module-option name="realmName" value="gatein-domain"/>
         </login-module>
         <login-module code="org.exoplatform.services.security.jaas.SharedStateLoginModule" flag="required">
            <module-option name="portalContainerName" value="portal"/>
            <module-option name="realmName" value="gatein-domain"/>
         </login-module>
         <login-module code="org.exoplatform.services.organization.idm.CustomMembershipLoginModule" flag="required">
           <module-option name="portalContainerName" value="portal"/>
           <module-option name="realmName" value="gatein-domain"/>
           <module-option name="membershipType" value="member" />
           <module-option name="groupId" value="/platform/users" />
         </login-module>
         <login-module code="org.exoplatform.services.security.j2ee.JBossAS7LoginModule" flag="required">
           <module-option name="portalContainerName" value="portal"/>
           <module-option name="realmName" value="gatein-domain"/>
         </login-module>
Configuration example with enabled SSO:
       <login-module code="org.gatein.sso.integration.SSODelegateLoginModule" flag="required">
        <module-option name="enabled" value="${gatein.sso.login.module.enabled}" />
        <module-option name="delegateClassName" value="${gatein.sso.login.module.class}" />
        <module-option name="portalContainerName" value="portal" />
        <module-option name="realmName" value="gatein-domain" />
        <module-option name="password-stacking" value="useFirstPass" />
      </login-module>
      <login-module code="org.exoplatform.services.organization.idm.CustomMembershipLoginModule" flag="required">
        <module-option name="portalContainerName" value="portal"/>
        <module-option name="realmName" value="gatein-domain"/>
        <module-option name="membershipType" value="member" />
        <module-option name="groupId" value="/platform/users" />
      </login-module>
      <login-module code="org.exoplatform.services.security.j2ee.JBossAS7LoginModule" flag="required">
        <module-option name="portalContainerName" value="portal"/>
        <module-option name="realmName" value="gatein-domain"/>
      </login-module>

18.2.2. Creating Your Own Login Module

Before creating your own login module, it is recommended that you study the source code of existing login modules to better understand the JAAS authentication process. You need to have good knowledge so that you can properly decide where your login module should be placed and if you need to replace some existing login modules or simply attach your own module to the existing chain.
There are two levels of authentication and the overall result of JAAS authentication should properly handle both these cases:
  • Authentication on application server level
  • Authentication on JBoss Portal Platform level
Authentication on Application Server Level
Application server needs to properly recognize that the user is successfully logged and it has their JAAS roles assigned. Unfortunately this part is not standardized and is specific for each aaplication server. For example in JBoss Enterprise Application Platform, you need to ensure that JAAS Subject has an assigned UserPrincipal with username and also a RolesPrincipal, which contains a list of JAAS roles. This part is actually done in JBossAS7LoginModule.commit(). In Tomcat, for example, this flow is little different, which means Tomcat has it is own TomcatLoginModule.
After successful authentication, user needs to be at least in JAAS role users because this role is declared in web.xml as you saw above. JAAS roles are extracted by special algorithm from JBoss Portal Platform memberships. See below in section with RolesExtractor.
Authentication on JBoss Portal Platform Level
The login process needs to create a special object org.exoplatform.services.security.Identity and register this object into JBoss Portal Platform component IdentityRegistry. The Identity object should encapsulate the username of the authenticated user, memberships of this user and JAAS roles. The Identity object can be easily created with interface Authenticator as shown below.

18.2.3. Authenticator and RolesExtractor

Authenticator is an important component in the authentication process. The interface org.exoplatform.services.security.Authenticator looks like this:

public interface Authenticator
{

   /**
    * Authenticate user and return userId.
    *
    * @param credentials - list of users credentials (such as name/password, X509
    *          certificate etc)
    * @return userId
    */
   String validateUser(Credential[] credentials) throws LoginException, Exception;

   /**
    * @param userId.
    * @return Identity
    */
   Identity createIdentity(String userId) throws Exception;

}

Method validateUser is used to authenticate the given credentials (username and password). It returns a username if the credentials are correct, otherwise a LoginException is thrown.
Method createIdentity is used to create an instance of object org.exoplatform.services.security.Identity, which encapsulates all important information about a single user:
  • Username
  • Set of Memberships (MembershipEntry objects) associated with the user. Membership is an object, which contains information about membershipType (manager, member, validator, ... ) and about group (/platform/users, /platform/administrators, /partners, /organization/management/executiveBoard, ... ).
  • Set of Strings with JAAS roles of given user. JAAS roles are simple Strings, which are mapped from MembershipEntry objects. There is another special component org.exoplatform.services.security.RolesExtractor, which is used to map JAAS roles from MembershipEntry objects.
RolesExtractor interface looks like this:
              
public interface RolesExtractor
{

   /**
    * Extracts J2EE roles from userId and|or groups the user belongs to both
    * parameters may be null
    *
    * @param userId
    * @param memberships
    */
   Set<String> extractRoles(String userId, Set<MembershipEntry> memberships);
}

Default implementation DefaultRolesExtractorImpl is based on a special algorithm, which uses the name of the role from the root of the group (for example for role "/organization/management/something" we have JAAS role "organization"). The only exception is the "platform" group where we use second level as the name of the group. For example from group "/platform/users" we have JAAS role "users".
For example, the user root has the following memberships:
  • member:/platform/users
  • manager:/platform/administrators
  • validator:/platform/managers
  • member:/partners
  • member:/customers/acme
  • member:/organization/management/board
In this case we will have JAAS roles: users, administrators, managers, partners, customers, organization.
The default implementation of Authenticator is OrganizationAuthenticatorImpl, which is implementation based on OrganizationService. See Chapter 23, Organization API .
You can override the default implementation of the mentioned Authenticator and RolesExtractor interfaces if the default behavior is not suitable for your needs.

18.3. Different Authentication Workflows

18.3.1. RememberMe Authentication

In the default login dialog is the
Remember my login
checkbox, which persist a user's login on their workstation. The default validity period of the RememberMe cookie is one day, so a user can be logged for a whole day before needing to re-authenticate. The validity period is configurable.

18.3.1.1. How It Works

  • User checks the checkbox RememberMe on the login screen of JBoss Portal Platform, then submits the form.
  • Form data such as username, password and rememberme are sent in an HTTP POST request to the /portal/login URL, with the rememberme parameter set to true.
  • Request is processed by PortalLoginController servlet. The servlet obtains an instance of RemindPasswordTokenService and saves user credentials into JCR. It generates and returns special token (key) for later use, then creates a cookie called RememberMe and uses the returned token as the cookie's value.

18.3.1.2. Re-authentication

  • After some time, the user wants to re-authenticate. It is assumed that his HTTP Session is already expired but his RememberMe cookie is still active.
  • The user sends the HTTP request to some portal pages (for example, http://localhost:8080/portal/classic).
  • There is a special HTTP Filter RememberMeFilter configured in web.xml, which checks the RememberMe cookie and then it retrieves credentials of user from RemindPasswordTokenService. Now filter redirects request to PortalLoginController and authentication process goes in same way as for normal FORM based authentication.

18.3.1.3. RemindPasswordTokenService

This is a special service used during the RememberMe authentication workflow. It is configurable in the file JPP_DIST/gatein/gatein.ear/portal.war/WEB-INF/conf/common/remindpwd-configuration.xml . See Chapter 21, Authentication Token Configuration for more information.
You can encrypt passwords before storing them in JCR. See Chapter 19, Password Encryption for more information.

18.3.2. Authorization Overview

In the previous section, you learned about JAAS authentication and about login modules. So you know the result of authentication, including:
  • JAAS Subject with principals for username (UserPrincipal) and for JAAS roles (RolesPrincipal).
  • Identity object, which encapsulates username, all memberships and all JAAS roles. This Identity object is bound to IdentityRegistry component.
Authorization in JBoss Portal Platform actually happens on two levels:

18.3.2.1. Servlet Container Authorization

First round of authorization is servlet container authorization based on secured URL from web.xml. We saw above in web.xml snippet that secured URL are accessible only for users from role users:
<auth-constraint>
  <role-name>users</role-name>
</auth-constraint>
This actually means that our user needs to be in JBoss Portal Platform role /platform/users (for details see Section 18.2.3, “Authenticator and RolesExtractor”). In other words, if we successfully authenticate but our user is not in group /platform/users, then it means that he is not in JAAS role users, which in turn means that they will have authorization error 403 Forbidden thrown by servlet container. For example in LDAP setup, your users may not be in /platform/users by default, but you can use CustomMembershipLoginModule to fix this problem. For details see Section 18.2, “Login Modules”.
You can change the behavior and possibly add some more auth-constraint elements into web.xml. However this protection of resources based on web.xml is not standard JBoss Portal Platform method and is mentioned here mainly for illustration purposes.

18.3.2.2. Portal level authorization

The second round of authorization is based on the component UserACL (See Chapter 6, Portal Default Permission Configuration). You can declare access and edit permissions for portals, pages and/or portlets. UserACL is then used to check if our user has particular permissions to access or edit specified resources. An important object containing information about the roles of our user is the Identity object created during JAAS authentication.
Authorization on portal level looks like this:
  • The user sends a HTTP request to some URLs in portal;
  • The HTTP request is processed through SetCurrentIdentityFilter, which is declared in JPP_DIST/gatein/gatein.ear/portal.war/WEB-INF/web.xml.
  • SetCurrentIdentityFilter reads username of current user from HttpServletRequest.getRemoteUser(). Then it looks for Identity of this user in IdentityRegistry, where Identity has been saved during authentication. The Identity found is then encapsulated into a ConversationState object and bound into the ThreadLocal variable.
  • UserACL is able to obtain Identity of current user from method UserACL.getIdentity(), which simply calls ConversationState.getCurrent().getIdentity() to find the current Identity bound to ThreadLocal. Now the UserACL has the identity of the user and can perform any security checks.

Chapter 19. Password Encryption

19.1. Hashing and Salting of Passwords in PicketLink IDM

JBoss Portal Platform uses PicketLink IDM framework to store information about identity objects (users/groups/memberships). For better security, PicketLink IDM does not save user passwords into database in plain-text, but it uses CredentialEncoder, which encodes password and saves the encoded form into PicketLink IDM database. Later when the user wants to authenticate, they need to provide their password in plain-text through a web login form. The provided password is then encoded and compared to an encoded password in the PicketLink IDM database. JBoss Portal Platform is then able to authenticate the user based on this comparison. See Chapter 22, PicketLink IDM Integration for more information.
Default implementation of CredentialEncoder is using password hashing with MD5 algorithm and storing those MD5 hashes in database. It does not use any salting of passwords. This is not safest solution, but it is backward compatible with previous releases of JBoss Portal Platform before version 6, where MD5 password hashing was the only possible encoding form. So if you migrate from older release of JBoss Portal Platform, your users will still be able to authenticate.
However if you are starting from a fresh database (no migration from previous JBoss Portal Platform release), you may increase security by using better hashing algorithm and especially by enabling password salting. See below for details.

19.1.1. Choosing CredentialEncoder Implementation

The implementation of CredentialEncoder is configured in file JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/organization/picketlink-idm/picketlink-idm-config.xml. Usually the most important options are the options of the idm_portal realm starting with credentialEncoder prefix. Possible implementations are HashingEncoder, DatabaseReadingSaltEncoder, and FileReadingSaltEncoder.

19.1.1.1. HashingEncoder

This is the default choice. It uses only hashing of passwords with MD5 algorithm without salting. As mentioned previously, it is not the safest solution but it is backward compatible with the previous JBoss Portal Platform releases, so there are no issues with database migration. Configuration looks like this:
<option>
  <name>credentialEncoder.class</name>
  <value>org.picketlink.idm.impl.credential.HashingEncoder</value>
</option>
<option>
  <name>credentialEncoder.hashAlgorithm</name>
  <value>MD5</value>
</option>

19.1.1.2. DatabaseReadingSaltEncoder

This implementation provides salting of password in addition to hashing. The salt is unique for each user, so it is much more complicated to decrypt password via brute force, if an attacker steals encoded passwords from your database. The salt is generated randomly for each user and stored in the PicketLink IDM database as an attribute. Random generation of salt ensures that all users have different salts, so even if two users have the same password, the encoded password in database will be different for them. Here is configuration example, which is using SHA-256 algorithm for hashing (more secure than MD5) and algorithm SHA1PRNG for generation of random salts.
<option>
  <name>credentialEncoder.class</name>
  <value>org.picketlink.idm.impl.credential.DatabaseReadingSaltEncoder</value>
</option>
<option>
  <name>credentialEncoder.hashAlgorithm</name>
  <value>SHA-256</value>
</option>
<option>
  <name>credentialEncoder.secureRandomAlgorithm</name>
  <value>SHA1PRNG</value>
</option>

19.1.1.3. FileReadingSaltEncoder

The FileReadingSaltEncoder also uses hashing and salting, so it is similar to the previous encoder. But it is theoretically even more secure, because salts are not stored in the PicketLink IDM database together with passwords. Salt of each user is generated from saltPrefix and user's username. And saltPrefix is read from some file in your file system. Configuration can look like this:
<option>
  <name>credentialEncoder.class</name>
  <value>org.picketlink.idm.impl.credential.FileReadingSaltEncoder</value>
</option>
<option>
  <name>credentialEncoder.hashAlgorithm</name>
  <value>SHA-256</value>
</option>
<option>
  <name>credentialEncoder.fileLocation</name>
  <value>/salt/mysalt.txt</value>
</option>
Please note that specified file /salt/mysalt.txt must exist and must be readable by user, which executed JBoss Portal Platform. The file should be properly secured so that it is not readable by every user of your operating system. The content of the file can be a random phrase, such as: a4564dac2aasddsklklkajdgnioiow.
The FileReadingSaltEncoderis probably the most secure of all options, but in addition to DatabaseReadingSaltEncoder, you need to set the file with salt.

Important

The CredentialEncoder from above is actually used only for encoding of passwords in PicketLink IDM database. It is not used for LDAP. PicketLink IDM LDAP implementation (LDAPIdentityStore) sends passwords to the LDAP server in plain form, because password encoding is usually provided by LDAP server itself. For example OpenDS 2 is using SHA1 based hashing of passwords with random generation of user salt (so actually something similar to our DatabaseReadingSaltEncoder implementation).

19.2. Password Encryption of Rememberme Passwords

Username and passwords stored in clear text

The Remember Me feature of JBoss Portal Platform uses a token mechanism to be able to authenticate returning users without requiring an explicit login. However, to be able to authenticate these users, the token needs to store the username and password in clear text in the JCR.
Administrators have two options available to ameliorate this risk:
  1. The Remember Me feature can be disabled by removing the corresponding checkbox in: JPP_DIST/gatein/gatein.ear/portal.war/login/jsp/login.jsp and JPP_DIST/gatein/gatein.ear/portal.war/groovy/portal/webui/UILoginForm.gtmpl.
  2. Passwords can be encoded prior to being saved to the JCR. This option requires administrators to provide a custom subclass of org.exoplatform.web.security.security.AbstractCodec and set up a codec implementation with CookieTokenService:

    Procedure 19.1. Encrypt Password in JCR

    1. Create a Java class similar to:
      package org.example.codec;
      
      import org.exoplatform.container.xml.InitParams;
      import org.exoplatform.web.security.security.AbstractCodec;
      import org.exoplatform.web.security.security.CookieTokenService;
      import org.picocontainer.Startable;
      
      public class ExampleCodec extends AbstractCodec implements Startable
      {
         private String simpleParam;
         private CookieTokenService cookieTokenService;
         
         public ExampleCodec(InitParams params, CookieTokenService cookieTokenService)
         {
            simpleParam = params.getValueParam("encodingParam").getValue();
            this.cookieTokenService = cookieTokenService;
         }
         
         public void start()
         {
            cookieTokenService.setupCodec(this);
         }
      
         public void stop()
         {      
         }
         
         /**
          * Very simple encoding algorithm used only for demonstration purposes. 
          * You should use stronger algorithm in real production environment. 
          */
         public String encode(String plainInput)
         {
            return plainInput + simpleParam;
         }
         
         public String decode(String encodedInput)
         {
            return encodedInput.substring(0, encodedInput.length() - simpleParam.length());
         }
      
      }
      
      
    2. Compile the class and package it into a jar file. For this example we will call the jar file codec-example.jar.
    3. Create a conf/portal/configuration.xml file within the codec-example.jar similar to the example below. This allows the portal kernel to find and use the new codec implementation.
      <?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_2.xsd http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"
      xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd">
      
      <component>
         <key>org.example.codec.ExampleCodec</key>
         <type>org.example.codec.ExampleCodec</type>
         <init-params>
            <value-param>
               <name>encodingParam</name>
               <value>aaa</value>
            </value-param>
         </init-params>
      </component>
      
      </configuration> 
    4. Deploy the codec-example.jar into the JPP_DIST/gatein/gatein.ear/lib/ directory.
    5. Start (or restart) JBoss Portal Platform.
      Any passwords written to the JCR will now be encoded and not plain text.

Chapter 20. Predefined User Configuration

The initial Organization configuration should be specified by editing the content of JPP_DIST/gatein/gatein.ear/portal.war:/WEB-INF/conf/organization/organization-configuration.xml. This file uses the portal XML configuration schema. It lists several configuration plug-ins.

20.1. Plug-in for Adding Users, Groups and Membership Types

The plug-in type org.exoplatform.services.organization.OrganizationDatabaseInitializer is used to specify a list of membership types, a list of groups and a list of users to be created.
The checkDatabaseAlgorithm initialization parameter determines how the database update is performed.
If its value is set to entry it means that each user, group and membership listed in the configuration is checked each time JBoss Portal Platform is started. If the entry does not yet exist in the database, it is created.
If checkDatabaseAlgorithm parameter value is set to empty, the configuration data will be updated to the database only if the database is empty.

20.2. Membership Types

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

Note

See organization-configuration.xml for the full content.
<field name="membershipType">
  <collection type="java.util.ArrayList">
    <value>
      <object type="org.exoplatform.services.organization.OrganizationConfig$MembershipType">
        <field name="type">
          <string>member</string>
        </field>
        <field name="description">
          <string>member membership type</string>
        </field>
      </object>
    </value>
    <value>
      <object type="org.exoplatform.services.organization.OrganizationConfig$MembershipType">
        <field name="type">
          <string>owner</string>
        </field>
        <field name="description">
          <string>owner membership type</string>
        </field>
      </object>
     </value>
     <value>
       <object type="org.exoplatform.services.organization.OrganizationConfig$MembershipType">
         <field name="type">
           <string>validator</string>
         </field>
         <field name="description">
           <string>validator membership type</string>
         </field>
       </object>
     </value>
   </collection>
</field>

20.3. Groups

The predefined groups are specified in the group field of the OrganizationConfig plug-in parameter.
<field  name="group">
  <collection type="java.util.ArrayList">             
    <value>
      <object type="org.exoplatform.services.organization.OrganizationConfig$Group">
        <field  name="name"><string>platform</string></field>
        <field  name="parentId"><string></string></field>
        <field  name="description"><string>the /platform group</string></field>
        <field  name="label"><string>Platform</string></field>                    
      </object>
    </value>
    <value>
      <object type="org.exoplatform.services.organization.OrganizationConfig$Group">
        <field  name="name"><string>administrators</string></field>
        <field  name="parentId"><string>/platform</string></field>
        <field  name="description"><string>the /platform/administrators group</string></field>
        <field  name="label"><string>Administrators</string></field>
      </object>
    </value>
    <value>
      <object type="org.exoplatform.services.organization.OrganizationConfig$Group">
        <field  name="name"><string>users</string></field>
        <field  name="parentId"><string>/platform</string></field>
        <field  name="description"><string>the /platform/users group</string></field>
        <field  name="label"><string>Users</string></field>
      </object>
    </value>
    <value>
      <object type="org.exoplatform.services.organization.OrganizationConfig$Group">
        <field  name="name"><string>guests</string></field>
        <field  name="parentId"><string>/platform</string></field>
        <field  name="description"><string>the /platform/guests group</string></field>
        <field  name="label"><string>Guests</string></field>
      </object>
    </value>
   ...
  </collection>
</field>

20.4. Users

The predefined users are specified in the user field of the OrganizationConfig plug-in parameter.
<field name="user">
  <collection type="java.util.ArrayList">
    <value>
      <object type="org.exoplatform.services.organization.OrganizationConfig$User">
        <field name="userName"><string>root</string></field>
        <field name="password"><string>exo</string></field>
        <field name="firstName"><string>root</string></field>
        <field name="lastName"><string>root</string></field>
        <field name="email"><string>exoadmin@localhost</string></field>
        <field name="groups"><string>member:/admin,member:/user,owner:/portal/admin</string></field>
      </object>
    </value>
    <value>
      <object type="org.exoplatform.services.organization.OrganizationConfig$User">
        <field name="userName"><string>exo</string></field>
        <field name="password"><string>exo</string></field>
        <field name="firstName"><string>site</string></field>
        <field name="lastName"><string>site</string></field>
        <field name="email"><string>exo@localhost</string></field>
        <field name="groups"><string>member:/user</string></field>
      </object>
    </value>
    ...
  </collection>
</field>

20.5. Plug-in for Monitoring User Creation

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

Note

The terms 'membership' and 'membership type' refer to the same thing, and are used interchangeably.
<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>/platform/users</string></field>
                 <field  name="membership"><string>member</string></field>
               </object>
             </value>               
           </collection>
         </field>
         <field  name="ignoredUser">
           <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>
       </object>
     </object-param>
   </init-params>
 </component-plugin>

Chapter 21. Authentication Token Configuration

21.1. The Token Service

The Token Service is used in authentication.
The token system prevents user account information being sent in clear text mode within inbound requests. This increases authentication security.
The token service allows administrators to create, delete, retrieve and clean tokens as required. The service also defines a validity period of any given token. The token becomes invalid once this period expires.

21.2. Implementing the Token Service API

All token services used in JBoss Portal Platform authentication must be implemented by subclassing an AbstractTokenService abstract class.
The following AbstractTokenService methods represent the contract between authentication runtime, and a token service implementation.
public Token getToken(String id) throws PathNotFoundException, RepositoryException;
public Token deleteToken(String id) throws PathNotFoundException, RepositoryException;
public String[] getAllTokens();
public long getNumberTokens() throws Exception;
public String createToken(Credentials credentials) throws IllegalArgumentException,NullPointerException;
public Credentials validateToken(String tokenKey, boolean remove) throws NullPointerException;

21.3. Configuring Token Services

The token services configuration includes specifying the token validity period. The token service is configured as a portal component (in the portal scope, as opposed to the root scope; refer to Part V, “Advanced Development Foundations” for more information).
In the XML example below, CookieTokenService is a subclass of AbstractTokenService so it has a property which specifies the validity period of the token.
The token service will initialize this validity property by looking for an init-param named service.configuration.
This property must have three values.
<component>
    <key>org.exoplatform.web.security.security.CookieTokenService</key>
    <type>org.exoplatform.web.security.security.CookieTokenService</type>
    <init-params>
      <values-param>
        <name>service.configuration</name>
        <!-- Service name -->
        <value>jcr-token</value>
        <!-- Amount of time -->
        <value>7</value>
        <!-- Unit of time -->
        <value>DAY</value>
        <value>autologin</value>
      </values-param>
    </init-params>
  </component>
In this case, the service name is jcr-token and the token expiration time is one week.
JBoss Portal Platform supports four time units:
  1. SECOND
  2. MINUTE
  3. HOUR
  4. DAY

Chapter 22. PicketLink IDM Integration

JBoss Portal Platform uses the PicketLink IDM component to store necessary identity information about users, groups and memberships. While legacy interfaces are still used (org.exoplatform.services.organization) for identity management, there is a wrapper implementation that delegates to PicketLink IDM framework.
This section does not 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 models represented in the org.exoplatform.services.organization interfaces and the one used in PicketLink IDM have some major differences.
For example; 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 org.exoplatform.services.organization model allows only pure tree-like membership structures.
Additionally, org.exoplatform.services.organization 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.

22.1. Configuration Files

The main configuration file is JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/organization/idm-configuration.xml :
<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"
               xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd">

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

22.1.1. PicketlinkIDMServiceImpl

Theorg.exoplatform.services.organization.idm.PicketLinkIDMServiceImpl service has the following options:
  • config (value-param) The PicketLink IDM configuration file.
  • hibernate.properties (properties-param) A list of hibernate properties used to create SessionFactory that will be injected to JBoss Identity IDM configuration registry.
  • hibernate.annotations A list of annotated classes that will be added to Hibernate configuration.
  • hibernate.mappings A list of .xml files that will be added to hibernate configuration as mapping files.
  • jndiName (value-param) If the 'config' parameter is not provided, this parameter will be used to perform JNDI lookup for IdentitySessionFactory.
  • portalRealm (value-param) The realm name that should be used to obtain proper IdentitySession. The default is 'PortalRealm'.
  • apiCacheConfig (value-param) The Infinispan configuration file with cache configuration for PicketLink IDM API. It's different for cluster and non-cluster because Infinispan needs to be replicated in cluster environment.
  • storeCacheConfig (value-param). The Infinispan configuration file with cache configuration for PicketLink IDM IdentityStore. Actually it's used only for LDAP store (not used with default DB configuration). It's different for cluster and non-cluster because Infinispan needs to be replicated in cluster environment.

22.1.2. PicketlinkIDMOrganizationServiceImpl

The org.exoplatform.services.organization.idm.PicketLinkIDMOrganizationServiceImpl key is a main entry point implementing org.exoplatform.services.organization.OrganizationService and is dependent on org.exoplatform.services.organization.idm.PicketLinkIDMService.
Theorg.exoplatform.services.organization.idm.PicketLinkIDMOrganizationServiceImpl service has the following options defined as fields of object-param of the org.exoplatform.services.organization.idm.Config type:
  • defaultGroupType The name of the PicketLink IDM GroupType that will be used to store groups. The default is GTN_GROUP_TYPE.
  • rootGroupName The name of the PicketLink IDM Group that will be used as a root parent. The default is GTN_ROOT_GROUP.
  • passwordAsAttribute This parameter specifies if a password should be stored using PicketLink IDM Credential object or as a plain attribute. The default is false.
  • useParentIdAsGroupType This parameter stores the parent ID path as a group type in PicketLink IDM for any IDs not mapped with a specific type in 'groupTypeMappings'. If this option is set to false, and no mappings are provided under 'groupTypeMappings', then only one group with the given name can exist in the portal 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 portal API as children of a given group ID, and stores them with a given group type name in PicketLink IDM. If the parent ID ends with "/*", then all child groups will have the mapped group type. Otherwise, only direct (first level) children will use this type. This can be leveraged by LDAP if LDAP DN is configured in PicketLink IDM to only store a specific group type. This will then store the given branch in portal group tree, while all other groups will remain in the database.
  • forceMembershipOfMappedTypes Groups stored in PicketLink IDM with a type mapped in 'groupTypeMappings' will automatically be members under the mapped parent. Group relationships linked by PicketLink IDM group association will not be necessary. This parameter can be set to false if all groups are added via portal APIs. This may be useful with LDAP configuration as, when set to true, it will make every entry added to LDAP appear in portal. This, however, is not true for entries added via JBoss Portal Platform management user interface.
  • 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, PicketlinkIDMOrganizationServiceImpl uses those defaults to perform identity management operations.
  • JBoss Portal Platform User interface properties fields are persisted in PicketLink IDM using those attributes names: firstName, lastName, email, createdDate, lastLoginTime, organizationId, password (if password is configured to be stored as attribute).
  • JBoss Portal Platform Group interface properties fields are persisted in PicketLink IDM using those attributes names: label, description.
  • JBoss Portal Platform MembershipType interface properties fields are persisted in JBoss Identity IDM using those RoleType properties: description, owner, create_date, modified_date. A sample PicketLink IDM configuration file is shown below. To understand all the options it contains, please refer to the PicketLink IDM Reference Guide.
<jboss-identity xmlns="urn:jboss:identity:idm:config:v1_0_beta"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="urn:jboss:identity:idm:config:v1_0_alpha identity-config.xsd">
    <realms>
        <realm>
            <id>PortalRealm</id>
            <repository-id-ref>PortalRepository</repository-id-ref>
            <identity-type-mappings>
                <user-mapping>USER</user-mapping>
            </identity-type-mappings>
        </realm>
    </realms>
    <repositories>
        <repository>
            <id>PortalRepository</id>
            <class>org.jboss.identity.idm.impl.repository.WrapperIdentityStoreRepository</class>
            <external-config/>
            <default-identity-store-id>HibernateStore</default-identity-store-id>
            <default-attribute-store-id>HibernateStore</default-attribute-store-id>
        </repository>
    </repositories>
    <stores>
        <attribute-stores/>
        <identity-stores>
            <identity-store>
                <id>HibernateStore</id>
                <class>org.jboss.identity.idm.impl.store.hibernate.HibernateIdentityStoreImpl</class>
                <external-config/>
                <supported-relationship-types>
                    <relationship-type>JBOSS_IDENTITY_MEMBERSHIP</relationship-type>
                    <relationship-type>JBOSS_IDENTITY_ROLE</relationship-type>
                </supported-relationship-types>
                <supported-identity-object-types>
                    <identity-object-type>
                        <name>USER</name>
                        <relationships/>
                        <credentials>
                            <credential-type>PASSWORD</credential-type>
                        </credentials>
                        <attributes/>
                        <options/>
                    </identity-object-type>
                </supported-identity-object-types>
                <options>
                    <option>
                        <name>hibernateSessionFactoryRegistryName</name>
                        <value>hibernateSessionFactory</value>
                    </option>
                    <option>
                        <name>allowNotDefinedIdentityObjectTypes</name>
                        <value>true</value>
                    </option>
                    <option>
                        <name>populateRelationshipTypes</name>
                        <value>true</value>
                    </option>
                    <option>
                        <name>populateIdentityObjectTypes</name>
                        <value>true</value>
                    </option>
                    <option>
                        <name>allowNotDefinedAttributes</name>
                        <value>true</value>
                    </option>
                    <option>
                        <name>isRealmAware</name>
                        <value>true</value>
                    </option>
                </options>
            </identity-store>
        </identity-stores>
    </stores>
</jboss-identity>

Chapter 23. Organization API

The exo.platform.services.organization package has five main components:
User
The User component contains basic information about a user; such as username, password, first name, last name, and email address.
User Profile
The User Profile component contains extra information about a user, such as user's personal information, and business information. You can also add additional information about a user if your application requires it.
Group
The Group component contains a group graph.
Membership Type
The Membership Type component contains a list of predefined membership types.
Membership
The Membership component connects a User, a Group and a Membership Type.
A user can have one or more memberships within a group. For example: User A can have the 'member' and 'admin' memberships in group /user. A user belongs to a group if he has at least one membership in that group.
The OrganizationService component is an additional component that serves as an entry point into the Organization API. It provides handling functionality for the five components.
Handler Objects Accessible by the OrganizationService Component

Figure 23.1. Handler Objects Accessible by the OrganizationService Component


By exposing the Organization API, the OrganizationService component provides developers with access to handler objects for managing each of the five components:
  • UserHandler
  • UserProfileHandler
  • GroupHandler
  • MembershipTypeHandler
  • MembershipHandler
The five central API components are designed to be similar to persistent entities and handlers are specified similarly to data access objects (DAO).
Organization API simply describes a contract, meaning it is not a concrete implementation. The described components are interfaces, allowing for different concrete implementations. In practical terms the existing implementation can be replaced with a different one.

Chapter 24. Accessing User Profile

The following code retrieves the details for a logged-in user:
// Alternative context: WebuiRequestContext context = WebuiRequestContext.getCurrentInstance() ;
PortalRequestContext context = PortalRequestContext.getCurrentInstance() ;
// Get the id of the user logged
String userId = context.getRemoteUser();

// Retrieve OrganizationService but it works only from WebUI code. See variants below in documentation 
OrganizationService orgService = getApplicationComponent(OrganizationService.class) ;

// Request the information from OrganizationService:
if (userId != null)
  {
  User user = orgService.getUserHandler().findUserByName(userId) ;
  if (user != null)
  {
    String firstName = user.getFirstName();
    String lastName = user.getLastName();
    String email = user.getEmail();
  }
}
Below are two alternatives for retrieving the Organization Service:
  1. OrganizationService service = (OrganizationService)
       ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(OrganizationService.class);
    
  2. OrganizationService service = (OrganizationService)
       PortalContainer.getInstance().getComponentInstanceOfType(OrganizationService.class);
    
Both alternatives are probably better than OrganizationService orgService = getApplicationComponent(OrganizationService.class) because you can use them from your own portlets or servlet/portlet filters. The variant with getApplicationComponent works only from WebUI.

Chapter 25. Create Users and Groups without Organization API

CoreOrganizationInitializer is a plug-in that creates users and groups outside the portal user interface, without the Organization API. The plug-in performs the function of the Organization API with regard to triggering listeners for users and groups at creation time. The plug-in prevents issues with missing JCR objects, resulting from incorrectly provisioned users and groups.
The plug-in is particularly useful when using the Site Publisher add-on, and directly adding users or groups to a LDAP server through LDIF files, or into a database using SQL.

25.1. Enable Initializer

The initializer is disabled by default. To activate it, uncomment the block containing the path to the initializer-configuration.xml file in JPP_DIST/gatein/gatein.ear/portal.war/conf/configuration.xml.
<!-- Uncomment for enable initializer (OrganizationListenersInitializerService and related stuff) -->
   <import>war:/conf/organization/initializer-configuration.xml</import>
All configuration for the initializer occurs in JPP_DIST/gatein/gatein.ear/portal.war/conf/organization/initializer-configuration.xml, which is the main configuration file for this initializer. More details about configuration options, along with alternatives to XML directive configuration, are described in subsequent sections.

25.2. Operations

There are a number of operations supported by CoreOrganizationInitializer.

Table 25.1. Supported Operations

Full Operation Parameters Remarks
treatUser(username, checkFolders) username
String value containing the user name to query.
checkFolders
Boolean value (true | false) used by treatUser, treatGroup, and launchAll to control when listeners are triggered for a user or group.
Detects whether JCR folders are already present for the specified user, or group (in the case of launchAll). The presence of the folder indicates to the operation that listeners are already triggered for the user or group.
If set to true (recommended), listeners will not be triggered for the user.
If set to false, all listeners are re-triggered for the specified user or group.
treatGroup(groupName, checkFolders)
groupName
(See checkFolders above for usage.)
String value containing the group name to query.
launchAll(checkFolders) See checkFolders above for usage.
Finds all users and groups and triggers treatUser and treatGroup as appropriate.
If this operation is used with checkFolders=false, all listeners will be restarted for all users and groups. This will have a significant impact on performance. To avoid this impact, use only checkFolders=true for this operation.

25.3. Using configuration directives

There are a number of ways of controlling operations associated with CoreOrganizationInitializer. All parameters are configured in <value-param> directive blocks in the JPP_DIST/gatein/gatein.ear/portal.war/conf/organization/initializer-configuration.xml file.

Example 25.1. <value-param> Block for Initializer Directives

<value-param>
   <name>directiveName</name>
   <value>[true | false]</value>
</value-param>

Procedure 25.1. Disable launchAll at Startup

For large implementations with many users and groups, consider disabling launchAll functionality during start-up for each portal container. Disabling this operation during start-up will result in a performance improvement.
  1. Open JPP_DIST/gatein/gatein.ear/portal.war/conf/organization/initializer-configuration.xml
  2. Locate the executeAllListenersDuringBoot <value-param> parameter.
  3. Set <value>false</value> to prevent listeners being started for each user and group.

Procedure 25.2. Disable launchAll Job Scheduler

A job scheduler is configured to run by default, and executes launchAll periodically. For large implementations with many users and groups, consider disabling the Job scheduler launchAll functionality for each portal container. Disabling this operation may result in a performance improvement.
  1. Open JPP_DIST/gatein/gatein.ear/portal.war/conf/organization/initializer-configuration.xml
  2. Search for "scheduler" in the file.
  3. Comment out the entire scheduler block to disable the job permanently.

Procedure 25.3. Altering On-demand JCR Object Creation at User Login

When a user logs into the portal, the treatUser operation runs on-demand to create the required JCR objects. This operation (activated by default) improves overall performance of the portal, because user objects are only created when needed. To disable the operation (not recommended), follow the guidelines below.
  1. Open JPP_DIST/gatein/gatein.ear/portal.war/conf/organization/initializer-configuration.xml
  2. Locate the ExtensibleFilter directive block.
  3. Within the ExtensibleFilter directive block, locate the triggerListenersForGroups <value-param> parameter.
  4. Set <value>false</value> to prevent the treatGroup operation from executing for each group the user is a member of.

25.4. Using JMX Console

The JMX Console is available for all CoreOrganizationInitializer operations.

Procedure 25.4. Triggering Operations through the JMX Console

  1. Open http://localhost:8080/jmx-console
  2. Locate the exo domain.
  3. Locate the correct MBean, depending on the portal container used. Substitute the portal="portal_name" value with the name of the portal container used in your production environment.
    • For JBoss Portal Platform.
      MBean name=OrganizationInitializerService,portal="portal", service=OrganizationInitializerService
  4. Alter the values of the operations as required in the console, referring to the descriptions in Table 25.1, “Supported Operations” for more information.

25.5. Using REST Interface

OrganizationInitializerService can be accessed using a REST interface. Some examples of commands are provided, as a practical way of demonstrating the REST syntax required. Pay particular attention to how forward slash symbols are escaped in directory paths featured in some REST syntax examples.
http://localhost:8080/rest/initializer/launchAllListeners/true
Trigger launchAll for portal container "portal", with checkFolders=true.
http://localhost:8080/rest-ecmdemo/initializer/launchAllListeners/true
Trigger launchAll for portal container "ecmdemo", with checkFolders=true.
http://localhost:8080/rest-ecmdemo/initializer/launchAllListeners/false
Trigger launchAll for portal container "ecmdemo", with checkFolders=false.
http://localhost:8080/rest/initializer/launchUserListeners/jduke/true
Trigger treatUser for portal container "portal", for user "jduke"with checkFolders=true.
http://localhost:8080/rest-ecmdemo/initializer/launchUserListeners/jduke/true
Trigger treatUser for portal container "portal", for user "jduke"with checkFolders=false.
http://localhost:8080/rest/initializer/launchGroupListeners/@platform@users/true
Trigger "treatGroup" for group "/platform/users" in the portal container "portal".
http://localhost:8080/rest-ecmdemo/initializer/launchGroupListeners/@acme@roles@employees/true
Trigger "treatGroup" for group "/acme/roles/employee" in the portal container "ecmdemo".

Chapter 26. Single Sign-On

26.1. Overview and Configuration Assumptions

JBoss Portal Platform provides an implementation of single sign-on (SSO) as an integration and aggregation platform.
When logging into the portal, users can access 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.
This section will cover the implementation of four different SSO plug-ins with JBoss Portal Platform:

Prerequisites

In this tutorial, the SSO server is being installed in a Tomcat environment. Tomcat can be obtained from http://tomcat.apache.org .
All the packages required for SSO setup can be found in the JPP_DIST/gatein-sso directory of the JBoss Portal Platform binary package.

Warning

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

26.2.  Central Authentication Service (CAS)

The CAS single sign-on (SSO) plug-in enables seamless integration between the platform and the CAS SSO framework. General information about CAS can be found on the Jasig website.

26.2.1. Authentication Process

The authentication process with CAS integration occurs in the following order:
  1. A user visits the main portal page, and wishes to authenticate. The user clicks Sign in.
  2. Normally this action would present the portal login dialog, however with SSO integration enabled, the action redirects the user to a marker URL such as http://localhost:8080/portal/sso.
    The portal handles this user action by calling the interceptor (Servlet filter) LoginRedirectFilter, which redirects the user seamlessly away from the /portal/sso URL to the CAS server page.
  3. The interceptor redirects the user to the CAS login page http://localhost:8888/cas/login. The user enters the correct authentication information, and submits the form.
    The CAS server retrieves the information from the identity store. The store could be an external database, a LDAP server, or from information obtained through an authentication plug-in such as the one shipped with JBoss Portal Platform. Refer to Section 26.2.4.1, “Authentication Plug-in” for specific details about this technology.
  4. Once CAS determines the user has the correct access privileges to access the portal server, CAS redirects the user back to the portal through another marker URL such as http://localhost:8080/portal/initiatelogin.
    The InitiateLoginFilter interceptor acts on the user redirection to /portal/initiatelogin by obtaining a CAS ticket attached in the HTTP request inside the ticket parameter. The interceptor then delegates validation of this ticket to a configured CASAgent component.
  5. The CASAgent validates the ticket by sending a validation request to the CAS server through a configured back channel. The CAS server validates the request, and ensures it contains the user name of the authenticated user in step 3.
  6. After SSO validation, InitiateLoginFilter redirects the user to the portal login URL http://localhost:8080/portal/login, which initiates JAAS authentication.
    The SSOLoginModule detects whether the user has been successfully validated by CASAgent. If this is the case, the login module obtains data about user (groups, memberships) from OrganizationService and encapsulates the details into an Identity object.
  7. The JBoss Enterprise Application Platform 6 LoginModule completes the authentication request by establishing the JAAS Subject, and saves the Identity object to the IdentityRegistry. For more information about login modules, refer to Section 18.2, “Login Modules”.
  8. After successful JAAS authentication, the user is redirected to the portal in an authenticated state.
For more information about the available Login Modules shipped with the product, refer to the JBoss Enterprise Application Platform Security Guide.

26.2.2. Logout Process

The logout process with CAS integration occurs in the following order:
  1. The authenticated user clicks the Sign out link.
  2. The CASLogoutFilter interceptor recognizes the logout request, and redirects the user to the CAS logout page http://localhost:8888/cas/logout.
  3. The CAS server logs out the user, and invalidates the CAS cookie CASTGC.
  4. CAS redirects the user back to the portal using the logout redirection configured in Section 26.2.4.2, “Logout Redirection Setup”.
    If the CASLogoutFilter is enabled, the user is logged out from both the portal and CAS server.
  5. The logout redirection request completes the logout process on the CAS server's side, and the user is redirected to the portal's anonymous page.

26.2.3. CAS Configuration Overview

For scope purposes, the setup instructions assume the following configuration outcomes:
  • The CAS 3.5 is downloaded, and required changes are made to authentication plug-in, logout redirection, and CASTGC cookie configuration.
  • Once configured, Apache Maven is used to create the custom CAS web archive, suitable for deployment.
  • The WAR is deployed to the Apache Tomcat server, which acts as the host for the CAS.
  • Apache Tomcat is configured to listen on localhost:8888.
  • JBoss Portal Platform is configured to listen on localhost:8080.

26.2.3.1.  Download CAS

CAS can be downloaded from http://www.jasig.org/cas/download. The supported version is CAS 3.5. More recent CAS versions may also work, however have not been officially tested as part of this specific configuration exercise.
Extract the downloaded file into a suitable working directory. This location will be referred to as CAS_DIR in subsequent configuration instructions.

26.2.4. Modifying the CAS server

To configure the CAS server correctly, the most effective way is to make the necessary changes directly in the CAS code base. Follow the instructions in the sections below to make the required changes to the CAS code base, before using Maven to build the CAS web archive.

26.2.4.1. Authentication Plug-in

While it is possible (and perfectly acceptable) for an administrator to configure CAS to retrieve user credentials from an external database, or from a LDAP server, it is also possible to use JBoss technology.
CAS can be configured to make secure authentication callbacks to a RESTful service installed on the remote portal instance using the supplied CAS AuthenticationPlugin.
Implementing the AuthenticationPlugin on the CAS server has the advantage of leveraging a single identity storage for portal user, group and role data. If a new user is added using the portal user management interface, the user information is instantly accessible to the CAS server through the technology implemented by the AuthenticationPlugin.
The plug-in verifies user credentials by connecting to an existing portal instance using REST over the HTTP protocol. The portal serves a REST authentication callback request, and verifies the user identity against the portal's own identity storage provided by the PicketLink IDM OrganizationService. The AuthenticationPlugin receives the portal's response to the CAS server, and continues with the authentication process based on user data in the response.
For the plug-in to function correctly, it must be properly configured on the CAS server to connect to this service. Set up the server to authenticate against the portal using the REST call-back.

Procedure 26.1. Configuring the Authentication Plug-in

  1. Open CAS_DIR/cas-server-webapp/src/main/webapp/WEB-INF/deployerConfigContext.xml.
  2. Replace the default configuration, which declares the Jasig SimpleTestUsernamePasswordAuthenticationHandler Authentication Handler with the following supported Authentication Handler.

    Note

    This configuration is available in the JPP_DIST/gatein-sso/cas/plugin/WEB-INF/deployerConfigContext.xml file. If you choose to take this configuration file, ensure the default host, port and context parameters are adjusted to match the values corresponding to the remote portal instance.
    <bean class="org.gatein.sso.cas.plugin.AuthenticationPlugin">
      <property name="gateInProtocol"><value>http</value></property>
      <property name="gateInHost"><value>localhost</value></property>
      <property name="gateInPort"><value>8080</value></property>
      <property name="gateInContext"><value>portal</value></property>
      <property name="httpMethod"><value>POST</value></property>
    </bean>
  3. Copy all jars from JPP_DIST/gatein-sso/cas/plugin/WEB-INF/lib/ to the CAS_DIR/cas-server-webapp/src/main/webapp/WEB-INF/lib directory.

26.2.4.2. Logout Redirection Setup

The CAS server displays the CAS logout page with a link to return to the portal by default. To make the CAS server redirect to the portal page after a logout, modify CAS_DIR/cas-server-webapp/src/main/webapp/WEB-INF/cas-servlet.xml to include the followServiceRedirects="true" parameter:
<bean id="logoutController"  class="org.jasig.cas.web.LogoutController"
  p:centralAuthenticationService-ref="centralAuthenticationService"
  p:logoutView="casLogoutView"
  p:warnCookieGenerator-ref="warnCookieGenerator"
  p:ticketGrantingTicketCookieGenerator-ref="ticketGrantingTicketCookieGenerator"
  p:followServiceRedirects="true"/>

26.2.4.3. CAS SSO Cookie Configuration (CASTGC)

Jasic CAS uses a cookie named CAS Ticket Granting Cookie (CASTGC) to control the authentication state within the browser session. The cookie contains a Ticket Granting Ticket (TGT), which preserves SSO authentication where more than one site is controlled by the same SSO profile.

Example 26.1. Basic CASTGC Portal Authentication Scenario

Two portal servers are provisioned that use a single CAS server to manage authentication. The portals are named accounts and services.
When a user initially accesses the accounts portal, they provide their SSO credentials, and CAS authenticates them as a registered user. The user then switches to the services portal, and is authenticated when she clicks the Sign in link.
This behavior is correct given this example because the browser instance stores the browser authentication state using the CASTGC cookie. The CASTGC cookie in this instance creates new ticket for the services portal automatically based on the authentication state present for the accounts portal.

The behavior described in Example 26.1, “Basic CASTGC Portal Authentication Scenario” exists through a secured connection only (https connection). To benefit from authentication across two or more portals, one of the options below must be implemented. Choose the correct option based on the deployment environment:
Testing
Alter the CASTGC cookie to be non-secure.
The cookie can be accessed through http (insecure) connections.
To configure this test behavior, open CAS_DIR/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xml and switch the attribute cookieSecure to false.
<bean id="ticketGrantingTicketCookieGenerator"
  p:cookieSecure="false"
  p:cookieMaxAge="-1"
  p:cookieName="CASTGC"
  p:cookiePath="/cas" />
Production
Correctly implement the https protocol for all production servers that rely on CAS. This configuration is the recommended method for any production server, and ensures greater security for CAS connections. Refer to the Jasig documentation about securing CAS https://wiki.jasig.org/display/CASUM/Securing+Your+New+CAS+Server for information and resources.

26.2.5. Install Apache Tomcat Server

Install and configure Apache Tomcat 7, which provides the host server for the CAS server.
File name abbreviations in this section are described in Section 1.1, “File Name Conventions”

Procedure 26.2. Configuring Apache Tomcat for CAS

  1. Visit http://tomcat.apache.org/download-70.cgi and download the Tomcat 7 binary distribution.
  2. Extract and install the binary on the server that is required to host CAS. This directory is now referred to as TOMCAT_HOME.
  3. Edit TOMCAT_HOME/conf/server.xml and change port 8080 to 8888 to avoid a conflict with the default JBoss Portal Platform listen port.

    Important

    If the Apache Tomcat server is installed on the same machine as JBoss Portal Platform, ensure other listen ports common to both servers are changed to prevent configuration issues. For example, change the Tomcat admin port from 8005 to 8805, and the Tomcat AJP port from 8009 to 8809.
  4. Ensure all Apache Tomcat ports are open in the server firewall, and the service is enabled and running so the platform can communicate with Apache Tomcat on the same server.

26.2.6. Modifying the Portal

Before building and deploying the Jasig CAS sever, configuration needs to be implemented on the JBoss Portal Platform server to prepare the portal for CAS integration.

26.2.6.1. Portal SSO Primary Configuration File

The main portal configuration file for SSO integration is JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/sso/security-sso-configuration.xml. All required SSO components such as agents and SSO interceptors (servlet filters in v5.x of the product) are configured in this file.
In most cases, it will never be necessary to edit security-sso-configuration.xml directly when using JBoss Portal Platform. The portal architecture allows users to override the base configuration described in this file using name/value pairs configured in one place: JPP_HOME/standalone/configuration/gatein/configuration.properties
The exception to this rule is where configuration present in security-sso-configuration.xml is fundamentally unsuitable for the production environment the server will be deployed to, or when additional underlying functionality is required (for example, another custom interceptor).

26.2.6.2. Portal configuration.properties for CAS SSO

To prepare the portal platform for CAS authentication, SSO filters and login modules need to be specified in global configuration files. The location of the CAS server, as configured in a locally-running Apache Tomcat server, also needs to be specified.

Procedure 26.3. Configuring SSO configuration.properties for CAS

  1. Open JPP_HOME/standalone/configuration/gatein/configuration.properties and locate the SSO section in the file.
  2. Make the following changes to the file to declare the correct login module, server and portal URLs, and the logout filter.
    # SSO
    gatein.sso.enabled=true
    gatein.sso.callback.enabled=${gatein.sso.enabled}
    gatein.sso.login.module.enabled=${gatein.sso.enabled}
    gatein.sso.login.module.class=org.gatein.sso.agent.login.SSOLoginModule
    gatein.sso.server.url=http://localhost:8888/cas
    gatein.sso.portal.url=http://localhost:8080
    gatein.sso.filter.logout.class=org.gatein.sso.agent.filter.CASLogoutFilter
    gatein.sso.filter.logout.url=${gatein.sso.server.url}/logout
    gatein.sso.filter.login.sso.url=${gatein.sso.server.url}/login?service=${gatein.sso.portal.url}/@@portal.container.name@@/initiatessologin
    
gatein.sso.enabled
Specifies whether SSO integration is enabled on the portal. With this option set to "true" when a user clicks the Sign in link, the user is redirected to the /portal/sso URL rather than a standard Sign in dialog.
gatein.sso.callback.enabled
Specifies whether the REST callback authentication handler is enabled.
The handler is required if the CAS server must use the SSO Authentication plug-in to handle portal authentication. See Section 26.2.4.1, “Authentication Plug-in” for details. The callback handler is enabled by default. Set the parameter to false if the authentication plug-in on the CAS server side is not required.
gatein.sso.login.module.enabled
Specifies whether a pre-defined SSO login module declared in JPP_HOME/standalone/configuration/standalone.xml is used for authentication. When the property is set to true, the SSODelegateLoginModule delegates work to another login module, as specified using the gatein.sso.login.module.class property. SSODelegateLoginModule will also resend all its options to its delegate.
This parameter removes the need to manually change any login module configuration in the standalone.xml file, which simplifies platform configuration.
gatein.sso.login.module.class
Specifies the classname of the login module SSODelegateLoginModule will delegate to. This parameter will work only if gatein.sso.login.module.enabled is specified.
gatein.sso.server.url
Specifies the URL from which the CAS server is accessible.
gatein.sso.portal.url
Specifies the URL from which the JBoss Portal Platform is accessible.
gatein.sso.filter.logout.class
Specifies the class of the logout filter. In the example above org.gatein.sso.agent.filter.CASLogoutFilter is the correct choice because this filter is able to redirect to the CAS server and perform logout on the CAS side.
gatein.sso.filter.logout.url
Specifies the CAS server logout URL, which is used for redirection by the logout filter.
gatein.sso.filter.logout.enabled
Optional parameter, which specifies whether the logout interceptor is enabled. To disable logout on CAS side, set the parameter value to false. This causes both options gatein.sso.filter.logout.class and gatein.sso.filter.logout.url to be ignored.
When a user logs out of the portal, the CAS authentication ticket is still valid for other CAS authenticated sites.
gatein.sso.filter.login.sso.url
Specifies the CAS server login URL, which is used by LoginRedirectFilter for redirection to the CAS server login page.

Note

The string @@portal.container.name@@ is dynamically replaced when the URL is interpreted by the platform's SSO Component. It is recommended that this string is used over hard-coding the name of the portal for future maintenance and ease of configuration changes.

26.2.7.  Build and Deploy the CAS

Jasig CAS uses Apache Maven to build the cas.war file. Follow the instructions to produce this file, and deploy it to the Apache Tomcat server.

Procedure 26.4. Building CAS, and Deploying to Tomcat

  1. Install Maven by following the recommendations and links in the Building and Deploying section of the Jasig CAS user documentation.
  2. In a terminal, navigate to CAS_DIR/cas-server-webapp/, and run mvn install.
  3. Copy CAS_DIR/cas-server-webapp/target/cas.war to TOMCAT_HOME/webapps.
  4. Tomcat should be running by default, if the process has been followed up to this step. Start JBoss Portal Platform, and verify the server is running by opening http://localhost:8080/.
  5. Open http://localhost:8888/cas to verify the CAS server has correctly deployed to Tomcat. If the link does not open the CAS login page, restart Apache Tomcat and try again.
The CAS server is now deployed to Tomcat, and the portal will now redirect users to the CAS login page when they click on the Sign In link.

26.3.  Java Open Single Sign-On (JOSSO)

Java Open Single Sign-On (JOSSO) is an open-source single sign-on solution based on Java EE. It allows multiple web servers or web applications to authenticate users with a credential store. Detailed information about JOSSO can be found at http://www.josso.org.
JOSSO integration with JBoss Portal Platform requires an Apache Tomcat server instance to host JOSSO. JBoss Portal Platform communicates with the JOSSO server through a single sign-on plug-in.
Setting up the integration consists of two steps – setting up the JOSSO server and setting up the portal to use the JOSSO server. These two steps differ depending on the used version of JOSSO, as described in Section 26.3.2, “JOSSO 1.8” and Section 26.3.3, “JOSSO 2.2”. After completing the procedures described in either section, all links redirecting to user authentication pages will redirect to the JOSSO centralized authentication form.

26.3.1. Authentication Process

The login workflow for JOSSO is quite similar to that used for CAS authentication (specific details can be found in Section 26.2.1, “Authentication Process”).
Briefly – when a user clicks to sign in to a portal they are redirected to the JOSSO login screen, where they supply the appropriate credentials. They are then redirected (with access authorization) back to the Portal.
The JOSSOAgent component performs a validation of the authorization ticket with the JOSSO server via a back channel after the InitiateLoginFilter has delegated the josso_assertion_id request to it. The JOSSO agent and JOSSO server communicate via web services.
After a successful validation, the user identity is successfully established and the user is logged into the requested Portal.
On logout, JOSSOLogoutFilter performs a logout on both the Portal and the JOSSO server (similar to the process for CAS).
While the authentication plug-in (which is able to send REST requests to the portal, receive the response, and authenticate the user on the JOSSO side) is supported, this support is only for JOSSO 1.8 (not JOSSO 2.2 as at this release).
In this section, we will assume that JBoss Portal Platform will be running on JBoss Enterprise Application Platform 6 using localhost:8080 and that the JOSSO server will be running on Tomcat, using localhost:8888.

Note

There are differences between various JOSSO minor versions (especially between JOSSO versions 1.8.1 and 1.8.2) so instructions will be slightly different between various versions. This will be pointed in text in more details.

26.3.2. JOSSO 1.8

26.3.2.1. Obtaining JOSSO

JOSSO can be downloaded from http://sourceforge.net/projects/josso/files/. Use any 1.8.z version in a package that embeds Apache Tomcat. Once downloaded, extract the package into what will be called JOSSO_HOME in this example.

26.3.2.2. Setting up the JOSSO Server

This section describes how to set up the JOSSO server to authenticate against the JBoss Portal Platform using the REST authentication plug-in. In this example, the JOSSO server will be installed on Tomcat.
  1. Optional: To use the SSO authentication plug-in with JOSSO (not mandatory but recommended, see Section 26.3.1, “Authentication Process” for details), copy the contents of the JPP_DIST/gatein-sso/josso/josso-<version>/plugin/ directory into the JOSSO_HOME directory. Among the files that will be copied, the following ones are the most important:
    • JOSSO_HOME/lib/josso-gateway-config.xml
      The original file is being replaced. You should consider creating a backup of it before adding the new file.
    • JOSSO_HOME/lib/josso-gateway-gatein-stores.xml
      This file is not present in the original JOSSO_HOME download.
    • JOSSO_HOME/webapps/josso/WEB-INF/classes/gatein.properties
      This file is not present in the original JOSSO_HOME download. You may need to edit the file and change the host and port to match your JBoss Portal Platform instance. The values will be used by the authentication plug-in when sending REST requests over HTTP.
  2. Edit JOSSO_HOME/conf/server.xml and replace the 8080 port to 8888 to change the default Tomcat port and avoid a conflict with the default JBoss Portal Platform port (for testing purposes).

    Port Conflicts

    If JBoss Portal Platform is running on the same machine as Tomcat, other ports need to be changed in addition to 8080 to avoid port conflicts. They can be changed to any free port. For example, you can change the admin port from 8005 to 8805, and AJP port from 8009 to 8809.
  3. Tomcat should now allow access to http://localhost:8888/josso/signon/login.do. However, if you are using the SSO Authentication plug-in, the login will not be available as you must set up your JBoss Portal Platform instance to use it.
    JOSSO Login Page

    Figure 26.1. JOSSO Login Page


26.3.2.3. Setting up the JOSSO Client

  1. Some of the configuration properties in JPP_HOME/standalone/configuration/gatein/configuration.properties need to be set on the client server.
    Locate the #SSO section of the file and edit it to match the sample below:
    #SSO
    gatein.sso.enabled=true
    gatein.sso.callback.enabled=${gatein.sso.enabled}
    gatein.sso.login.module.enabled=${gatein.sso.enabled}
    gatein.sso.login.module.class=org.gatein.sso.agent.login.SSOLoginModule
    gatein.sso.josso.agent.config.file=sso/josso/1.8/josso-agent-config.xml
    gatein.sso.josso.properties.file=file:${jboss.home.dir}/standalone/configuration/gatein/configuration.properties
    gatein.sso.josso.host=localhost:8888
    gatein.sso.josso.base.url=http://${gatein.sso.josso.host}/josso/signon
    gatein.sso.server.url=${gatein.sso.josso.base.url}/login.do
    gatein.sso.portal.url=http://localhost:8080
    gatein.sso.filter.logout.class=org.gatein.sso.agent.filter.JOSSOLogoutFilter
    gatein.sso.filter.logout.url=${gatein.sso.josso.base.url}/logout.do
    gatein.sso.filter.login.sso.url=${gatein.sso.server.url}?josso_back_to=${gatein.sso.portal.url}/@@portal.container.name@@/initiatessologin
    
    Some of the properties differ for JOSSO:
    • The Logout filter is org.gatein.sso.agent.filter.JOSSOLogoutFilter.
    • gatein.sso.josso.host points to the location of the JOSSO server.
    • gatein.sso.portal.url must be changed if you intend to access JBoss Portal Platform on any URL other than localhost:8080.
    • The gatein.sso.josso.agent.config.file property points to the location of the Agent configuration file, which is relative to classpath. Therefore the agent file location is actually located at JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/classes/sso/josso/1.8/josso-agent-config.xml.
      In the majority of cases, nothing in this file will need to be configured beyond the defaults.
  2. JOSSO has some specific dependencies, which differ between various versions. The original org.gatein.sso SSO module must be replaced with one appropriate for your version of JOSSO. The alternate modules are available in the JOSSO download.
    1. Delete the JPP_HOME/modules/org/gatein/sso directory.
    2. Copy the SSO_HOME/josso/gatein-josso-<version>/modules/org/gatein/sso directory into JPP_HOME/modules/org/gatein/.
From now on, all links redirecting to the user authentication pages will redirect to the JOSSO centralized authentication form. If you set Authentication plug-in for JOSSO, you can login with JBoss Portal Platform credentials (like john/gtn) on JOSSO side.

26.3.3. JOSSO 2.2

JOSSO 2.2 takes a different approach to SSO than JOSSO 1.8. It is designed to allow users to create their own SSO environment by modelling it in a flash web application called atricore-console.
Unfortunately, this makes it more difficult to use the SSO Authentication plug-in as it is not easily possible to configure an existing JOSSO 2.2 environment via Spring XML files. Using the AuthenticationPlugin with JOSSO 2.2 is not supported.

26.3.3.1. JOSSO 2.2 Server Setup

You can download JOSSO 2.2.0 from JOSSO site and follow the instructions from the JOSSO 2 quickstart in http://www.josso.org/confluence/display/JOSSO1/JOSSO2+Quick+start .
After unzipping the download and running the JOSSO, you can access the atricore console at http://server.local.network:8081/atricore-console (server.local.network is the virtual host defined in /etc/hosts).
  1. Login to the portal with the username/password combination; admin/admin.
  2. Create a new, empty Identity appliance with the following details:

    Table 26.1. 

    Setting Value
    Name MYFIRSTIA
    Realm name com.mycompany.myrealm
    Appliance location http://server.local.network:8081

  3. Create a new Identity provider named AcmeIDP (use the default settings).
  4. Create an Identity vault IDPUsers and connect it with AcmeIDP via Identity lookup connection.
  5. Create a Service provider called SP1 but let the hosts to be on server.local.network:8081.
  6. Create an Identity vault SP1Users and wire it with SP1 via Identity lookup connection.
  7. Create empty temporary directory /tmp/tomcat7 and then in the atricore console create new Execution environment of type Tomcat with the following parameters:

    Table 26.2. 

    Setting Value
    Name SP1EE
    Version 7.0.x
    Target host Local
    Install home /tmp/tomcat7 (the /tmp/tomcat7 directory must exist, but it can be empty).

  8. Wire SP1 and SP1EE via an Activation connection. All parameters of the connection can keep their default values, with the exception of the Partner application location parameter, whose value needs to be changed to http://localhost:8080/portal.
  9. Wire SP1 and AcmeIDP via Federated connection.
  10. Click Save and save this model.
  11. Go to the Identity appliance life cycle management tab and go through life cycle of Identity appliance (SavedStagedDeployedStarted) as suggested in the quickstart.
  12. Go to the Account & Entitlement management tab and create users. Users must be created this way because REST callbacks to the Portal are not supported in this release.
    This example will create the following user/password accounts: john/password, root/password and demo/password.

26.3.3.2. JOSSO Client Setup

This section assumes that all relevant configurations were made as described in Section 26.3.3.1, “JOSSO 2.2 Server Setup”.
  1. Assuming again that you have JBoss Portal Platform running on JBoss Enterprise Application Platform 6, you need to change some of the properties in the SSO sections of JPP_HOME/standalone/configuration/gatein/configuration.properties to match those below:
    # SSO
    gatein.sso.enabled=true
    gatein.sso.callback.enabled=${gatein.sso.enabled}
    gatein.sso.login.module.enabled=${gatein.sso.enabled}
    gatein.sso.login.module.class=org.gatein.sso.agent.login.SSOLoginModule
    gatein.sso.filter.initiatelogin.enabled=false
    gatein.sso.filter.initiatelogin.josso2.enabled=true
    gatein.sso.josso.agent.config.file=sso/josso/2.2/josso-agent-config.xml
    gatein.sso.josso.properties.file=file:${jboss.home.dir}/standalone/configuration/gatein/configuration.properties
    gatein.sso.portal.url=http://localhost:8080
    gatein.sso.filter.logout.class=org.gatein.sso.agent.filter.JOSSOLogoutFilter
    gatein.sso.filter.logout.url=
    gatein.sso.josso.host=server.local.network:8081
    gatein.sso.server.url=http://${gatein.sso.josso.host}
    gatein.sso.josso.identityApplianceId=MYFIRSTIA
    gatein.sso.josso.partnerAppId=SP1
    gatein.sso.josso.partnerAppPoint=SP1EE
    gatein.sso.filter.login.sso.url=${gatein.sso.server.url}/IDBUS/${gatein.sso.josso.identityApplianceId}/${gatein.sso.josso.partnerAppPoint}/JOSSO/SSO/REDIR?josso_back_to=${gatein.sso.portal.url}/@@portal.container.name@@/josso_security_check&josso_partnerapp_id=${gatein.sso.josso.partnerAppId}
    
    Note that gatein.sso.filter.logout.url is empty now as the logout URL will be obtained from the JOSSO agent configuration set in file JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/classes/sso/josso/2.2/josso-agent-config.xml.
  2. Update the SSO module in JBoss Enterprise Application Platform 6:
    1. Delete the JPP_HOME/modules/org/gatein/sso directory.
    2. Copy the SSO_HOME/josso/gatein-josso-182/modules/org/gatein/sso into JPP_HOME/modules/org/gatein/ directory.
  3. Test the configuration:
    1. Start the Portal.
    2. Access http://localhost:8080/portal and click Sign in. You will be redirected to the JOSSO instance, but you will need to login with the username and password created via the JOSSO console (for example john/password) as REST callbacks are not supported.
    After a successful login to JOSSO, you will be redirected to the portal authenticated as john.

26.4. OpenAM

OpenAM is an open source access management, entitlements and federation server platform. It is a successor of OpenSSO, the access management and federation server platform whose integration was available in JBoss Enterprise Portal Platform 5. As the development of OpenSSO has been discontinued, the OpenSSO integration has been replaced with OpenAM integration in JBoss Portal Platform 6.

26.4.1. Login and Logout Workflow

When the OpenAM integration is configured and a user clicks the Sign in link on a JBoss Portal Platform page, they are redirected to the OpenAM login screen, where they provide their login credentials. Authentication on the OpenAM server side is performed by the JBoss Portal Platform SSO Authentication Plugin. The plug-in sends a REST request to JBoss Portal Platform, obtains a response, and authenticates the user on the OpenAM side based on the response.
After successful authentication with OpenAM, an OpenAM authentication ticket is stored in the iPlanetDirectoryPro cookie in the client browser and the user is redirected back to the portal page. When the portal page is requested, the InitiateLoginFilter iterceptor delegates validation of the OpenAM ticket to the OpenSSOAgent component. The OpenSSOAgent then uses the OpenAM REST API to perform back channel validation of the ticket with the OpenAM server. After successful validation, user identity is established and the user is logged in to JBoss Portal Platform.
When logout is requested by clicking the Sign out button on a portal page, the OpenSSOLogoutFilter interceptor performs logout on both JBoss Portal Platform and the OpenAM server.
The OpenAM login and logout workflow is similar to the workflow that users go through with CAS, so you can refer to Section 26.2.1, “Authentication Process” for more detailed information.

26.4.2. Obtaining OpenAM

OpenAM must be obtained from Forgerock. Only the 9.5 and 10.0 versions of OpenAM are fully tested and supported with JBoss Portal Platform 6. Integration with other versions—including OpenSSO, the predecessor of OpenAM—may be functional, but has not been fully tested and is therefore not supported.
The procedures in this section assume that OpenAM is obtained in the form of a ZIP distribution. Once downloaded, extract the contents of the ZIP package into a location of your choice. This location will be referred to as OPENAM_HOME in the following text.

26.4.3. OpenAM Server Setup

This section contains procedures that need to be followed to set up an OpenAM server for authentication against JBoss Portal Platform. The authentication set up by these procedures is ensured by the JBoss Portal Platform SSO Authentication Plugin. The plug-in will be installed in OpenAM and configured to perform authentication against the portal using a REST callback.

Note

Using the REST callback as presented in this section is not mandatory. You can achieve authentication on the OpenAM side by any other means according to your preference.

Procedure 26.5. Deploying the OpenAM Server

  1. Obtain a copy of Tomcat 7 and extract it into a suitable location. This location will be referred to as TOMCAT_HOME further in the text.
  2. Deploy OpenAM to Tomcat by copying the OPENAM_HOME/opensso/deployable-war/opensso.war archive to the TOMCAT_HOME/webapps/ directory.

    Note

    The name of the WAR file is still opensso.war, even if it contains OpenAM, which is the successor of OpenSSO. The context of the OpenAM web application has also kept the name /opensso.
  3. Change the default Tomcat port to avoid conflict with the default JBoss Portal Platform port. For the purpose of this example, let's change it to 8888 by editing the TOMCAT_HOME/conf/server.xml configuration file and replacing the 8080 port number with 8888.
  4. If JBoss Portal Platform is running on the same machine as Tomcat, other Tomcat port numbers also need to be changed to avoid conflicts. These port numbers can be changed to any unassigned port numbers available on the machine. For example, you can change the admin port from 8005 to 8805 and the AJP port from 8009 to 8809 if the latter are not assigned.

Procedure 26.6. Adding the Authentication Plug-in

  1. Copy the contents of the JPP_DIST/gatein-sso/opensso/plugin/ directory to TOMCAT_HOME/webapps/opensso/. This will add:
    • the AuthenticationPlugin.xml file to the TOMCAT_HOME/webapps/opensso/config/auth/default/ directory. The file contains the following configuration:
      <?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 OpenAM Login">
          <NameCallback>
            <Prompt>
              Username
            </Prompt>
          </NameCallback>
          <PasswordCallback echoPassword="false">
            <Prompt>
              Password
            </Prompt>
          </PasswordCallback>
        </Callbacks>
      </ModuleProperties>
      
    • the sso-opensso-plugin-<VERSION>.jar, sso-common-plugin-<VERSION>.jar and commons-httpclient-<VERSION>.jar archives to the TOMCAT_HOME/webapps/opensso/WEB-INF/lib directory.
    • the gatein.properties file to the TOMCAT_HOME/webapps/opensso/WEB-INF/classes/ directory. You may need to change the values specified in this file to match the configuration of the JBoss Portal Platform instance. The values will be used by the authentication plugin to establish the REST connection to the portal.
  2. Start Tomcat and verify that you are able to access the OpenAM user interface at http://localhost:8888/opensso.

Procedure 26.7. Configuring a Realm in OpenAM User Interface

  1. Direct your browser to http://localhost:8888/opensso.
  2. Create the default configuration.
  3. Login as amadmin and navigate to ConfigurationAuthentication.
  4. Select the Core link, add a new value containing the org.gatein.sso.opensso.plugin.AuthenticationPlugin class name and click Save. This step is important for the JBoss Portal Platform SSO Authentication Plugin to be available among other OpenAM authentication modules.
  5. Go to the Access control tab and create a new realm called gatein.
  6. Go to the gatein realm and click the Authentication tab. In the Authentication chaining section at the bottom of the tab, click ldapService. Here, change the selection from Datastore, which is the default module in the authentication chain, to AuthenticationPlugin. This ensures that authentication of the gatein realm will be performed using the JBoss Portal Platform REST service instead of the OpenAM LDAP server.
  7. Still on the Authentication tab of the gatein realm, click the All Core Settings button. When the settings are displayed, change the UserProfile value from Required to Dynamic. This is needed because JBoss Portal Platform users are not initially present in the OpenAM LDAP server datastore. The Dynamic value ensures that all users are automatically added to the datastore after their first successful authentication.
  8. Go to Access controlTop level realmPrivilegesAll authenticated users and check the following check boxes:
    • Read and write access only for policy properties
    • Read and write access to all realm and policy properties
    Adding these privileges allows REST access to the OpenAM server.
  9. Repeat the previous step to increase the privileges also for the gatein realm.

26.4.4. JBoss Portal Platform Setup as OpenAM Client

On the JBoss Portal Platform server, you need to ensure proper configuration of single sign-on properties in the JPP_HOME/standalone/configuration/gatein/configuration.properties file. Locate the SSO section in this file and change/add properties in the section as follows:
# SSO
gatein.sso.enabled=true
gatein.sso.callback.enabled=${gatein.sso.enabled}
gatein.sso.login.module.enabled=${gatein.sso.enabled}
gatein.sso.login.module.class=org.gatein.sso.agent.login.SSOLoginModule
gatein.sso.server.url=http://localhost:8888/opensso
gatein.sso.openam.realm=gatein
gatein.sso.portal.url=http://localhost:8080
gatein.sso.filter.logout.class=org.gatein.sso.agent.filter.OpenSSOLogoutFilter
gatein.sso.filter.logout.url=${gatein.sso.server.url}/UI/Logout
gatein.sso.filter.login.sso.url=${gatein.sso.server.url}/UI/Login?realm=${gatein.sso.openam.realm}&goto=${gatein.sso.portal.url}/@@portal.container.name@@/initiatessologin
Most of the properties are already described for CAS integration in Section 26.2.6.2, “Portal configuration.properties for CAS SSO”, so you can refer to that section for detailed information.
Values of some properties are different with OpenAM, specifically the URLs for login/logout redirection, the logout filter class and the gatein.sso.server.url property, which points to the location of the OpenAM server. The gatein.sso.openam.realm value points to the realm created on the OpenAM server in Procedure 26.7, “Configuring a Realm in OpenAM User Interface”.
With all the previously described configuration, all links redirecting users to authentication pages will redirect them to the OpenAM authentication form. On the OpenAM side, users will be able to log in to the gatein realm using their JBoss Portal Platform credentials.
OpenAM Login Page

Figure 26.2. OpenAM Login Page


26.4.5. Cross-domain Authentication with OpenAM

The configuration described in Section 26.4.3, “OpenAM Server Setup” and Section 26.4.4, “JBoss Portal Platform Setup as OpenAM Client” assumes that JBoss Portal Platform and OpenAM are deployed on the same server or in the same DNS domain.
In such an environment, OpenAM adds the iPlanetDirectoryPro cookie for the shared domain to the client browser. It stores the authentiction token in the cookie and performs redirection to the OpenSSOAgent in JBoss Portal Platform. The agent is able to read the token from the cookie and perform its validation because the portal is running in the same domain.
This is not possible when the JBoss Portal Platform server and the OpenAM server are running in different domains and cannot share cookies. OpenAM provides the CDCServlet to solve this situation. Authenticated users can send requests to this servlet and the servlet responds with an encoded SAML message containing the SSO token and other information required to authenticate. The portal agent is then able to parse and validate the message, obtain the SSO token and establish the iPlanetDirectoryPro cookie for the domain where JBoss Portal Platform is deployed. Once the OpenAM agent on the portal side has token, it can perform further validation of this token and finish authentication of the user.
Detailed information about the CDCServlet servlet can be found in Sun OpenSSO Enterprise Deployment Planning Guide.

Procedure 26.8. Cross-domain Authentication Configuration

For the purpose of this example, we will assume that the OpenAM server is deployed on the opensso.mydomain.com host and the JBoss Portal Platform server on the portal.yourdomain.com host.
If you want to simulate this configuration on a single machine, you can do it by configuring virtual hosts. On Linux, this can be achieved by editing the /etc/hosts file and adding records similar to the following, with the IP addresses changed accordingly:
opensso.mydomain.com 192.168.2.7
portal.yourdomain.com 10.11.12.13
  1. On the JBoss Portal Platform side, single sign-on configuration in the configuration.properties file needs to be specified as follows:
    # SSO
    gatein.sso.enabled=true
    gatein.sso.callback.enabled=${gatein.sso.enabled}
    gatein.sso.login.module.enabled=${gatein.sso.enabled}
    gatein.sso.login.module.class=org.gatein.sso.agent.login.SSOLoginModule
    gatein.sso.server.url=http://opensso.mydomain.com:8888/opensso
    gatein.sso.openam.realm=gatein
    gatein.sso.portal.url=http://portal.yourdomain.com:8080
    gatein.sso.filter.logout.class=org.gatein.sso.agent.filter.OpenSSOLogoutFilter
    gatein.sso.filter.logout.url=${gatein.sso.server.url}/UI/Logout
    gatein.sso.filter.login.enabled=false
    gatein.sso.filter.login.openamcdc.enabled=true
    gatein.sso.filter.login.sso.url=${gatein.sso.server.url}/cdcservlet
    
    As we need to redirect requests to the CDCServlet, the gatein.sso.filter.login.sso.url property points to the URL of the servlet. It is also necessary to use a modified version of the LoginRedirectFilter interceptor. That is why the gatein.sso.filter.login.openamcdc.enabled value is changed to true and the gatein.sso.filter.login.enabled value is now set to false.
  2. Access the OpenAM user interface at http://opensso.mydomain.com:8888/opensso and log in as amadmin.
  3. Navigate to Access Control"gatein" realmAgentsWeb.
  4. Create a new web agent through the wizard using the following properties:
    Creating this agent is required for the CDCServlet to work properly. If you have more portal servers on different hosts, you may want to create an agent for each of them. Please refer to OpenAM Administration Guide for more details.

26.5. Simple and Protected GSSAPI Negotiation Mechanism (SPNEGO)

The Simple and Protected GSSAPI Negotiation Mechanism (SPNEGO) uses desktop credentials provided during desktop authentication to transparently authenticate a portal user through a web browser.
Let's illustrate this mechanism on a typical use case:
  1. A user logs into their desktop computer with a login that is governed by an Active Directory domain.
  2. The user launches a web browser to access a web application that is hosted on JBoss Portal Platform and uses JBoss Negotiation.
  3. The browser transfers the desktop credentials to the web application.
  4. JBoss Portal Platform uses background GSS messages to validate the user's Kerberos ticket with the Active Directory (or another Kerberos server).
  5. The user experiences a seamless sign-on into the web application.

26.5.1. SPNEGO Server Configuration

This section describes the steps to set up a Kerberos server on Linux. The server will then be used for SPNEGO authentication against JBoss Portal Platform.

Note

The procedure below only describes the basic steps to configure a SPNEGO server in a Linux environment. If you are already familiar with SPNEGO, or if you are using Windows and an Active Directory domain, you can proceed to Procedure 26.11, “Configuring SPNEGO Integration with JBoss Portal Platform” to see how SPNEGO can be integrated with JBoss Portal Platform.
Kerberos setup is also dependent on your Linux distribution, so the steps can be slightly different in your environment.

Procedure 26.9. Configuring the SPNEGO Server

  1. Ensure correct host name to IP address mapping on the machine where Kerberos and JBoss Portal Platform are running. For example, if the machine's IP address is 192.168.1.88 and you want it to be accessed under the server.local.network host name, add the following line to the /etc/hosts file:
    192.168.1.88  server.local.network
    

    Note

    It is not recommended you use loopback addresses.
  2. Install Kerberos with these packages: krb5-admin-server, krb5-kdc, krb5-config, krb5-user, krb5-clients, and krb5-rsh-server.
  3. Edit the Kerberos configuration in the /etc/krb5.config file:
    • Uncomment the following lines:
      default_tgs_enctypes = rc4-hmac
      default_tkt_enctypes = rc4-hmac
      permitted_enctypes = rc4-hmac
      
    • Add local.network as a default realm, add it to the list of realms, and remove the remaining realms. The resulting content of the file will be as follows:
      [libdefaults]
          default_realm = LOCAL.NETWORK
       
      # The following krb5.conf variables are only for MIT Kerberos.
          krb4_config = /etc/krb.conf
          krb4_realms = /etc/krb.realms
          kdc_timesync = 1
          ccache_type = 4
          forwardable = true
          proxiable = true
       
      # The following encryption type specification will be used by MIT Kerberos
      # if uncommented.  In general, the defaults in the MIT Kerberos code are
      # correct and overriding these specifications only serves to disable new
      # encryption types as they are added, creating interoperability problems.
      #
      # Thie only time when you might need to uncomment these lines and change
      # the enctypes is if you have local software that will break on ticket
      # caches containing ticket encryption types it doesn't know about (such as
      # old versions of Oracle).
       
          default_tgs_enctypes = rc4-hmac
          default_tkt_enctypes = rc4-hmac
          permitted_enctypes = rc4-hmac
       
      # The following libdefaults parameters are only for Heimdal Kerberos.
          v4_instance_resolve = false
          v4_name_convert = {
              host = {
                  rcmd = host
                  ftp = ftp
              }
              plain = {
                  something = something-else
              }
          }
          fcc-mit-ticketflags = true
       
      [realms]
          LOCAL.NETWORK = {
              kdc = server.local.network
              admin_server = server.local.network
          }
       
      [domain_realm]
          .local.network = LOCAL.NETWORK
          local.network = LOCAL.NETWORK
       
      [login]
          krb4_convert = true
          krb4_get_tickets = false
      
  4. Modify KDC configuration.
    • Edit the /etc/krb5kdc/kdc.conf file as shown below:
      [kdcdefaults]
      kdc_ports = 750,88
       
      [realms]
          LOCAL.NETWORK = {
              database_name = /var/lib/krb5kdc/principal
              admin_keytab = FILE:/etc/krb5.keytab
              acl_file = /etc/krb5kdc/kadm5.acl
              key_stash_file = /etc/krb5kdc/stash
              kdc_ports = 750,88
              max_life = 10h 0m 0s
              max_renewable_life = 7d 0h 0m 0s
              master_key_type = rc4-hmac
              supported_enctypes = rc4-hmac:normal
              default_principal_flags = +preauth
          }
       
      [logging]
              kdc = FILE:/tmp/kdc.log
              admin_server = FILE:/tmp/kadmin.log
      
    • Create a KDC database.
      sudo krb5_newrealm
      
    • Start the KDC and Kerberos servers.
      sudo /etc/init.d/krb5-kdc restart
      sudo /etc/init.d/krb-admin-server restart
      
  5. Add principals and create keys.
    • Start an interactive kadmin session and create the necessary principals.
      sudo kadmin.local
      
    • Add the JBoss Portal Platform machine and keytab file.
      addprinc -randkey HTTP/server.local.network@LOCAL.NETWORK
      ktadd HTTP/server.local.network@LOCAL.NETWORK
      
    • Add the default JBoss Portal Platform user accounts and enter a password for each of them.
      addprinc john
      addprinc demo
      addprinc root
      
  6. Issue the following command to test your setup:
    kinit -A demo
    
    • If everything is set up well, you are required to enter the password created for the demo user in the previous step.
      Without the -A option, the Kerberos ticket validation would involve reverse DNS lookups, which can be difficult to debug with certain network DNS configurations. This is a production level security feature and it is not necessary to use it in a development environment. In production environment, it is recommended to avoid using the -A option.
    • After successful login to Kerberos, you can display your Kerberos ticket using the following command:
      klist
      
    • If you want to log out and destroy your Kerberos ticket, issue the following command:
      kdestroy
      

26.5.2. Client Configuration

After performing the server configuration described in Section 26.5.1, “SPNEGO Server Configuration”, you need to enable negotiated authentication in web browsers on client machines from which users will authenticate.
Procedure 26.10, “Enabling Negotiated Authentication in Mozilla Firefox” describes how negotiated authentication can be enabled in Mozilla Firefox. For instructions on enabling negotiated authentication in a different browser, consult the browser's documentation.

Procedure 26.10. Enabling Negotiated Authentication in Mozilla Firefox

  1. Start Mozilla Firefox and enter about:config into the address field.
  2. Search for the network.negotiate-auth preferences and set the values as follows:
    network.negotiate-auth.allow-proxies = true
    network.negotiate-auth.delegation-uris = .local.network
    network.negotiate-auth.gsslib (no-value)
    network.negotiate-auth.trusted-uris = .local.network
    network.negotiate-auth.using-native-gsslib = true
    

26.5.3. JBoss Portal Platform Configuration

JBoss Portal Platform uses JBoss Negotiation to enable SPNEGO-based desktop single sign-on for the portal. The following procedure describes how to integrate SPNEGO with JBoss Portal Platform.

Procedure 26.11. Configuring SPNEGO Integration with JBoss Portal Platform

  1. Modify the # SSO section of the JPP_HOME/standalone/configuration/gatein/configuration.properties file, replacing the original content with the following properties:
    # SSO
    gatein.sso.enabled=true
    gatein.sso.callback.enabled=false
    gatein.sso.skip.jsp.redirection=false
    gatein.sso.login.module.enabled=false
    gatein.sso.filter.login.sso.url=/@@portal.container.name@@/dologin
    gatein.sso.filter.logout.enabled=false
    gatein.sso.filter.initiatelogin.enabled=false
    gatein.sso.valve.enabled=true
    gatein.sso.valve.class=org.gatein.sso.spnego.GateInNegotiationAuthenticator
    
    The list below explains the meaning of individual properties:
    gatein.sso.enabled
    This is a general property used to inform JBoss Portal Platform that clicking the Sign in link will redirect users to a URL ending with the /portal/dologin suffix.
    gatein.sso.callback.enabled
    This property can be set to false as REST callbacks are not required for SPNEGO integration.
    gatein.sso.skip.jsp.redirection
    This property must be set to false for SPNEGO integration, especially to ensure fallback to FORM authentication.
    gatein.sso.login.module.enabled
    This property can be set to false as a different set of login modules is needed for SPNEGO integration.
    gatein.sso.filter.login.sso.url
    This value ensures that clicking the Sign in link will redirect users to the /portal/dologin URL, which is a secured URL declared in the <security-constraint> section of JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/web.xml file, allowing the GateInNegotiationAuthenticator valve to intercept the HTTP request.
    gatein.sso.filter.logout.enabled, gatein.sso.filter.initiatelogin.enabled
    These properties can be set to false as the logout filter and the InitiateLoginFilter are not needed for SPNEGO integration.
    gatein.sso.valve.enabled
    SPNEGO integration requires a custom Tomcat valve to intercept HTTP requests for secured URLs. The SSODelegateValve is defined in the JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/jboss-web.xml file and is used only if this option is set to true. The purpose of the valve is to delegate the real work to another valve declared in the gatein.sso.valve.class property. This eliminates the need to edit configuration in the jboss-web.xml file.
    gatein.sso.valve.class
    The delegate valve for SPNEGO is org.gatein.sso.spnego.GateInNegotiationAuthenticator. It is used to establish identity of the client by exchanging several handshakes with client browser. The valve is able to obtain and parse an SPNEGO token and resend it to the JAAS layer, where login modules (especially the SPNEGOLoginModule) are able to verify the validity of the wrapped Kerberos token and establish user identity at the JBoss Portal Platform layer.
  2. Modify configuration of the security subsystem in the JPP_HOME/standalone/configuration/standalone.xml file:
    1. Rename the existing gatein-domain security domain to gatein-form-auth-domain.
    2. Add a new definition of the gatein-domain security domain.
    3. Add a new security domain called host. Values of the following properties need to be specified in accordance with your environment setup:
      principal
      The HTTP server principal specified in your Kerberos keytab. In Procedure 26.9, “Configuring the SPNEGO Server”, we used the LOCAL.NETWORK Kerberos realm and the server.local.network host and subsequently added the HTTP/server.local.network@LOCAL.NETWORK principal to the keytab.
      keyTab
      The path to the file with your Kerberos keytab. In Procedure 26.9, “Configuring the SPNEGO Server”, we used the /etc/krb5.keytab location. Please note that this file should be readable by the user under whose account JBoss Portal Platform is executed. Generally, Kerberos keytab files should not be readable by normal OS users as they contain encrypted keys of server principals. Consult Kerberos documentation for more details.
      debug
      Enabling this option adds more log messages into JBoss Portal Platform console and log file. It is useful to have this option enabled during configuration, but it is advised to disable it in production to avoid unnecessarily large server logs.
    The code extract below shows the expected result of the security domain configuration.
    <security-domain name="gatein-form-auth-domain" cache-type="default">
      <authentication>
        <login-module code="org.gatein.sso.integration.SSODelegateLoginModule" flag="required">
          <module-option name="enabled" value="${gatein.sso.login.module.enabled}" />
          <module-option name="delegateClassName" value="${gatein.sso.login.module.class}" />
          <module-option name="portalContainerName" value="portal" />
          <module-option name="realmName" value="gatein-domain" />
          <module-option name="password-stacking" value="useFirstPass" />
        </login-module>
        <login-module code="org.exoplatform.services.security.j2ee.JBossAS7LoginModule" flag="required">
          <module-option name="portalContainerName" value="portal"/>
          <module-option name="realmName" value="gatein-domain"/>
        </login-module>
      </authentication>
    </security-domain>
    
    <security-domain name="gatein-domain" cache-type="default">
      <authentication>
        <login-module code="org.gatein.sso.spnego.SPNEGOLoginModule" flag="requisite">
          <module-option name="password-stacking" value="useFirstPass"/>
          <module-option name="serverSecurityDomain" value="host"/>
          <module-option name="removeRealmFromPrincipal" value="true"/>
          <module-option name="usernamePasswordDomain" value="gatein-form-auth-domain"/>
        </login-module>
        <login-module code="org.gatein.sso.agent.login.SPNEGORolesModule" flag="required">
          <module-option name="password-stacking" value="useFirstPass"/>
          <module-option name="portalContainerName" value="portal"/>
          <module-option name="realmName" value="gatein-domain"/>
        </login-module>
      </authentication>
    </security-domain>
    
    <security-domain name="host">
      <authentication>
        <login-module code="com.sun.security.auth.module.Krb5LoginModule" flag="required">
          <module-option name="storeKey" value="true" />
          <module-option name="useKeyTab" value="true" />
          <module-option name="principal" value="HTTP/server.local.network@LOCAL.NETWORK" />
          <module-option name="keyTab" value="/etc/krb5.keytab" />
          <module-option name="doNotPrompt" value="true" />
          <module-option name="debug" value="true" />
        </login-module>
      </authentication>
    </security-domain>
    
  3. While logged in as a user with read permissions for the Kerberos keytab file, start JBoss Portal Platform using the following command:
    ./standalone.sh -Djava.security.krb5.realm=LOCAL.NETWORK -Djava.security.krb5.kdc=server.local.network -b server.local.network
    

26.5.4. SPNEGO Configuration Testing

Once you have performed the configuration above, you can follow Procedure 26.12, “Testing the SPNEGO Configuration” to verify that the configured authentication is functional.

Procedure 26.12. Testing the SPNEGO Configuration

  1. Log in to Kerberos using the following command:
    kinit -A demo
    
  2. In a browser, access http://server.local.network:8080/portal and click the Sign In link in the top menu of the portal. If the authentication is configured correctly, you will be automatically signed in as the demo user.
  3. Destroy the previously obtained Kerberos ticket using the following command:
    kdestroy
    
  4. In the browser, log out and log back in. In case of correct configuration, you will be redirected to the login screen of JBoss Portal Platform. This happens because you no longer have an active Kerberos ticket and a fallback to the standard FORM authentication occurred.

26.5.5. Additional Configuration

26.5.5.1. Disabling Fallback to FORM Authentication

As demonstrated in Procedure 26.12, “Testing the SPNEGO Configuration”, users trying to sign in without a valid Kerberos ticket are automatically redirected to the JBoss Portal Platform logon page. There, they can perform standard FORM authentication using their user name and password.
If you want to disable FORM authentication to allow only users with a valid Kerberos ticket to sign in, you need to comment out the usernamePasswordDomain option in the SPNEGOLoginModule configuration in the JPP_HOME/standalone/configuration/standalone.xml file.
<login-module
    code="org.gatein.sso.spnego.SPNEGOLoginModule" flag="requisite">
    <module-option name="password-stacking" value="useFirstPass"/>
    <module-option name="serverSecurityDomain" value="host"/>
    <module-option name="removeRealmFromPrincipal" value="true"/>
<!--<module-option name="usernamePasswordDomain" value="gatein-form-auth-domain"/>-->
</login-module>

26.5.5.2. Enabling Logging

To enable logging of events related to SPNEGO authentication, you can add the following entries to the logging subsystem in the JPP_HOME/standalone/configuration/standalone.xml file:
<logger category="org.gatein.sso">
    <level name="TRACE"/>
</logger>
<logger category="org.jboss.security.negotiation">
    <level name="TRACE"/>
</logger>
Increased logging of Kerberos events to standard output can be enabled by launching JBoss Portal Platform with the -Dsun.security.krb5.debug=true option.
./standalone.sh -Djava.security.krb5.realm=LOCAL.NETWORK -Djava.security.krb5.kdc=server.local.network -b server.local.network -Dsun.security.krb5.debug=true

26.6. Single Sign-On in a Cluster

In a cluster, the JBoss SSO valve can be used to authenticate a user on one JBoss Portal Platform node and have that authentication automatically carried across to other nodes in the cluster.

26.6.1. Default Configuration

The JBoss SSO valve is enabled by default. The enablement is ensured by the following JBoss Web subsystem configuration entry in the JPP_HOME/standalone/configuration/standalone-ha.xml file:
<sso cache-container="web" cache-name="sso" reauthenticate="false" />
When a loadbalancer is used in a cluster, no further configuration is needed to set up single sign-on. All JBoss Portal Platform servers in the cluster are accessed through the same URL, which is the URL of the loadbalancer. Automatic single sign-on is performed when the loadbalancer redirects client requests to individual nodes in the cluster.

26.6.2. Clustered Single Sign-On in a Shared DNS Domain

If multiple JBoss Portal Platform servers are accessed through different URLs in the same DNS domain, single sign-on can be configured by adding the domain parameter to the sso configuration entry.
<sso cache-container="web" cache-name="sso" reauthenticate="false" domain="yourdomain.com"/>
The parameter must be added to the entry on all servers in the cluster and the name of the shared DNS domain must be specified as its value. This configuration ensures that the JSESSIONIDSSO cookie will be scoped to the specified domain, which is otherwise scoped only to the host where the initial authentication was performed.

Procedure 26.13. Configuring and Testing Single-Sign On in a Shared DNS Domain

This procedure demonstrates the configuration and testing of single sign-on for two JBoss Portal Platform server instances running in a shared domain on a single physical Linux machine.
It is expected that each instance is installed in a separate directory in the machine's file system, and that the 192.168.210.101 and 192.168.210.102 virtual IP addresses are available on the machine.
  1. Map the IP addresses to domain names within the same domain by adding the following lines to the /etc/hosts file:
    192.168.210.101 machine1.yourdomain.com
    192.168.210.102 machine2.yourdomain.com
    
  2. Open the JPP_HOME/standalone/configuration/standalone-ha.xml file on both instances, add the domain parameter to the sso entry and specify the name of the shared DNS domain in its value:
    <sso cache-container="web" cache-name="sso" reauthenticate="false" domain="yourdomain.com"/>
    
  3. By default, the standalone-ha.xml file is configured to use a shared H2 database, which is intended to be used only for testing purposes. Start the database by issuing the following command in the JPP_HOME directory of the first instance:
    java -cp modules/com/h2database/h2/main/h2-<VERSION>.jar org.h2.tools.Server
    
  4. Start the first instance by issuing the following command in its JPP_HOME/bin/ directory:
    ./standalone.sh -b machine1.yourdomain.com -c standalone-ha.xml -Djboss.node.name=node1
    
  5. Start the second instance by issuing the following command in its JPP_HOME/bin/ directory:
    ./standalone.sh -b machine2.yourdomain.com -c standalone-ha.xml -Djboss.node.name=node2
    
  6. Access the first instance at http://machine1.yourdomain.com:8080/portal and log in as a user.
  7. Access the second instance at http://machine2.yourdomain.com:8080/portal. When the page loads, you will be automatically logged in with the same user account that you used on the first server.
  8. Log out on any of the two instances. Then switch to the other instance and verify that you have been logged out of it as well.

26.6.3. Reauthentication

The JBoss SSO valve can also be used to authenticate with any other web application. If that application uses the same roles as the main JBoss Portal Platform instance, no further configuration is required. Because the JBoss SSO valve includes the same JAAS principal in all HTTP requests, even in requests to other web applications, matching roles ensure successful authentication with those applications.
To enable single sing-on authentication with an application that uses different roles, you need to set the reauthenticate parameter of the sso JBoss Web subsystem configuration entry to true.
<sso cache-container="web" cache-name="sso" reauthenticate="true" />
The true value ensures that reauthentication with user credentials will be performed against the web application's security domain in each HTTP request. This will enforce creation of a new principal with updated roles for the web application. As user credentials are used for authentication in this case, it is required that the same user credentials exist in both the web application and the JBoss Portal Platform instance.

Chapter 27.  LDAP Integration

Notational Device

For ease of readability the following section uses the notational device ID_HOME to represent the file path JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/organization/, as this directory is the root of all JBoss Portal Platform's identity-related configuration.
LDAP (Lightweight Directory Access Protocol) is a set of open protocols used to access centrally stored information over a network. It is based on the X.500 standard for directory sharing, but is less complex and resource-intensive.
Using a client/server architecture, LDAP provides a reliable means to create a central information directory accessible from the network. When a client attempts to modify information within this directory, the server verifies the user has permission to make the change, and then adds or updates the entry as requested. To ensure the communication is secure, the Secure Sockets Layer (SSL) or Transport Layer Security (TLS) cryptographic protocols can be used to prevent an attacker from intercepting the transmission.
LDAP provides the protocols required to manage the data stored in a Directory Server. A Directory Server contains information about resources available (user accounts and printers for example) and their location on the network.
Refer to the JBoss Portal Platform Supported Configurations page for a list of supported directory servers.

Examples

JBoss Portal Platform includes several example LDAP configuration .xml files and .ldif (LDAP Data Interchange Format) data files.
These examples are in the ID_HOME/picketlink-idm/examples directory and can be deployed in a testing environment to assist in configuring LDAP.

Procedure 27.1. LDAP Setup

  1. Install your LDAP server by following the installation instructions provided for the product you are using.
    If you are installing the Red Hat Directory Server, you should refer to the Installation Guide at https://access.redhat.com/knowledge/docs/Red_Hat_Directory_Server/.
    If you are using a third party directory server (OpenDS, OpenLDAP or Microsoft Active Directory), refer to the appropriate documentation for that product.
  2. Optional: Import an ldif file and populate the Directory Server.
  3. Start the Directory Server.

27.1. LDAP in Read-only Mode

If LDAP is configured to operate in read-write mode, changes to user and group information made in the portal platform is written back to the directory server.
If LDAP is operating in read-only mode, existing user and group information is consumed from the directory server, and all new user data entries created using the Portal User Interface are stored in the portal database.
The only exception is that passwords updated through the user interface will also be propagated into the appropriate LDAP entry.

Procedure 27.2. Set up LDAP Read-only Mode

  1. Open the ID_HOME/idm-configuration.xml file.
    JBoss Portal Platform uses the PicketLink IDM framework as the underlying identity storage system, therefore the configuration uses dedicated PicketLink settings.
  2. Comment out the default PicketLink config value:
    <value>war:/conf/organization/picketlink-idm/picketlink-idm-config.xml</value>
    
  3. Uncomment the appropriate sample configuration values as described below, depending on which Directory Server you are implementing:
  4. To use a different LDAP server or directory data, edit the DS-specific .xml file you uncommented in the relevant sub-procedure above, and change the values to suit your requirements.
    Refer to the list in Example 27.1, “LDAP Configuration” for some examples, or refer to the product-specific documentation for more information.
  5. Start the server.
  6. Navigate to the portal homepage (http://localhost:8080/portal) and log in as an administrator.
  7. Navigate to GroupOrganizationUsers and groups management.
    1. Create a new group called acme under the root node.
      • For RHDS, OpenDS and OpenLDAP:
        Create two sub-groups called roles and organization_units.
      • For MSAD:
        Create a subgroup called roles.
Users defined in LDAP should be visible in "Users and groups management" and groups from LDAP should be present as children of /acme/roles and /acme/organization_units.
More information about configuration can be found in the PicketLink IDM 1.x Community Documentation.

Procedure 27.3. Red Hat Directory Server or OpenDS

  1. Uncomment the line under "Read Only "ACME" LDAP Example":
    <!--Read Only "ACME" LDAP Example-->
    <value>war:/conf/organization/picketlink-idm/examples/picketlink-idm-LDAP-acme-config.xml</value>
    
  2. Uncomment the groupTypeMappings under "Uncomment for ACME LDAP example":
    <!-- Uncomment for ACME LDAP example -->
    <entry>
     <key><string>/acme/roles/*</string></key>
     <value><string>acme_roles_type</string></value>
    </entry>
    <entry>
     <key><string>/acme/organization_units/*</string></key>
     <value><string>acme_ou_type</string></value>
    </entry>
    
    Refer to Example 27.2, “Read-only groupTypeMappings” for more information about how these groupTypeMappings operate.

Procedure 27.4. Microsoft Active Directory

  1. Uncomment the line under "MSAD Read Only "ACME" LDAP Example":
    <!--MSAD Read Only "ACME" LDAP Example-->
    <value>war:/conf/organization/picketlink-idm/examples/picketlink-idm-msad-readonly-config.xml</value>
    
  2. Uncomment the groupTypeMappings under "Uncomment for MSAD ReadOnly LDAP example":
    <!-- Uncomment for MSAD ReadOnly LDAP example -->
    <entry>
      <key><string>/acme/roles/*</string></key>
      <value><string>msad_roles_type</string></value>
    </entry>
    
    Refer to Example 27.2, “Read-only groupTypeMappings” for more information about how these groupTypeMappings operate.

Procedure 27.5. OpenLDAP

  1. If you have not done so already, install your LDAP server. Refer to Procedure 27.1, “LDAP Setup” for some assistance.
  2. Uncomment the line under "OpenLDAP ReadOnly "ACME" LDAP Example":
    <!--OpenLDAP ReadOnly "ACME" LDAP Example-->
    <value>war:/conf/organization/picketlink-idm/examples/picketlink-idm-openLDAP-acme-config.xml</value>
    
  3. Uncomment the groupTypeMappings under "Uncomment for ACME LDAP example":
    <!-- Uncomment for ACME LDAP example -->
    <entry>
     <key><string>/acme/roles/*</string></key>
     <value><string>acme_roles_type</string></value>
    </entry>
    <entry>
     <key><string>/acme/organization_units/*</string></key>
     <value><string>acme_ou_type</string></value>
    </entry>
    
    Refer to Example 27.2, “Read-only groupTypeMappings” for more information about how these groupTypeMappings operate.

27.2. LDAP as Default Store

Follow the procedure below to set LDAP up as the default identity store for JBoss Portal Platform. All default accounts and some of groups that come with JBoss Portal Platform will be created in the LDAP store.
The LDAP server will be configured to store part of the JBoss Portal Platform group tree. This means that groups under specified part of the tree will be stored in directory server while all others will be stored in database.

Procedure 27.6. Set up LDAP as Default Identity Store

  1. Install the LDAP server. Refer to Procedure 27.1, “LDAP Setup” for assistance with this step.
  2. Open the ID_HOME/idm-configuration.xml file.
    JBoss Portal Platform uses the PicketLink IDM framework as the underlying identity storage system, hence all the configurations use dedicated PicketLink settings.
  3. Comment out the default Picketlink config value: war:/conf/organization/picketlink-idm/picketlink-idm-config.xml
  4. Complete the steps in the procedure that relate to the chosen LDAP server:
  5. Uncomment the groupTypeMappings under "Uncomment for sample LDAP configuration":
    <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>
    
    Refer to Example 27.3, “Default groupTypeMappings” for more information about how these groupTypeMappings operate.
  6. Uncomment ignoreMappedMembershipTypeGroupList under Uncomment for sample LDAP config.
    <value>
      <string>/platform/*</string>
    </value>
    <value>
      <string>/organization/*</string>
    </value>

    Important

    If this configuration is not uncommented, memberships will be used as both relationships and roles, which may cause duplicated records in the GUI.
    Normally, the same roles being used for LDAP mapping need to be uncommented. User memberships under the specified groups will be created in PicketLink IDM only as relationships, and not as roles.
  7. To use a different LDAP server or directory data, edit the DS-specific .xml file you uncommented in Step 4 above and change the values to suit your requirements.
    Refer to the list in Example 27.1, “LDAP Configuration” for some configuration examples, or refer to the LDAP server product-specific documentation for more information.
  8. Result

    All portal groups under /platform and under /organization groups (for example /platform/users, /platform/administrators, /organization/management/executive-board) are mapped to the LDAP tree. The location of groups in the LDAP tree are configurable through the parameter ctxDNs in the PicketLink IDM configuration file. See Section 27.3, “Examples” for more information about configuration parameters.

Procedure 27.7. For RHDS and OpenDS

  1. Expose the entry under "Sample LDAP config":
    <!--Sample LDAP config-->
    <value>war:/conf/organization/picketlink-idm/examples/picketlink-idm-LDAP-config.xml</value>
    

Procedure 27.8. For MSAD

  1. Expose the entry under "MSAD LDAP Example":
    <!--MSAD LDAP Example-->
    <value>war:/conf/organization/picketlink-idm/examples/picketlink-idm-msad-config.xml</value>
    
  2. To enable SSL encryption, perform the following sub-steps:
    1. Open the ID_HOME/picketlink-idm/examples/picketlink-idm-msad-config.xml.
    2. Ensure the following entries are uncommented and that the path to the truststore file and password are correct:
      <option>
        <name>customSystemProperties</name>
        <value>javax.net.ssl.trustStore=/path/to/truststore</value>
        <value>javax.net.ssl.trustStorePassword=password</value>
      </option>
      
      You can import a custom certificate by replacing the certificate and truststore details in the following command:
      keytool -import -file  certificate  -keystore  truststore 

Procedure 27.9. For OpenLDAP

  1. Expose the entry under "OpenLDAP LDAP config":
    <!--OpenLDAP LDAP config-->
    <value>war:/conf/organization/picketlink-idm/examples/picketlink-idm-openLDAP-config.xml</value>
    

27.3. Examples

Example 27.1. LDAP Configuration

The following settings are stored in the PicketLink configuration file that is nominated in the idm-configuration.xml file of your deployment (under the config parameter of the PicketLinkIDMService component):
This file could be:
  • The default picketlink-idm-config.xml.
  • One of the three example configuration files discussed in Procedure 27.2, “Set up LDAP Read-only Mode”:
    picketlink-idm-LDAP-acme-config.xml
    picketlink-idm-msad-readonly-config.xml
    picketlink-idm-openLDAP-acme-config.xml
  • A custom file created by modifying one of the above files.

Configuration Options

ctxDNs
This is the DN that will be used as context for IdentityObject searches. More than one value can be specified.
Some examples are:
  • ou=People,o=acme,dc=example,dc=com
  • ou=Roles,o=acme,dc=example,dc=com
  • ou=OrganizationUnits,o=acme,dc=example,dc=com
  • MSAD: CN=Users,DC=test,DC=domain (in two places).
providerURL
The LDAP server connection URL. Formatted as "ldap://<HOST>:<PORT>". The default setting is: ldap://localhost:1389.
MSAD: Should use SSL connection (ldaps://<HOST>:636) for password update or creation to work.
adminDN
The LDAP entry used to connect to the server.
Some possible values are:
  • RHDS or OpenDS: cn=Directory Manager
  • OpenLDAP: cn=Manager,dc=my-domain,dc=com
  • MSAD: TEST\Administrator
adminPassword
The password associated with the adminDN.
customSystemProperties
This option defines the values needed to use SSL encryption with LDAP.

Example 27.2. Read-only groupTypeMappings

The groupTypeMappings exposed in the idm-configuration.xml file correspond to identity-object-type values defined in the DS-specific configuration file (referenced in Sub-step 3a of the DS-specific procedure above).
For RHDS, OpenDS and OpenLDAP the picketlink-idm-LDAP-acme-config.xml and picketlink-idm-openLDAP-acme-config.xml files contain the following values:
<repository>
   <id>PortalRepository</id>
   <class>org.picketlink.idm.impl.repository.FallbackIdentityStoreRepository</class>
   <external-config/>
   <default-identity-store-id>HibernateStore</default-identity-store-id>
   <default-attribute-store-id>HibernateStore</default-attribute-store-id>
   <identity-store-mappings>
     <identity-store-mapping>
       <identity-store-id>PortalLDAPStore</identity-store-id>
       <!-- Comment #1 -->
       <identity-object-types>
         <identity-object-type>USER</identity-object-type>
         <identity-object-type>acme_roles_type</identity-object-type>
         <identity-object-type>acme_ou_type</identity-object-type>
       </identity-object-types>
       <!-- Comment #2 -->
       <options>
         <option>
           <name>readOnly</name>
           <value>true</value>
         </option>
       </options>
     </identity-store-mapping>
   </identity-store-mappings>
   <options>
     <option>
       <name>allowNotDefinedAttributes</name>
       <value>true</value>
     </option>
   </options>
 </repository>
Comment #1: The PicketLink IDM configuration file dictates that users and those two group types be stored in LDAP.
Comment #2: An additional option defines that nothing else (except password updates) should be written there.
All groups under /acme/roles will be stored in PicketLink IDM with the acme_roles_type group type name and groups under /acme/organization_units will be stored in PicketLink IDM with acme_ou_type group type name.
For MSAD, the identity-object-types values in picketlink-idm-msad-readonly-config.xml change to:
 <identity-store-id>PortalLDAPStore</identity-store-id>
 <identity-object-types>
   <identity-object-type>USER</identity-object-type>
   <identity-object-type>msad_roles_type</identity-object-type>
 </identity-object-types>
The difference is that this configuration maps only one group type and points to the same container in LDAP for both users and mapped groups.

Example 27.3. Default groupTypeMappings

The groupTypeMappings exposed in the idm-configuration.xml file correspond to identity-object-type values defined in the DS-specific configuration file (referenced in Sub-step 3a of the DS-specific procedure above).
All of the supported LDAP configurations use the following values when implemented as the default identity store:
<repository>
   <id>PortalRepository</id>
   <class>org.picketlink.idm.impl.repository.FallbackIdentityStoreRepository</class>
   <external-config/>
   <default-identity-store-id>HibernateStore</default-identity-store-id>
   <default-attribute-store-id>HibernateStore</default-attribute-store-id>
   <identity-store-mappings>
     <identity-store-mapping>
       <identity-store-id>PortalLDAPStore</identity-store-id>
       <!-- Comment #1 -->
       <identity-object-types>
         <identity-object-type>USER</identity-object-type>
         <identity-object-type>platform_type</identity-object-type>
         <identity-object-type>organization_type</identity-object-type>
       </identity-object-types>
       <options/>
     </identity-store-mapping>
   </identity-store-mappings>
   <options>
     <option>
       <name>allowNotDefinedAttributes</name>
       <value>true</value>
     </option>
   </options>
 </repository>
 <repository>
   <id>DefaultPortalRepository</id>
   <class>org.picketlink.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>
Comment #1: The groupTypeMappings define that all groups under /platform should be stored in PicketLink IDM with the platform_type group type name and groups under /organization should be stored in PicketLink IDM with organization_type group type name.
The PicketLink IDM configuration file repository maps users and those two group types as stored in LDAP.

Chapter 28. Security Assertion Markup Language (SAML2)

28.1. What is SAML2

SAML (Security Assertion Markup Language) is an Oasis standard for exchanging authentication and authorization data between security domains. SAML 2.0 is an XML-based protocol that uses security tokens containing assertions to pass information about a principal (usually an end user) between an identity provider and a web service. SAML 2.0 enables web-based authentication and authorization scenarios including single sign-on (SSO).
General information relating to SAML2 is located in PDF form at http://docs.oasis-open.org/security/saml/v2.0/.
In Red Hat JBoss Portal, SAML2 support is provided by the PicketLink Federation. Read the documentation hosted at https://docs.jboss.org/author/display/PLINK/SAML+v2.0 for an excellent overview of what configuration is required for SAML2.

28.2. What is an Assertion

In SAML2, an assertion is a packet of security information. Each assertion contains statements used by service providers to make access-control decisions. The assertion statements provided by SAML2 include: authentication; attribute; and authorization decision statements.

28.3. What is an Identity Provider (IDP)

An Identity Provider is defined by OASIS as a kind of provider that creates, maintains, and manages identity information for principals and provides principal authentication to other service providers within a federation, such as with web browser profiles.
A Federation in this definition means an association comprising any number of service providers and identity providers.

28.4. What is a Service Provider (SP)

A Service Provider is defined by OASIS as a role donned by a system entity, where the system entity provides services to principals or other system entities.

28.5. SAML2 Authentication Overview

The following workflow phases apply when Red Hat JBoss Portal is used as a SAML2 Service Provider (SP).
SAML2 Authentication Workflow

Figure 28.1. SAML2 Authentication Workflow


When a user attempts to authenticate on a SAML2-enabled system, the request is handled by an authenticator that sits atop the Service Provider (SP).
If a user attempts to access a protected resource, for example http://localhost:8080/portal/dologin, the authenticator verifies whether the user has a SAML assertion known to the SP. If the user is authenticated, the authenticator redirects the user to the requested resource.
If the user is not yet authenticated, the following work flow is initiated.
Phase 1
User requests access to a protected resource.
Phase 2
The authenticator verifies whether the user has a SAML assertion known to the SP.
Phase 3
Red Hat JBoss Portal (SP) determines that it does not have a valid SAML assertion for the user.
The SAML request is encapsulated into a HttpRequest and is redirected to the Identity Provider (IDP) using the SAML Redirect Binding (sent as a Base64, URL-encoded, GET request parameter).
Phase 4
The IDP returns a login screen to the user, prompting for valid credentials.
Phase 5
The IDP receives the user credentials through a JAAS login module (SAML2ldpLoginModule). The login module sends a callback request through the REST API, back to Red Hat JBoss Portal to check the user exists in the identity store.
Phase 6
After the user is verified to exist in the identity store by the login module, the IDP authenticator issues a SAML assertion ticket that contains all roles for the user.
Phase 7
The assertion ticket is encapsulated in a HttpRequest, and is redirected back to the Service Provider (SP).
Phase 8
The SP decodes the HttpRequest. The SP login module (SAML2IntegrationLoginModule) parses the authenticated username, and determines whether the assertion is valid according to the known roles the SP has about the user.
The login module then creates an identity object for the user, and registers the user in the IdentityRegistry.
Phase 9
The user is now successfully authenticated, and is redirected back to the secure resource. The secure resource will then redirect the user to Red Hat JBoss Portal as an authenticated user.
If the user needs to authenticate against a different SP application within the same browser session, credentials are not required to re-authenticate because the user is already known to the IDP.
An example of this would include another instance of Red Hat JBoss Portal on a different host, or a completely different web application within the same IDP federation.

28.6. The platform as SAML2 SP and SAML2 IDP

Configuring Red Hat JBoss Portal as a Service Provider (SP) and an Identity Provider (IDP)

This basic scenario describes how to configure the necessary configuration.properties file on each virtual host portal instance. The scenario uses a bundled test keystore, which is not suitable for production environments.
For production environments, follow the instructions in Section 28.8, “Implementing Keystores” to provision a production-ready keystore.

Pre-requisites

  • Two available virtual hosts running on the local machine.
  • One instance of Red Hat JBoss Portal deployed to a directory referred to as referred to as JPP_SP_DIST.
  • A separate instance of Red Hat JBoss Portal deployed to another directory referred to as JPP_IDP_DIST.

  1. Configure Red Hat JBoss Portal as a SAML2 SP

    Open JPP_SP_DIST/standalone/configuration/gatein/configuration.properties and add the following configuration parameters.
    # SSO
    gatein.sso.enabled=true
    gatein.sso.callback.enabled=${gatein.sso.enabled}
    gatein.sso.login.module.enabled=${gatein.sso.enabled}
    gatein.sso.login.module.class=org.gatein.sso.agent.login.SAML2IntegrationLoginModule
    # Comment #1
    gatein.sso.filter.login.sso.url=/@@portal.container.name@@/dologin
    # Comment #2
    gatein.sso.filter.logout.class=org.gatein.sso.agent.filter.SAML2LogoutFilter
    gatein.sso.filter.initiatelogin.enabled=false
    gatein.sso.valve.enabled=true
    # Comment #3
    gatein.sso.valve.class=org.picketlink.identity.federation.bindings.tomcat.sp.ServiceProviderAuthenticator
    # Comment #4
    gatein.sso.saml.config.file=/WEB-INF/conf/sso/saml/picketlink-sp.xml
    # Comment #5
    gatein.sso.idp.host=www.idp.com
    # Comment #6 
    gatein.sso.idp.url=http://${gatein.sso.idp.host}:8080/portal/dologin
    # Comment #7
    gatein.sso.sp.url=http://www.sp.com:8080/portal/dologin
    # Comment #8 
    # WARNING: This bundled keystore is only for testing purposes. Generate and use your own keystore in production!
    gatein.sso.picketlink.keystore=/sso/saml/jbid_test_keystore.jks
    

    Comment #1
    For Red Hat JBoss Portal integration as SAML2 SP, we need to use login module class org.gatein.sso.agent.login.SAML2IntegrationLoginModule, which will act as a JAAS delegate from SSODelegateLoginModule
    Comment #2
    The specific filter class org.gatein.sso.agent.filter.SAML2LogoutFilter is needed to enable support for SAML2 single logout.
    SAML2 single logout will take place and will log you out from both Red Hat JBoss Portal IDP and SP servers.
    1. A user clicks Sign out.
    2. SAML2LogoutFilter will redirect them to the SAML2 IDP application where they will be logged out.
    3. The IDP application will also manage the logout from all other SP applications.
    4. The user will be redirected to Red Hat JBoss Portal and logged out.
    Refer to Section 28.7, “Disable SAML2 Single logout” should you not want to use SAML2 Single logout.
    Comment #3
    There is a special valve; org.picketlink.identity.federation.bindings.tomcat.sp.ServiceProviderAuthenticator provided by the Picketlink federation library.
    This valve manages the creation and parsing of SAMLRequest and SAMLResponse messages and so is vital for a successful SAML2 integration.
    Comment #4
    This element points to the location of the SAML2 SP configuration file.
    The path is relative to to the portal WAR application. The abolute path to the file is JPP_SP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/sso/saml/picketlink-sp.xml.
    For this simple scenario, there is no need to edit this file. However, in a production environment, you will likely need to generate and use your own keystore and thus you must then configure the use of those keys in this file.
    Refer to Section 28.8, “Implementing Keystores” for more information.
    Comment #5
    This points to the host serving the SAML2 IDP.
    Comment #6
    This element points to the URL of the IDP application. This guide assumes that you will use another instance of Red Hat JBoss Portal as SAML2 IDP, so request path will be /portal/dologin in this case.
    Comment #7
    This points to URL of this GateIn Portal, which will be used as SAML2 SP.
    Comment #8
    This points to the location of the keystore file. It is relative to the classpath of the portal WAR application. The absolute location is JPP_SP_HOME/gatein/gatein.ear/portal.war/WEB-INF/classes/sso/saml/jbid_test_keystore.jks.
  2. Configure Red Hat JBoss Portal as a SAML2 IDP

    Open JPP_IDP_DIST/standalone/configuration/gatein/configuration.properties and add the following configuration parameters.
    # SSO
    # Comment #1
    gatein.sso.enabled=false
    gatein.sso.valve.enabled=true
    # Comment #2
    gatein.sso.valve.class=org.gatein.sso.saml.plugin.valve.PortalIDPWebBrowserSSOValve
    # Comment #3
    gatein.sso.saml.config.file=/WEB-INF/conf/sso/saml/picketlink-idp.xml
    # Comment #4
    gatein.sso.idp.url=http://www.idp.com:8080/portal/dologin
    # Comment #5 
    gatein.sso.idp.listener.enabled=true
    # Comment #6
    gatein.sso.sp.domains=sp.com
    # Comment #7
    gatein.sso.sp.host=www.sp.com
    # Comment #8
    # WARNING: This bundled keystore is only for testing purposes. Generate and use your own keystore in production!
    gatein.sso.picketlink.keystore=/sso/saml/jbid_test_keystore.jks
    

    Comment #1
    In this IDP configuration, the gatein.sso.enabled parameter has been disabled.
    This is because the Red Hat JBoss Portal IDP does not use any external SSO provider, but will now act as an SSO provider itself (SAML2 Identity Provider) for the Red Hat JBoss Portal SP.
    Comment #2
    For IDP we need to use org.gatein.sso.saml.plugin.valve.PortalIDPWebBrowserSSOValve valve, which is able to handle SAML request/response messages from SP applications and react on them.
    Comment #3
    The location of configuration file is relative to the portal WAR. The absolute path is JPP_IDP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/sso/saml/picketlink-idp.xml.
    For this simple scenario, there is no need to edit this file. However, in a production environment, you will likely need to generate and use your own keystore and thus you must then configure the use of those keys in this file.
    Refer to Section 28.8, “Implementing Keystores” for more information. You will also need to manually add validating aliases in the keystore section if you have multiple SP applications on different hosts.
    Comment #4
    The URL of the Red Hat JBoss Portal which will act as the SAML2 IDP.
    Comment #5
    This will enable a special session listener to clean up records about SAML tickets of expired hosts.
    Comment #6
    Comma-separated list of all SP domains to be trusted by this IDP.
    Comment #7
    Host for Red Hat JBoss Portal SP. If you want more SP applications, you will need to manually edit the picketlink-idp.xml file and add ValidatingAlias element for each of them.
    Comment #8
    The keystore in JPP_IDP_HOME/gatein/gatein.ear/portal.war/WEB-INF/classes/sso/saml/jbid_test_keystore.jks.
  3. Navigate to JPP_SP_DIST/bin, and start the SP instance by running:
    ./standalone.sh -b www.sp.com
  4. Navigate to JPP_IDP_DIST/bin, and start the IDP instance by running:
    ./standalone.sh -b www.idp.com
  5. Test the configuration

    Navigate to http://www.sp.com:8080/portal/classic and click to Sign in.
    From here, SAML request will be sent to www.idp.com and you will be redirected to login screen of the Red Hat JBoss Portal IDP instance on http://www.idp.com:8080/portal/dologin.
    After a successful login, the SAML response will be sent back to www.sp.com and you will be redirected to the Red Hat JBoss Portal on www.sp.com as a logged-in user.
    Now you can go to www.idp.com:8080/portal and you will see that you are logged-in as the SSO service has already logged you into this host.
    Return to www.sp.com:8080/portal and click to Sign out. SAML2 single log out will take place and will logged you from both Red Hat JBoss Portal on the IDP and SP servers.
    You can check that you are logged out from both www.sp.com:8080/portal and www.idp.com:8080/portal

28.7. Disable SAML2 Single logout

SAML2 Single logout is very powerful feature of SAML2 protocol, because triggering logout from any SP application will enforce "global" logout also from IDP and all other SP applications. It makes sense that you may not want this feature to be enabled, so logging out from Red Hat JBoss Portal (click to Sign out link) will only logout the user from the Red Hat JBoss Portal on www.sp.com, but user will still be logged in JPP IDP on www.idp.com and in all other SP applications.
Disabling this feature is as easy as switching an option in file GATEIN_SP_HOME/standalone/configuration/gatein/configuration.properties :
gatein.sso.filter.logout.enabled=false
Now when you click Sign out in www.sp.com, you will still be logged in Red Hat JBoss Portal on www.idp.com . There won't be any SAML communication between SP and IDP during logout from www.sp.com.

28.8. Implementing Keystores

For secure and trusted communication, you will need your own keystores with your own keys. The default keystore is useful only for testing purposes, and should not be used in production. Separate keys for the portal SP, and for the IDP are created in this scenario.

Configuring a Keystore for Secure Communication Between SP and IDP

Follow the instructions to create a keystore for secured and trusted communication between SAML2 components.
  1. Go to JPP_SP_HOME/gatein/gatein.ear/portal.war/WEB-INF/classes/sso/saml/ directory and run the command:
    keytool -genkey -alias secure-key -keyalg RSA -keystore secure-keystore.jks
    
    You need to choose keystore password and private key password. Other values do not matter. This guide assumes that your keystore password is keystorepass and a private key password is keypass.
  2. For simplification purposes, this guide will use the same keystore for both the SP and IDP servers.
    Copy the keystore file generated in the last step to the IDP server directory; JPP_IDP_HOME/gatein/gatein.ear/portal.war/WEB-INF/classes/sso/saml/.
  3. Configure the new keystore in file JPP_SP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/sso/saml/picketlink-sp.xml and replace existing KeyProvider definition with:
    <KeyProvider ClassName="org.picketlink.identity.federation.core.impl.KeyStoreKeyManager">
      <Auth Key="KeyStoreURL" Value="/sso/saml/secure-keystore.jks"/>
      <Auth Key="KeyStorePass" Value="keystorepass"/>
      <Auth Key="SigningKeyPass" Value="keypass"/>
      <Auth Key="SigningKeyAlias" Value="secure-key"/>
      <ValidatingAlias Key="${gatein.sso.idp.host}" Value="secure-key"/>
    </KeyProvider>
    
  4. Configure the keystore in JPP_IDP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/sso/saml/picketlink-idp.xml similarly:
    <KeyProvider ClassName="org.picketlink.identity.federation.core.impl.KeyStoreKeyManager">
      <Auth Key="KeyStoreURL" Value="/sso/saml/secure-keystore.jks"/>
      <Auth Key="KeyStorePass" Value="keystorepass"/>
      <Auth Key="SigningKeyPass" Value="keypass"/>
      <Auth Key="SigningKeyAlias" Value="secure-key"/>
      <ValidatingAlias Key="${gatein.sso.sp.host}" Value="secure-key"/>
    </KeyProvider>
    
  5. Restart both the SP and IDP servers.
    They will now use your new keystore instead of the default jbid_test_keystore.jks.
    While an argument could be made to use certificates signed by certification authority, self-signed certificates are fine for this purpose.

28.9. Setup with Picketlink IDP using REST callback

Setup with Picketlink IDP using REST callback

This basic scenario describes how to configure the necessary configuration.properties file.
In the following procedure Red Hat JBoss Portal is configured as the SP and the PicketLink IDP application as the IDP.
Additionally, the PicketLink IDP application will be configured to use REST callback to the Red Hat JBoss Portal instance to authenticate users (it will use Red Hat JBoss Portal as an identity store).

Important

The scenario uses a bundled test keystore, which is not suitable for production environments. For production environments, follow the instructions in Section 28.8, “Implementing Keystores” to provision a production-ready keystore.

Pre-requisites

  • One instance of Red Hat JBoss Portal deployed to a directory called JPP_SP_DIST.
  • Another instance of Red Hat JBoss Portal deployed to JPP_IDP_DIST. This server will run on www.idp.com and will host the PicketLink IDP application. Alternatively, if you have access to EAP 6, you can use it instead of Red Hat JBoss Portal, however you will need to copy the PicketLink libraries required by the Picketlink IDP application from the Red Hat JBoss Portal SP instance. The libraries are located in JPP_SP_DIST/modules/org/picketlink/gatein

  1. Configure Red Hat JBoss Portal as a SP compatible with PicketLink IDP

    Open JPP_SP_DIST/standalone/configuration/gatein/configuration.properties and add the following configuration parameters.
    # SSO
    gatein.sso.enabled=true
    gatein.sso.callback.enabled=${gatein.sso.enabled}
    gatein.sso.login.module.enabled=${gatein.sso.enabled}
    gatein.sso.login.module.class=org.gatein.sso.agent.login.SAML2IntegrationLoginModule
    gatein.sso.filter.login.sso.url=/@@portal.container.name@@/dologin
    gatein.sso.filter.logout.class=org.gatein.sso.agent.filter.SAML2LogoutFilter
    gatein.sso.filter.initiatelogin.enabled=false
    gatein.sso.valve.enabled=true
    gatein.sso.valve.class=org.picketlink.identity.federation.bindings.tomcat.sp.ServiceProviderAuthenticator
    gatein.sso.saml.config.file=/WEB-INF/conf/sso/saml/picketlink-sp.xml
    gatein.sso.idp.host=www.idp.com
    gatein.sso.idp.url=http://${gatein.sso.idp.host}:8080/idp-sig/
    gatein.sso.sp.url=http://www.sp.com:8080/portal/dologin
    # WARNING: This bundled keystore is only for testing purposes. Generate and use your own keystore in production!
    gatein.sso.picketlink.keystore=/sso/saml/jbid_test_keystore.jks
  2. Navigate to JPP_SP_DIST/bin, and start the SP instance by running:
    ./standalone.sh -b www.sp.com
  3. Configure Picketlink Identity Provider

    1. Copy JPP_DIST/gatein-sso/saml/idp-sig.war to IDP_DIST/standalone/deployments/.
    2. Create IDP_DIST/standalone/deployments/idp-sig.war.dodeploy to force the application server to deploy the idp-sig.war. This file is required because the idp-sig.war is in an exploded format, not an archive format.
    3. Create a new security domain in IDP_DIST/standalone/configuration/standalone.xml that contains the following configuration.
      <security-domain name="idp" cache-type="default">
         <authentication>
            <login-module code="org.gatein.sso.saml.plugin.SAML2IdpLoginModule" flag="required">
               <module-option name="rolesProcessing" value="STATIC"/>
               <module-option name="staticRolesList" value="manager,employee,sales"/>
               <module-option name="gateInURL" value="http://www.sp.com:8080/portal"/>
            </login-module>
         </authentication>
      </security-domain>
  4. Start the IDP instance

    1. Navigate to IDP_DIST/bin and execute the following command:
      ./standalone.sh -b www.idp.com -Dsp.host=www.sp.com -Dsp.domains=sp.com -Dpicketlink.keystore=/jbid_test_keystore.jks
    2. Navigate to http://www.sp.com:8080/portal and click Sign in.
      You will be redirected to the idp-sig application on http://www.idp.com:8080/idp-sig/.
      You are able to log in with your Red Hat JBoss Portal credentials because the REST callback will be sent to the portal instance on www.sp.com and it will manage user authentication.
      After authentication you will be redirected to the portal on www.sp.com and logged-in.

28.10. Integration with Salesforce and Google Apps

JBoss Portal Platform SSO component contains the support for Salesforce and Google Apps integration for SAML2 based SSO.
Three scenarios are listed here with links to sections detailing the configuration changes required to implement the them.
Scenario One
Using JBoss Portal Platform as the SAML Identity Provider (IDP) and Salesforce as the SAML Service Provider (SP).
Scenario Two
Using JBoss Portal Platform as the Identity Provider and Google Apps as the Service Provider.
Scenario Three
Using Salesforce as the Identity Provider and JBoss Portal Platform as the Service Provider.
A video which shows the integration between Salesforce and Google Apps is available at http://vimeo.com/45895919. This is based on a community release of the GateIn Portal, so the configuration described in this video should be used as reference only.

28.10.1. Scenario One

Salesforce Configuration

This configuration uses JBoss Portal Platform as the IDP and Salesforce as the SP.

Prerequisites:


Procedure 28.1. 

  1. In Salesforce's SSO configuration, set the Issuer, Identity Provider Login URL and Identity Provider Logout URL to be http://www.idp.com:8080/portal/dologin.
    This assumes that you are using virtual host www.idp.com as described in Section 28.6, “The platform as SAML2 SP and SAML2 IDP”.
    Salesforce SSO Configuration Dialog

    Figure 28.2. Salesforce SSO Configuration Dialog


  2. Import the correct certificate into Salesforce. It needs to be a certificate which is used for signing of SAML messages from JBoss Portal Platform. You can export this certificate from directory JPP_IDP_HOME/gatein/gatein.ear/portal.war/WEB-INF/classes/sso/saml with command:
    keytool -export -file portal-idp.crt -keystore secure-keystore.jks -alias secure-key
    
    This certificate then needs to be imported into Salesforce as described in https://docs.jboss.org/author/display/PLINK/Picketlink+as+IDP,+Salesforce+as+SP. This assumes that your certificate is in file secure-keystore.jks with alias name secure-key (as described in Section 28.8, “Implementing Keystores”). If this is not the case, modify the command using to the name of your keystore and signing key aliases.
  3. Create few users in your Salesforce domain, which are also available in JBoss Portal Platform 6. You can do it otherwise, and create users in JBoss Portal Platform, which are present in Salesforce. The important point is about mapping, wherein the username in portal is mapped to the Federation ID in Salesforce, such as JBoss Portal Platform user mary is mapped to Salesforce user in your domain, which has Federation ID mary.

JBoss Portal Configuration

Make the requisite changes in JBoss Portal Platform to allow it to act as an IDP for Salesforce.

Prerequisites:


Procedure 28.2. 

  1. Import Salesforce client certificate into your JBoss Portal Platform keystore with the command:
    keytool -import -keystore secure-keystore.jks -file /tmp/proxy-salesforce-com.123 -alias salesforce-cert
    
  2. Configure ValidatingAlias and Metadata similarly like for base https://docs.jboss.org/author/display/PLINK/Picketlink+as+IDP,+Salesforce+as+SP. Note that main PicketLink configuration file where you need to do changes is JPP_IDP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/sso/saml/picketlink-idp.xml and assumption is that you will add your metadata into JPP_IDP_HOME/gatein/gatein.ear/portal.war/WEB-INF/sp-metadata.xml.
  3. Trusted domain are configured through configuration.properties. So property gatein.sso.sp.domains in file JPP_IDP_HOME/standalone/configuration/gatein/configuration.properties should look like this:
    gatein.sso.sp.domains=sp.com,idp.com,salesforce.com
    

Test the Configuration

Test your configuration for using JBoss Portal Platform as the SAML Identity Provider and Salesforce as the SAML Service Provider.

Procedure 28.3. 

  • After configuring both IDP and SP, you can test that after accessing the URL of your Salesforce domain, for example https://yourdomain.my.salesforce.com, a SAML Request will be sent to JBoss Portal Platform and you will be redirected to the JBoss Portal Platform login screen at http://www.idp.com:8080/portal/dologin. You can login with some JBoss Portal Platform default user credentials (like mary) and you should be redirected back to Salesforce and automatically logged here as mary thanks to SAML SSO.

28.10.2. Scenario Two

Google Apps Configuration

This scenario uses JBoss Portal Product as the IDP and Google Apps as the SP.

Prerequisites:


Procedure 28.4. 

  1. Create some users in Google Apps, which are available in JBoss Portal Platform as well (or vice-versa). Username from portal is mapped to nick in Google Apps. For example JBoss Portal Platform user mary is standardly mapped to Google Apps user , which normally has nick mary.
  2. In SSO configuration of Google Apps domain, you need to change all Sign-In and Sign-Out URL to value http://www.idp.com:8080/portal/dologin instead of http://localhost:8080/idp-sig, which is used in PicketLink guide (assuming that you are using virtual host www.idp.com as described in Section 28.6, “The platform as SAML2 SP and SAML2 IDP”).
  3. You again need to export certificate from your JBoss Portal Platform keystore into some file (for example portal-idp.crt as described in Section 28.10.1, “Scenario One”) and then import it from this file into Google Apps through Google Apps UI.

Portal Configuration

Configure JPP to act as IDP.

Prerequisites:


Procedure 28.5. 

  1. Configure metadata in similar way like in https://docs.jboss.org/author/display/PLINK/Picketlink+as+IDP,+Google+Apps+as+SP. Assumption is that metadata are in the JPP_IDP_HOME/gatein/gatein.ear/portal.war/WEB-INF/sp-metadata.xml file.
  2. Trusted domains are configured through configuration.properties. You need to add google.com domain like described in PicketLink tutorial. So property gatein.sso.sp.domains in file JPP_IDP_HOME/standalone/configuration/gatein/configuration.properties should look like this:
    gatein.sso.sp.domains=sp.com,idp.com,salesforce.com,google.com
    

Test the Configuration


Procedure 28.6. 

  • Test the integration - when you access your Google Apps domain, SAML Request will be sent to JBoss Portal Platform on your host like http://www.idp.com:8080/portal/dologin and after login with portal username, you will be again redirected to Google Apps with SAML Response and you should be automatically logged to Google Apps thanks to SAML SSO.

28.10.3. Scenario Three

Salesforce Configuration

The scenario uses JBoss Portal Platform as the SP and Salesforce as the IDP.

Prerequisites:


Procedure 28.7. 

  1. Like in generic PicketLink guide, you need to disable SSO in Salesforce, because now we don't want Salesforce to act as SP but as IDP.
  2. Create Service Provider - It is also necessary to add JBoss Portal Platform Service Provider in configuration of Identity Provider in Salesforce. In https://docs.jboss.org/author/display/PLINK/Picketlink+as+SP+Salesforce+as+IDP it is described how to do it. Configuration for JBoss Portal Platform SP can look similarly to shown here: (Assuming that portal SP will be executed on virtual host www.sp.com):
    JBoss Portal Platform SP Configuration

    Figure 28.3. JBoss Portal Platform SP Configuration


  3. As certificate, you may need to export certificate from your JBoss Portal Platform keystore file in JPP_SP_HOME/gatein/gatein.ear/portal.war/WEB-INF/classes/saml/sso/secure-keystore.jks (you can simply use certificate portal-idp.crt if you went through previous sections of this tutorial and you're using same certificate for SP like for IDP. See section Section 28.8, “Implementing Keystores” for more details).

Portal Configuration

Prerequisites:


Procedure 28.8. 

  1. Certificate - Similarly like in https://docs.jboss.org/author/display/PLINK/Picketlink+as+SP+Salesforce+as+IDP, we need to import certificate created by Salesforce into JBoss Portal Platform keystore in file JPP_SP_HOME/gatein/gatein.ear/portal.war/WEB-INF/classes/saml/sso/secure-keystore.jks. You can use command like this (assuming certificate downloaded from Salesforce is in /tmp/salesforce_idp_cert.cer):
    keytool -import -file /tmp/salesforce_idp_cert.cer -keystore secure-keystore.jks -alias salesforce-idp
    
  2. URL configuration - In JPP_SP_HOME/standalone/configuration/gatein/configuration.properties you need to change URLs to values corresponding to Salesforce:
    gatein.sso.idp.url=https://yourdomain.my.salesforce.com/idp/endpoint/HttpPost
    gatein.sso.sp.url=http://www.sp.com:8080/portal/dologin
    
  3. ValidatingAlias needs to be added to file In JPP_SP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/sso/saml/picketlink-sp.xml (assuming that you imported salesforce certificate into your keystore under alias salesforce-idp ):
    <ValidatingAlias Key="yourdomain.my.salesforce.com" Value="salesforce-idp" />
    
  4. For roles-mapping, you don't need to configure nothing on JBoss Portal Platform because JBoss Portal Platform SP is configured by default to obtain roles from Picketlink IDM database and not from IDP SAML Response. So no changes needed.

Test the Integration


Procedure 28.9. 

  • After startup of JBoss Portal Platform on your host (assuming virtual host www.sp.com ), you can go to http://www.sp.com:8080/portal and click Sign in link. Then JBoss Portal Platform will send SAML Request to Salesforce and you will be redirected to Salesforce login screen. After login into Salesforce, you will be redirected to your JBoss Portal Platform and logged as the user which you logged into Salesforce. Again, username from JBoss Portal Platform is mapped to Federation ID in Salesforce, so JBoss Portal Platform user john is mapped to Salesforce user with Federation ID john.

28.10.4. Tips and Tricks

  • Videos - You can follow some videos on JBoss Portal Platform vimeo channel, where you can see SAML2 integration in action. The videos are:
    1. http://vimeo.com/45841256 - Video with basic use cases showing JBoss Portal Platform as SAML2 SP and as SAML2 IDP.
    2. http://vimeo.com/45895919 - Video showing integration with Salesforce and Google Apps
  • SAML tracer is a useful plug-in to Firefox browser, which allows you to monitor HTTP requests and see wrapped SAML messages in XML format. It could be useful especially for troubleshooting. You can download plug-in here
  • Logging - Like for other SSO solutions, it may be useful to enable trace logging, for example for org.gatein.sso. In case of SAML, it is also good to enable logging for org.picketlink.identity.federation, which is the base package of the PicketLink Federation library. You can add these categories to GATEIN_HOME/standalone/configuration/standalone.xml :
    <logger category="org.gatein.sso">
      <level name="TRACE"/>
    </logger>
    <logger category="org.picketlink.federation">
      <level name="TRACE"/>
    </logger>
    <logger category="org.jboss.security">
      <level name="TRACE"/>
    </logger>
    <logger category="org.picketbox">
      <level name="TRACE"/>
    </logger>
    <logger category="org.exoplatform.services.security">
      <level name="TRACE"/>
    </logger>
    

Part IV. Web Services for Remote Portlets (WSRP)

Chapter 29. Web Services for Remote Portlets (WSRP)

29.1. Introduction

The Web Services for Remote Portlets specification defines a web service interface for accessing and interacting with interactive presentation-oriented web services. It has been produced through the efforts of the Web Services for Remote Portlets (WSRP) OASIS Technical Committee. It is based on the requirements gathered and on the concrete proposals made to the committee.
Scenarios that motivate WSRP functionality include:
  • Content hosts, such as portal servers, providing Portlets as presentation-oriented web services that can be used by aggregation engines.
  • Aggregating frameworks, including portal servers, consuming presentation-oriented web services offered by content providers and integrating them into the framework.
More information on WSRP can be found on the official website for WSRP. Further suggested reading is the primer for a good, albeit technical, overview of WSRP.

29.2. Level of support in JBoss Portal Platform

The WSRP Technical Committee defined WSRP Use Profiles to help with WSRP interoperability. This section will refer to terms defined in that document.
JBoss Portal Platform provides a Simple level of support for the WSRP Producer except that out-of-band registration is not currently handled. In-band registration and persistent local state (which are defined at the Complex level) are supported.
On the Consumer side, JBoss Portal Platform provides a Medium level of support for WSRP, except that the consumer only handles HTML markup (because JBoss Portal Platform itself does not handle other markup types). The platform does support explicit portlet cloning and it fully supports the PortletManagement interface.
As far as caching goes, the component has Level 1 Producer and Consumer. Cookie handling is supported properly on the Consumer and the Producer requires initialization of cookies (it has been found that it improves interoperability with some consumers). The component does not support custom window states or modes, as JBoss Portal Platform does not. The component does, however, support CSS on both the Producer (though it is more a function of the portlets than inherent Producer capability) and Consumer.
While a complete implementation of WSRP 1.0 is provided, the community developers 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).
JBoss Portal Platform supports WSRP 2.0 with a complete implementation of the non-optional features. The only features not implemented are support for lifetimes and leasing support.

Note

As of version 6.0.0 of JBoss Portal Platform, WSRP is only activated and supported using JBoss Portal Platform deployed on JBoss Enterprise Application Platform 6.

29.3.  Deploying JBoss Portal Platform's WSRP services

JBoss Portal Platform provides complete support for WSRP 1.0 and 2.0 standard interfaces, and offers both consumer and producer services. Starting with version 2.1.0-GA of the component, WSRP is packaged as a JBoss Portal Platform extension, and is self-contained in a package named JPP_HOME/gatein/extensions/gatein-wsrp-integration.ear .
The only files of interest from a user perspective are located in the JPP_HOME/standalone/configuration/gatein/wsrp directory.
  • gatein-wsse-consumer.xml, which allows you to configure WS-Security support for the consumer.
  • gatein-wsse-producer.xml which allows you to configure WS-Security support for the producer.
Refer to Section 29.4.3.1, “ WS-Security Configuration” section for configuration required in these files.
The extension itself is composed of the following components:
  • META-INF contains files necessary for EAR packaging.
  • The extension-component-6.0.0.jar, which contains the components needed to integrate the WSRP component into JBoss Portal Platform. It also includes the default configuration files for the WSRP producer and the default WSRP consumers.
  • The extension-config-6.0.0.jar, which contains the configuration file needed by the GateIn extension mechanism to properly register this EAR as an extension.
  • The extension-war-6.0.0.war, which contains the configuration files needed by the portal extension mechanism to properly setup the WSRP service. It includes wsrp-configuration.xml which, in particular, configures several options for the WSRPServiceIntegration component at the heart of the WSRP integration in JBoss Portal Platform.
  • The lib directory, which contains the different libraries needed by the WSRP service.
  • The wsrp-admin-gui-2.2.2.Final.war, which contains the WSRP Configuration portlet with which you can configure consumers to access remote servers and how the WSRP producer is configured.
  • The wsrp-producer-jb5wsss-2.2.2.Final.war, which contains the producer-side support for WS-Security.
If you are not going to use WSRP in JBoss Portal Platform, it will not adversely affect your installation to leave it as is. Otherwise, you can just remove the gatein-wsrp-integration.ear file from your AS deploy directory.

29.3.1. Considerations to use WSRP when running JBoss Portal Platform on a non-default port or hostname

The web service stack that JBoss Portal Platform uses is based on JBoss WS. It updates the port and host name used in WSDL. For more information, refer to the Web Services chapter in the JBoss Enterprise Application Platform 6 Administration and Configuration User Guide.
If you have modified the host name and port on which your server runs, you will need to update the configuration for the consumer used to consume JBoss Portal Platform's 'self' producer.

29.4.  Securing WSRP

There are two main ways to secure the communication between a producer and consumer:
  1. Securing the Transport Layer This requires using SSL and a HTTPS endpoint. By using this, the communication between the consumer and producer will be encrypted.
  2. Securing the Contents of the SOAP message This option requires using ws-security to handle parts of the SOAP message. With this option you can specify things like encryption, signing, timestamps, etc as well as passing across user credentials to perform a login on the producer side. WS-Security is more powerful and has more options, but is requires more complex configurations.
Depending on requirements, an HTTPs endpoint and/or ws-security can be used.

29.4.1. WSRP over SSL with HTTPS endpoints

It is possible to use WSRP over SSL for a secure exchange of data. Since JBoss Portal Platform does not come initially configured for HTTPS connectors, we will need to configure the producer's server for this first. This is a global configuration change, and will affect more than just the portal and WSRP. Refer to the JBoss Enterprise Application Platform 6 Administration and Configuration Guide for instructions about how to configure HTTPS connectors for the server.
Once the producer is configured for HTTPS connections, on the consumer you will just need to modify the URL for the WSRP endpoint to point to the new HTTPS based URL. This will require either manually updating the value in the WSRP administration application, or by specifying it using the wsrp-consumers-config.xml configuration file before the server is first started.

29.4.1.1. Sample Configuration For Enabling SSL With WSRP

Warning

The following procedures are provided as an example of configuring HTTPS/SSL with WSRP.
It is not meant to show best practices for configuring HTTPS with the platform, and does things which should not be used in a production server (such as self-signed certificates). Refer to the JBoss Enterprise Application Platform 6 product documentation for detailed, best practice configuration guidelines.

Procedure 29.1. Configure the Producer to Use HTTPS

Configure the producer's server to use HTTPS. This is handled in the same manner that you would configure any JBoss Enterprise Application server for HTTPS.
  1. Generate the keystore for the producer by executing the following command.
    keytool -genkey -alias tomcat -keyalg RSA -keystore producerhttps.keystore -dname "cn=localhost" -keypass changeme -storepass changeme
  2. Configure the server to add an HTTPS connection. This requires modifying the standalone/configuration/standalone.xml file with the following content in bold:
                    
                    <subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="false">
                    
                    <connector name="http" protocol="HTTP/1.1" scheme="http" socket-binding="http"/>
                    
                    <connector name="https" protocol="HTTP/1.1" scheme="https" socket-binding="https" secure="true">
                    
                    <ssl certificate-key-file="/path/to/producerhttps.keystore" password="changeme"/>
                    
                    </connector>
                    
                    <virtual-server name="default-host" enable-welcome-root="true">
                    
                    <alias name="localhost"/>
                    
                    <alias name="example.com"/>
                    
                    </virtual-server>
                    
                    ...
  3. Start the server and verify that https://localhost:8443/portal is accessible. Note that since you are using a self-signed certificate that your browser will give a warning that the certificate cannot be trusted.

    Note

    In this example case we are accessing the portal using 'localhost' hence why we are using "cn=localhost" in the keytool command. If you are using this across another domain, you will need to make the necessary changes.

Procedure 29.2. Configure the Consumer to Access the WSRP Endpoint over HTTPS

  1. Export the producer's public key from the producer's keystore
    keytool -export -alias tomcat -file producerkey.rsa -keystore producerhttps.keystore -storepass changeme
  2. Import the producer's public key into a new keystore for the consumer
    keytool -import -alias tomcat -file producerkey.rsa -keystore consumerhttps.keystore -storepass changeme -noprompt
  3. Configure the bin/standalone.conf file to add the following line at the end of the file:
    JAVA_OPTS="$JAVA_OPTS -Djavax.net.ssl.trustStore=/path/to/consumerhttps.keystore -Djavax.net.ssl.trustStorePassword=changeme"
  4. Start the consumer and change the selfv2 producer url to https://localhost:8443/wsrp-producer/v2/MarkupService?wsdl and verify that the consumer can access the producer.

Note

It is possible to modify the wsrp-consumers-config.xml configuration file to change the URL instead of modifying it in the administration GUI.
It is possible to use WSRP over SSL for secure exchange of data. Configure your server appropriately as described in the HTTPS Configuration section of the Installation Guide.

29.4.2. WSRP and WS-Security

Portlets may present different data or options depending on the currently authenticated user. For remote portlets, this means having to propagate the user credentials from the consumer back to the producer in a safe and secure manner. The WSRP specification does not directly specify how this should be accomplished, but delegates this work to the existing WS-Security standards. The WS-Security standards can also be used to secure the soap message, such as encryption and signing the message.

Encryption is strongly recommended

Encrypt the credentials being sent between the consumer and producer, otherwise they will be sent in plain text and could be easily intercepted. Configure WS-Security to encrypt and sign the SOAP messages being sent, or secure the transport layer by using an HTTPS endpoint. Failure to encrypt the soap message or transport layer will result in the username and password being sent in plain text.

Web Container Compatibility

WSRP and WS-Security is only supported on JBoss Portal Platform when running on JBoss Enterprise Application Platform 6.

29.4.3. Credentials

When the consumer sends the user credentials to the producer, it is sending the credentials for the currently authenticated user in the consumer. This makes signing in to remote portlets transparent to end users, but also requires that the producer and consumer use the same credentials. This means that the username and password must be the same and valid on both servers.
The recommended approach for this situation would be to use a common LDAP configuration. Refer to Chapter 27, LDAP Integration to correctly configure LDAP on JBoss Portal Platform.

29.4.3.1.  WS-Security Configuration

29.4.3.1.1. Introduction
JBoss Enterprise Application Platform 6 uses a different web service implementation than the previous versions: it is now uses the JBossWS CXF Stack instead of the JBossWS Native Stack. Due to these changes, the way we configure WS-Security for WSRP with JBoss Portal Platform on JBoss Enterprise Application Platform 6 has changed.

Note

We only support one ws-security configuration option for the producer. All consumers accessing the producer will have to conform to this security constraint. This means if the producer requires encryption, all consumers will be required to encrypt their messages when accessing the producer.
We only support one ws-security configuration option to be used by all the consumers. A consumer has the option to enable or disable ws-security, which allows for one or more consumers to use ws-security while the others do not.
29.4.3.1.2. Overview
CXF uses interceptors to extend and configure its behavior. There are two main types of interceptors: inInterceptors and outInterceptors. InInterceptors are invoked for communication coming into the client or server, while outInterceptors are invoked when the client or server sends a message.
So for the WSRP case, the communication from the consumer to the producer is governed by the consumer's OutInterceptor and the producer's InInterceptors. The communication from the producer to the consumer is governed by the producer's OutInterceptor and the consumer's InInterceptor. This may mean having to configure 4 Interceptors.
When dealing with user propagation, only the consumer sends the user credentials to the producer. So Username Tokens only need to be configured for the consumer's OutInterceptor and the producer's InInterceptor.
When dealing with things like encryption, you will most likely want to encrypt the message from the consumer to the producer and also the message from the producer to the consumer. This means that encryption properties must be configured for all 4 interceptors.
Please see the CXF Documentation for more details on interceptors and their types: http://cxf.apache.org/docs/interceptors.html
To support ws-security, JBoss Portal Platform 6 uses CXF's WSS4J Interceptors which handle all ws-security related tasks. Please see the CXF Documentation for more details: http://cxf.apache.org/docs/ws-security.html

29.4.3.2. WSS4J Interceptors and WSRP

The WSS4J Interceptors are configured using using simple property files. WSRP looks for specific property files to know whether or not in/out interceptors must be added and configured for either consumers or producer.
Theses files are located in the standalone/configuration/jpp/wsrp/cxf/ws-security directory of your the JBoss Enterprise Application Server 6 home directory.
Consumer-specific files are in the consumer subdirectory while producer-specific files should be located in the producer subdirectory. To add and configure a WSS4J interceptor, you just need to add the proper configuration file in the proper directory. If no configuration file is found for a specific interceptor type, then no such interceptor will be added.
"In" interceptors are configured using WSS4JInInterceptor.properties files while "out" interceptors are configured using WSS4JOutInterceptor.properties files.

Table 29.1. Files needed to configure interceptor for WSRP

Side Interceptor Type Configuration File
Consumer IN standalone/configuration/gatein/wsrp/cxf/ws-security/consumer/WSS4JInInterceptor.properties
OUT standalone/configuration/gatein/wsrp/cxf/ws-security/consumer/WSS4JOutInterceptor.properties
Producer IN standalone/configuration/gatein/wsrp/cxf/ws-security/producer/WSS4JInInterceptor.properties
OUT standalone/configuration/gatein/wsrp/cxf/ws-security/producer/WSS4JOutInterceptor.properties

Please refer to the CXF or WSS4J documentation for instructions and options available for each type of interceptors.
29.4.3.2.1. User Propagation
User propagation can be configured to be used over WSRP with ws-security. What this means is that a user logged into a consumer can have their credentials propagated over to the producer. This allows the producer to authenticate the user and any portlet on the producer (a remote portlet from the consumer's perspective) will view the user as being properly authenticated. This allows for remote portlets to access things like user information.

Note

This only works if the user's credentials on the producer and consumer are the same. This may require using a common authentication mechanism, such as LDAP.
This requires some special options when configuring the producer and server.

29.4.3.3.  WS-Security Consumer Configuration

In order to configure ws-security on the consumer side, you will have to configure the WSS4J Interceptors as seen above. This will require having to configure the WSS4JInInterceptor and/or WSS4JOutInterceptor. You will also need to check the 'Enable WS-Security' checkbox on the WSRP Admin Portlet for the consumer configuration to take effect.
WSRP Consumers Configuration

Figure 29.1. WSRP Consumers Configuration


29.4.3.3.1. Special JBoss Portal Platform Configuration Options for User Propagation
In order to handle user propagation in JBoss Portal Platform across ws-security, a couple of special configuration options have been created which should be applied to the consumer's WSS4JOutInterceptor.
29.4.3.3.1.1. Custom 'user' option
user=gtn.current.user
This option sets the 'user' property to the currently authenticated user on the consumer.
29.4.3.3.1.2. Custom 'action' option
action=gtn.UsernameToken.ifCurrentUserAuthenticated
If a user is currently authenticated, it will replace the 'gtn.UsernameToken.ifCurrentUserAuthenticated' with 'UsernameToken'. If the current user is an unauthenticated user, 'gtn.UsernameToken.ifCurrentUserAuthenticated' will be removed from the action list. If no other actions are specified, then the WSS4J interceptor will not be added to the consumer. This allows you to only use ws-security when dealing with authenticated users, and not for anonymous users.

Note

This requires that the user option is set to 'gtn.current.user'
29.4.3.3.1.3. Custom PasswordCallbackClass
To set the password for the username token, we need to specify the password in a callback class. See the cxf ws-security documentation for more details http://cxf.apache.org/docs/ws-security.html
A special callback class has already been created which handles this for you: CurrentUserPasswordCallback. This class will retrieve the currently authenticated user's password and set this as the password in the callback object.
passwordCallbackClass=org.gatein.wsrp.wss.cxf.consumer.CurrentUserPasswordCallback

29.4.3.4. Producer Configuration

The configuration of the producer is similar to that of the consumer. It also requires having to configure the WSS4JInInterceptor and/or WSS4JOutInterceptor.
29.4.3.4.1. Special Configuration Options for User Propagation
To properly propagate user information on the producer-side, you will need to use GTNSubjectCreatingInterceptor instead of a regular WSS4JInInterceptor. This JBoss Portal Platform specific "in" interceptor is an extension of the traditional WSS4JInInterceptor and therefore can be configured similarly and accept the same configuration properties. To specify that you want to use the GTNSubjectCreatingInterceptor, please create a property file at standalone/configuration/gatein/wsrp/cxf/ws-security/producer/GTNSubjectCreatingInterceptor.properties instead of the regular WSS4JInInterceptor.properties file.
This Interceptor will handle the ws-security headers and retrieve the users credentials. It will then use these credentials to perform a login on the producer site, thus authenticating the user on the producer and makes the user available to remote portlets.

Note

This class also extends org.jboss.wsf.stack.cxf.security.authentication.SubjectCreatingInterceptor and can accept the same properties this class normally accepts. See the JBossWS documentation for options and more information.
29.4.3.4.2. Custom 'action' option
action=gtn.UsernameToken.ifAvailable
When this option is activated, the interceptor will set the action to 'UsernameToken' when the received SOAP message contains ws-security headers. If no ws-security header is included in the message, then no action is taken and the interceptor is not run. This is useful for dealing with authenticated and unauthentcated users trying to access the producer.

29.4.3.5. Sample Configuration using the UsernameToken and User Propagation

Warning

This example configuration does not encrypt the message. This means the username and password will be sent between the producer and consumer in plain text. This is a security concern and is only being shown as a simple example. It is up to administrators to properly configure the WSS4J Interceptors to encrypt messages or to only use https communication between the producer and consumer.
29.4.3.5.1. Producer Setup
  1. create the following file: standalone/configuration/gatein/wsrp/cxf/ws-security/producer/GTNSubjectCreatingInterceptor.properties
  2. set the content of GTNSubjectCreatingInterceptor.properties created in step 1 to:
    action=gtn.UsernameToken.ifAvailable
  3. start the producer server
29.4.3.5.2. Consumer Setup
  1. create the following file: standalone/configuration/gatein/wsrp/cxf/ws-security/consumer/WSS4JOutInterceptor.properties
  2. set the content of the WSS4JOutInterceptor.properties created in step 1 to:
    passwordType=PasswordText
    user=gtn.current.user
    action=gtn.UsernameToken.ifCurrentUserAuthenticated
    passwordCallbackClass=org.gatein.wsrp.wss.cxf.consumer.CurrentUserPasswordCallback
  3. start the consumer server
  4. in the WSRP admin portlet, click the 'enable ws-security' checkbox
  5. access a remote portlet (for example, the user identity portlet included as an example portlet in JBoss Portal Platform) and verify that the authenticated user is the same as the one on the consumer

29.4.3.6. Sample Configuration Securing the Endpoints using Encryption and Signing

The following steps outline how to configure the producer and consumer to encrypt and sign SOAP messages passed between the producer and consumer. This example only deals with SOAP messages being sent between the producer and consumer, and not with user propagation.

Note

Some of the configuration options specified here are based on the content at http://cxf.apache.org/docs/ws-security.html and http://www.jroller.com/gmazza/entry/cxf_x509_profile More information may be available at these sites.
29.4.3.6.1. Password Callback Class
WSS4J uses a Java class to specify the password when performing any security related actions. For the purpose of these encryption and signing examples, we will use the same password for the producer's and consumer's keystore (wsrpAliasPassword). This simplifies things a bit as it means we can use just one password callback class for both the producer and consumer.
Example test.TestCallbackHandler class:
package test;
 
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
 
import org.apache.ws.security.WSPasswordCallback;
import org.gatein.wsrp.wss.cxf.consumer.CurrentUserPasswordCallback;
 
public class TestCallbackHandler implements CallbackHandler
{
 
    @Override
    public void handle(Callback[] callbacks) throws IOException,
            UnsupportedCallbackException
    {
 
        //First check if we have any user name token call backs to add.
        //NOTE: only needed if using username tokens, and you want the currently authenticated users password added
        CurrentUserPasswordCallback currentUserPasswordCallback = new CurrentUserPasswordCallback();
        currentUserPasswordCallback.handle(callbacks);
 
        for (Callback callback: callbacks)
        {
            if (callback instanceof WSPasswordCallback)
            {
                WSPasswordCallback wsPWCallback = (WSPasswordCallback)callback;
                // since the CurrentUserPasswordCallback already handles the USERNAME_TOKEN case, we don't want to set it in this case
                if (wsPWCallback.getUsage() != WSPasswordCallback.USERNAME_TOKEN)
                {
                    wsPWCallback.setPassword("wsrpAliasPassword");
                }
            }
        }
    }
}

Note

CallbackHandler implementations are provided to JBoss Portal Platform using the standard Java ServiceLoader infrastructure. As such, CallbackHandler implementations need to be bundled in a jar containing a file META-INF/services/javax.security.auth.callback.CallbackHandler specifying the fully qualified name of the CallbackHandler implementation class. This jar then needs to be put in the gatein/extensions directory of your JBoss Portal Platform installation.
You can see a working example of a CallbackHandler implentation at https://github.com/gatein/gatein-wsrp/tree/master/examples/wss-callback
29.4.3.6.2. Configuring the Keystores

Note

In this example we are making it a bit easier by specifying the same keystore password for both the producer and consumer, as they can use the same password callback class.
  1. Generate the producer's private encryption keys
    keytool -genkey -alias producerAlias -keypass wsrpAliasPassword -keystore producer.jks -storepass keyStorePassword -dname "cn=producerAlias" -keyalg RSA
  2. Export the producer's public key
    keytool -export -alias producerAlias -file producerkey.rsa -keystore producer.jks -storepass keyStorePassword
  3. Generate the consumer's private encryption keys
    keytool -genkey -alias consumerAlias -keypass wsrpAliasPassword -keystore consumer.jks -storepass keyStorePassword -dname "cn=consumerAlias" -keyalg RSA
  4. Export the consumer's public key
    keytool -export -alias consumerAlias -file consumerkey.rsa -keystore consumer.jks -storepass keyStorePassword
  5. Import the consumer's public key into the producer's keystore
    keytool -import -alias consumerAlias  -file consumerkey.rsa -keystore producer.jks -storepass keyStorePassword -noprompt
  6. Import the producer's public key into the consumer's keystore
    keytool -import -alias producerAlias  -file producerkey.rsa -keystore consumer.jks -storepass keyStorePassword -noprompt
  7. Copy the producer.jks file to the standalone/configuration/gatein/wsrp/cxf/ws-security/producer directory on the producer
  8. Copy the consumer.jks file to the standalone/configuration/gatein/wsrp/cxf/ws-security/consumer directory on the consumer
29.4.3.6.3. Configuring the Producer
  1. Create standalone/configuration/gatein/wsrp/cxf/ws-security/producer/WSS4JInInterceptor.properties with the following content. This will configure the incoming message between the producer and the consumer
    action=Signature Encrypt Timestamp
    signaturePropFile=producer-security.properties
    decryptionPropFile=producer-security.properties
    passwordCallbackClass=test.TestCallbackHandler
  2. Create standalone/configuration/gatein/wsrp/cxf/ws-security/producer/WSS4JOutInterceptor.properties with the following content. This will configure the outgoing message between the producer and the consumer
    action=Signature Encrypt Timestamp
    signaturePropFile=producer-security.properties
    encryptionPropFile=producer-security.properties
    passwordCallbackClass=test.TestCallbackHandler
    user=producerAlias
    encryptionUser=consumerAlias
    signatureUser=producerAlias
  3. Create standalone/configuration/gatein/wsrp/cxf/ws-security/producer/producer-security.properties with the following content:
    org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
    org.apache.ws.security.crypto.merlin.keystore.type=jks
    org.apache.ws.security.crypto.merlin.keystore.password=keyStorePassword
    org.apache.ws.security.crypto.merlin.file=producer.jks
  4. The passwordCallbackClass property in these configuration files needs to match the fully qualified name of your CallbackHandler implementation class. In our case, it is test.TestCallbackHandler .
29.4.3.6.4. Configuring the Consumer
  1. Create standalone/ configuration/gatein/wsrp/cxf/ws-security/consumer/WSS4JOutInterceptor.properties with the following content. This will configure the outgoing message between the consumer and the producer
    action=Signature Encrypt Timestamp
    signaturePropFile=consumer-security.properties
    encryptionPropFile=consumer-security.properties
    passwordCallbackClass=test.TestCallbackHandler
    user=consumerAlias
    encryptionUser=producerAlias
    signatureUser=consumerAlias
  2. Create standalone/ configuration/gatein/wsrp/cxf/ws-security/consumer/WSS4JInInterceptor.properties with the following content. This will configure the incoming message between the consumer and the producer
    action=Signature Encrypt Timestamp
    signaturePropFile=consumer-security.properties
    decryptionPropFile=consumer-security.properties
    passwordCallbackClass=test.TestCallbackHandler
  3. Create standalone/configuration/gatein/wsrp/cxf/ws-security/consumer/consumer-security.properties with the following content:
    org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
    org.apache.ws.security.crypto.merlin.keystore.type=jks
    org.apache.ws.security.crypto.merlin.keystore.password=keyStorePassword
    org.apache.ws.security.crypto.merlin.file=consumer.jks
  4. The passwordCallbackClass property in these configuration files needs to match the fully qualified name of your CallbackHandler implementation class. In our case, it is test.TestCallbackHandler .

29.4.3.7. Sample Configuration using UsernameToken, Encryption and Signing with User Propagation

The following steps outline how to configure the producer and consumer to encrypt and sign the soap message as well as use user propagation between the producer and consumer.
29.4.3.7.1. Configure the Producer
Follow the steps outlined in the Sample Configuration Securing the Endpoints using Encryption and Signing section but make the following changes:
  1. rename the WSS4JInInterceptor.properties file to GTNSubjectCreatingInterceptor.properties
  2. set the action property in GTNSubjectCreatingInterceptor.properties as:
    action= gtn.UsernameToken.ifAvailable Signature Encrypt Timestamp
  3. set the passwordType in GTNSubjectCreatingInterceptor.properties as:
    passwordType=PasswordText
29.4.3.7.2. Configure the Consumer
Follow the steps outlined in the Sample Configuration Securing the Endpoints using Encryption and Signing section but make the following changes:
  1. set the action property in WSS4JOutInterceptor.properties as:
    action=gtn.UsernameToken.ifCurrentUserAuthenticated Signature Encrypt Timestamp
  2. set the user in the WSS4JOutInterceptor.properties as:
    user=gtn.current.user
  3. set the passwordType in the WSS4JOutInterceptor.properties as:
    passwordType=PasswordText

29.5. Making a portlet remotable

Important

Only JSR-286 (Portlet 2.0) portlets can be made remotable as the mechanism to expose a portlet to WSRP relies on a JSR-286-only functionality.
JBoss Portal Platform does not, by default, expose local portlets for consumption by remote WSRP consumers. In order to make a portlet remotely available, it must be made remotable by marking it as such in the associated portlet.xml. This is accomplished by using a specific org.gatein.pc.remotable container-runtime-option. Setting its value to true makes the portlet available for remote consumption, while setting its value to false will not publish it remotely. As specifying the remotable status for a portlet is optional, you do not need to do anything if you do not need your portlet to be available remotely.

Example 29.1. Single Portlet Remotable Example

The "BasicPortlet" portlet is specified as being remotable.
<?xml version="1.0" standalone="yes"?>
<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-app>
   <portlet>
      <portlet-name>BasicPortlet</portlet-name>

      ...

      <container-runtime-option>
         <name>org.gatein.pc.remotable</name>
         <value>true</value>
      </container-runtime-option>
   </portlet>
</portlet-app>

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

Example 29.2. Multiple Portlets Remotable Example

The example defines two portlets. The org.gatein.pc.remotable container-runtime-option being set to true at the portlet-app level, all portlets defined in this particular portlet application are exposed remotely by JBoss Portal Platform's WSRP producer.
<?xml version="1.0" standalone="yes"?>
<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-app>

   <portlet>
      <portlet-name>RemotelyExposedPortlet</portlet-name>
      ...
   </portlet>
   <portlet>
      <portlet-name>NotRemotelyExposedPortlet</portlet-name>
      ...
      <container-runtime-option>
         <name>org.gatein.pc.remotable</name>
         <value>false</value>
      </container-runtime-option>
   </portlet>

   <container-runtime-option>
      <name>org.gatein.pc.remotable</name>
      <value>true</value>
   </container-runtime-option>
</portlet-app>
It is possible to override the default behavior by specifying a value for the org.gatein.pc.remotable container-runtime-option at the portlet level.
The RemotelyExposedPortlet inherits the remotable status defined at the portlet-app level since it does not specify a value for the org.gatein.pc.remotable container-runtime-option. The NotRemotelyExposedPortlet, however, overrides the default behavior and is not remotely exposed. Note that in the absence of a top-level org.gatein.pc.remotable container-runtime-option value set to true, portlets are not remotely exposed.

29.6. Consuming WSRP portlets from a remote Consumer

WSRP Producers vary a lot as far as how they are configured. Most of them require that you specify the URL for the Producer's WSDL definition. Please refer to the remote producer's documentation for specific instructions. For instructions on how to do so in JBoss Portal Platform, please refer to Consumer Configuration.
JBoss Portal Platform's Producer is automatically set up when you deploy a portal instance with the WSRP service. You can access the WSDL file at http://{hostname}:{port}/wsrp-producer/v2/MarkupService?wsdl. If you wish to use only the WSRP 1 compliant version of the producer, please use the WSDL file found at http://{hostname}:{port}/wsrp-producer/v1/MarkupService?wsdl. The default hostname is localhost and the default port is 8080.

29.7. Consuming remote WSRP portlets in JBoss Portal Platform

29.7.1. Overview

To be able to consume WSRP portlets exposed by a remote producer, JBoss Portal Platform's WSRP consumer needs to know how to access that remote producer. You can configure access to a remote producer using the provided configuration portlet. Alternatively, it is also possible to configure access to remote producers using an XML descriptor, though it is recommended (and easier) to do so via the configuration portlet.
Once a remote producer has been configured, the portlets that it exposes are then available in the Application Registry to be added to categories and then to pages.

29.7.2. Configuring a remote producer using the configuration portlet

This section will cover the steps of defining access to a remote producer using the configuration portlet so that its portlets can be consumed within JBoss Portal Platform. We will configure access to NetUnity's public WSRP producer.

Note

Some WSRP producers do not support chunked encoding that is activated by default by JBoss WS. If your producer does not support chunked encoding, your consumer will not be able to properly connect to the producer. This will manifest itself with the following error: Caused by: org.jboss.ws.WSException: Invalid HTTP server response [503] - Service Unavailable. Please see this wiki page for more details.
JBoss Portal Platform provides a portlet to configure access (among other functions) to remote WSRP Producers graphically. Starting with 6.0.0, the WSRP configuration portlet is installed by default. You can find it at http://localhost:8080/portal/login?initialURI=%2Fportal%2Fprivate%2Fclassic%2FwsrpConfigurationp&username=root&password=gtn
You should see a screen similar to:
This screen presents all the configured Consumers associated with their status and possible actions on them. A Consumer can be active or inactive. Activating a Consumer means that it is ready to act as a portlet provider. Note also that a Consumer can be marked as requiring refresh meaning that the information held about it might not be up to date and refreshing it from the remote Producer might be a good idea. This can happen for several reasons: the service description for that remote Producer has not been fetched yet, the cached version has expired or modifications have been made to the configuration that could potentially invalidate it, thus requiring re-validation of the information.

Note

The WSRP configuration was not installed by default in previous versions of JBoss Portal Platform. We include here the legacy instructions on how to install this portlet in case you ever need to re-install it.
Use the usual procedure to log in as a Portal administrator and go to the Application Registry. With the default install, you can open the Group > Administration > Application Registry screen and add the WSRP Configuration portlet to the Administration category. If you use the Import Applications functionality, the WSRP Configuration portlet will be automatically added to the Administration category.
Now that the portlet is added to a category, it can be added to a page and used. It is recommended that you add it to the same page as the Application Registry as operations relating to WSRP and adding portlets to categories are somewhat related. Go ahead and add the WSRP Configuration portlet to the page using the standard procedure.
Next, create a new Consumer called netunity. Type "netunity" in the "Create a consumer named:" field then click on "Create consumer":
You should now see a form allowing you to enter/modify the information about the Consumer. Set the cache expiration value to 300 seconds, leave the default timeout value for web services (WS) operations and enter the WSDL URL for the producer in the text field and press the "Refresh & Save" button:
This will retrieve the service description associated with the Producer which WSRP interface is described by the WSDL file found at the URL you just entered. In this case, querying the service description will reveal that the Producer requires registration, requested three registration properties and that some of these properties are missing values:
This particular producer requests simple Yes or No values for the three registration properties. Entering No, Yes and No (in that order) for the values and then pressing the "Refresh & Save" button should result in:

Note

At this point, there is no automated way to learn about which possible values (if any) are expected by the remote Producer. Sometimes, the possible values will be indicated in the registration property description but this is not always the case... Please refer to the specific Producer's documentation.
If you had been dealing with a producer which required registration but did not require any registration properties, as is the case for the selfv2 consumer (the consumer that accesses the portlets made remotely available by JBoss Portal Platform's producer via WSRP 2), you would have seen something similar to the screenshot below, after pressing the "Refresh & Save" button:

29.7.3. Configuring access to remote producers via XML

While it is recommended you use the WSRP Configuration portlet to configure Consumers, the component provides an alternative way to configure consumers by adding an XML file called wsrp-consumers-config.xml in the JPP_DIST/standalone/configuration/gatein/wsrp/ directory.

Note

An XML Schema defining which elements are available to configure Consumers via XML can be found in JPP_DIST/modules/org/gatein/wsrp/main/wsrp-integration-api-2.2.2.Final.jar/xsd/gatein_wsrp_consumer_1_0.xsd

Important

Once the XML configuration file for consumers has been read upon the WSRP service first start, the associated information is put under control of JCR (Java Content Repository). Subsequent launches of the WSRP service will use the JCR-stored information and ignore the content of the XML configuration file.

29.7.3.1. Required configuration information

This section covers what information needs to be provided to configure access to a remote producer.
First, you need to provide an identifier for the producer you are configuring so that you can refer to it afterwards. This is accomplished via the mandatory id attribute of the <wsrp-producer> element.
JBoss Portal Platform also needs to learn about the remote producer's endpoints to be able to connect to the remote web services and perform WSRP invocations. This is accomplished by specifying the URL for the WSDL description for the remote WSRP service, using the <endpoint-wsdl-url> element.
Both the id attribute and <endpoint-wsdl-url> elements are required for a functional remote producer configuration.

29.7.3.2. Optional 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 round-trips 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, JBoss Portal Platform 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, it is recommended that you use this caching facility to minimize bandwidth usage.
It is also possible to define a timeout after which WS operations are considered as failed. This is helpful to avoid blocking the WSRP service, waiting on the service that does not answer. Use the ws-timeout attribute of the <wsrp-producer> element to specify how many milliseconds the WSRP service will wait for a response from the remote producer before timing out and giving up.
Additionally, some producers require consumers to register with them before authorizing them to access their offered portlets. If you know that information beforehand, you can provide the required registration information in the producer configuration so that the consumer can register with the remote producer when required.

Note

At this time, though, only simple String properties are supported and it is not possible to configure complex registration data. This should, however, be sufficient for most cases.
Registration configuration is done via the <registration-data> element. Since JBoss Portal Platform 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 JBoss Portal Platform via the <consumer-name> element. If you choose to provide a consumer name, please remember that this should uniquely identify your consumer.

29.7.3.3. Examples

Here is the configuration of the selfv1 and selfv2 consumers as found in JPP_HOME/gatein/extensions/gatein-wsrp-integration.ear/lib/extension-component-2.2.2.Final.jar/conf/wsrp-consumers-config.xml with a cache expiring every 500 seconds and with a 50 second timeout for web service operations.

Note

This file contains the default configuration and you should not need to edit it. If you want to make modifications to it, it is recommended that you follow the procedure detailed in consumer_gui .

Example 29.3. Example

<?xml version='1.0' encoding='UTF-8' ?>
<deployments xmlns="http://www.gatein.org/xml/ns/gatein_wsrp_consumer_1_0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_wsrp_consumer_1_0 http://www.jboss.org/portal/xsd/gatein_wsrp_consumer_1_0.xsd">
   <deployment>
      <wsrp-producer id="selfv1" expiration-cache="500" ws-timeout="50000">
         <endpoint-wsdl-url>http://localhost:8080/wsrp-producer/v1/MarkupService?wsdl</endpoint-wsdl-url>
         <registration-data/>
      </wsrp-producer>
   </deployment>
   <deployment>
      <wsrp-producer id="selfv2" expiration-cache="500" ws-timeout="50000">
         <endpoint-wsdl-url>http://localhost:8080/wsrp-producer/v2/MarkupService?wsdl</endpoint-wsdl-url>
         <registration-data/>
      </wsrp-producer>
   </deployment>
</deployments>

Example 29.4. Registration Data and Cache Expiry Example

This example shows a WSRP descriptor with registration data and cache expiring every minute:
<?xml version='1.0' encoding='UTF-8' ?>
<deployments xmlns="http://www.gatein.org/xml/ns/gatein_wsrp_consumer_1_0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_wsrp_consumer_1_0 http://www.jboss.org/portal/xsd/gatein_wsrp_consumer_1_0.xsd">
<deployments>
   <deployment>
      <wsrp-producer id="AnotherProducer" expiration-cache="60">
         <endpoint-wsdl-url>http://example.com/producer/producer?WSDL</endpoint-wsdl-url>
         <registration-data>
            <property>
               <name>property name</name>
               <lang>en</lang>
               <value>property value</value>
            </property>
         </registration-data>
      </wsrp-producer>
   </deployment>
</deployments>

29.7.4. Adding remote portlets to categories

If you go to the Application Registry and examine the available portlets by clicking on the Portlet link, you will now be able to see remote portlets if you click on the REMOTE tab in the left column:
These portlets are, of course, available to be used such as regular portlets: they can be used in categories and added to pages. If you use the Import Applications functionality, they will also be automatically imported in categories based on the keywords they define.
More specifically, if you want to add a WSRP portlet to a category, you can access these portlets by selecting wsrp in the Application Type drop-down menu:

29.7.5. Adding remote portlets to pages

Since remote portlets can be manipulated just like regular portlets, you can add them to pages just like you would do for a regular portlet. Please refer to the appropriate section of the documentation for how to do so.
Of note, though, is that, starting with version 5.2 of JBoss Portal Platform, it is now possible to also add a remote portlet to a pages.xml configuration file. This is accomplished using the <wsrp> element instead of the <portlet> element in your pages.xml document. While <portlet> references a local portlet using the name of the application in which the portlet is contained and the portlet name itself to identify which portlet to use, <wsrp> references a remote portlet using a combination of the consumer identifier for the producer publishing the portlet and the portlet handle identifying the portlet within the context of the producer.
The format for such a reference to a remote portlet is a follows: first, the identifier of the consumer that accesses the remote producer publishing the remote portlet, then a separator (currently a period (.)) and finally the portlet handle for that portlet, which is a string provided by the producer to identify the portlet.
Since there currently is no easy way to determine the correct portlet handle, it is recommended that you use the graphical user interface to add remote portlets to pages instead of using pages.xml.

Note

For remote portlets published by JBoss Portal Platform's WSRP producer, the portlet handles are, at the time of this writing, following the /<portlet application name>.<portlet name> format.

29.7.5.1. Example

In the following example, two portlets are defined for a page named Test in the pages.xml configuration. They are actually references to the same portlet, albeit one accessed locally and the other one accessing it via the selfv2 consumer which consumes JBoss Portal Platform's WSRP producer. You can see that the first one is local (the <portlet-application> with the 'Added locally' title) and follows the usual declaration. The second portlet (the one with the 'Added from selfv2 consumer' title) comes from the selfv2 consumer and uses the <wsrp> element instead of <portlet> element and follows the format for portlets coming from the JBoss Portal Platform's WSRP producer.

Example 29.5. Example

<page>
   <name>Test</name>

   ...

   <portlet-application>
      <portlet>
         <application-ref>richFacesPortlet</application-ref>
         <portlet-ref>richFacesPortlet</portlet-ref>
      </portlet>
      <title>Added locally</title>

      ...

   </portlet-application>

   <portlet-application>
      <wsrp>selfv2./richFacesPortlet.richFacesPortlet</wsrp>
      <title>Added from selfv2 consumer</title>

      ...

   </portlet-application>
</page>

29.8. Consumers maintenance

29.8.1. Modifying a currently held registration

29.8.1.1. Registration modification for service upgrade

Producers often offer several levels of service depending on consumers' subscription levels (for example). This is implemented at the WSRP level with the registration concept: producers can assert which level of service to provide to consumers based on the values of given registration properties.
There might also be cases where you just want to update the registration information because it has changed. For example, the producer required you to provide a valid email and the previously email address is not valid anymore and needs to be updated.
It is therefore sometimes necessary to modify the registration that concretizes the service agreement between a consumer and a producer. The following is an example of a producer requiring a valid email (via an email registration property) as part of its required information that consumers need to provide to be properly registered.
Suppose now that you would like to update the email address that you provided to the remote producer when you first registered. You will need to tell the producer that the registration data has been modified.
Select the consumer for the remote producer in the available consumers list to display its configuration. Assuming you want to change the email you registered with to foo@example.com, change its value in the field for the email registration property:
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:

29.8.1.2. Registration modification on producer error

It can also happen that a producer administrator decided to change its requirement for registered consumers. JBoss Portal Platform will attempt to help you in this situation. This section will walk through an example using the selfv2 consumer (assuming that registration is requiring a valid value for an email registration property). If you go to the configuration screen for this consumer, you should see:
Now suppose that the administrator of the producer now additionally requires a value to be provided for a name registration property. Steps on how to do perform this operation in JBoss Portal Platform are outlined in the section on how to configure JBoss Portal Platform's producer (Section 29.9, “Configuring JBoss Portal Platform's WSRP Producer”). Operations with this producer will now fail. If you suspect that a registration modification is required, you should go to the configuration screen for this remote producer and refresh the information held by the consumer by pressing "Refresh & Save":
As you can see, the configuration screen now shows the currently held registration information and the expected information from the producer. Enter a value for the name property and then click on "Modify registration". If all went well and the producer accepted your new registration data, you should see something similar to:

Note

WSRP 1 makes it rather difficult to ascertain for sure what caused an OperationFailedFault as it is the generic exception returned by producers if something did not 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 exists when using WSRP 1.

29.8.2. Consumer operations

Several operations are available from the consumer list view of the WSRP configuration portlet:
The available operations are:
  • Configure: displays the consumer details and allows user to edit them
  • Refresh: forces the consumer to retrieve the service description from the remote producer to refresh the local information (offered portlets, registration information, etc.)
  • Activate/Deactivate: activates/deactivates a consumer, governing whether it will be available to provide portlets and receive portlet invocations
  • Register/Deregister: registers/deregisters a consumer based on whether registration is required and/or acquired
  • Delete: destroys the consumer, after deregistering it if it was registered
  • Export: exports some or all of the consumer's portlets to be able to later import them in a different context
  • Import: imports some or all of previously exported portlets

Note

Import/Export functionality is only available to WSRP 2 consumers of producers that support this optional functionality. Import functionality is only available if portlets had previously been exported.

29.8.3. Importing and exporting portlets

Import and export are new functionality added in WSRP 2. Exporting a portlet allows a consumer to get an opaque representation of the portlet which can then be use by the corresponding import operation to reconstitute it. It is mostly used in migration scenarios during batch operations. Since JBoss Portal Platform does not currently support automated migration of portal data, the functionality that is provided as part of WSRP 2 is necessarily less complete than it could be with full portal support.
The import/export implementation in JBoss Portal Platform allows users to export portlets from a given consumer. These portlets can then be used to replace existing content on pages. This is accomplished by assigning previously exported portlets to replace the content displayed by windows on the portal's pages. Below is an example to make things clearer.
Clicking on the "Export" action for a given consumer will display the list of portlets currently made available by this specific consumer. An example of such a list is shown below:
Once portlets have been selected, they can be exported by clicking on the "Export" button thus making them available for later import:
You can re-import the portlets directly by pressing the "Use for import" button or, on the Consumers list page, using the "Import" action for a given consumer. This assumes that you used that second option and that you currently have several available sets of previously exported portlets to import from. After clicking the action link, you should see a screen similar to the one below:
As you can see this screen presents the list of available exports with available operations for each:
  • View: displays the export details as previously seen when the export was first performed
  • Delete: deletes the selected export, asking you for confirmation first
  • Use for import: selects the export to import portlets from
Once you have selected an export to import from, you will see a screen similar to the one below:
The screen displays the list of available exported portlets for the previously selected export. You can select which portlet you want to import by checking the checkbox next to its name. Next, you need to select the content of which window the imported portlet will replace. This process is done in three steps. This example assumes that you have the following page called page1 and containing two windows called NetUnity WSRP 2 Interop - Cache Markup (remote) and /samples-remotecontroller-portlet.RemoteControl (remote) as shown below:
In this example, you want to replace the content of the /samples-remotecontroller-portlet.RemoteControl (remote) by the content of the /ajaxPortlet.JSFAJAXPortlet portlet that you previously exported. To do so, you will check the checkbox next to the /ajaxPortlet.JSFAJAXPortlet portlet name to indicate that you want to import its data and then select the page1 in the list of available pages. The screen will then refresh to display the list of available windows on that page, similar to the one seen below:
Note that, at this point, you still need to select the window which content you want to replace before being able to complete the import operation. Select the /samples-remotecontroller-portlet.RemoteControl (remote) window, at which point the "Import" button will become enabled, indicating that you now have all the necessary data to perform the import. If all goes well, pressing that button should result in a screen similar to the one below:
If you now take a look at the page1 page, you should now see that the content /samples-remotecontroller-portlet.RemoteControl (remote) window has been replaced by the content of the /ajaxPortlet.JSFAJAXPortlet imported portlet and the window renamed appropriately:

29.8.4. Erasing local registration data

There are rare cases where it might be required to erase the local information without being able to de-register first. This is the case when a consumer is registered with a producer that has been modified by its administrator to not require registration anymore. If that ever was to happen (most likely, it will not), you can erase the local registration information from the consumer so that it can resume interacting with the remote producer. To do so, click on "Erase local registration" button next to the registration context information on the consumer configuration screen:

Warning:

This operation is dangerous as it can result in inability to interact with the remote producer if invoked when not required. A warning screen will be displayed to give you a chance to change your mind:

29.9. Configuring JBoss Portal Platform's WSRP Producer

29.9.1. Overview

The preferred way to configure the behavior of Portal's WSRP Producer is through the WSRP configuration portlet. Alternatively, it is possible to add an XML file called wsrp-producer-config.xml in the JPP_HOME/standalone/configuration/gatein/wsrp directory. Several aspects can be modified with respect to whether registration is required for consumers to access the Producer's services.

Note

An XML Schema defining which elements are available to configure Consumers via XML can be found in JPP_HOME/modules/org/gatein/wsrp/main/wsrp-integration-api-2.2.2.Final.jar/xsd/gatein_wsrp_producer_1_0.xsd

Important

It is important to note that once the XML configuration file for the producer has been read upon the WSRP service first start, the associated information is put under control of the JCR (Java Content Repository). Subsequent launches of the WSRP service will use the JCR-stored information and ignore the content of the XML configuration file.

29.9.2. Default configuration

The default producer configuration requires 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 the 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. Property validators will be discussed in greater detail later in Section 29.9.3, “Registration configuration”. This allows users to customize how the Portal's WSRP Producer determines whether a given registration property is valid or not.
JBoss Portal Platform provides a web interface to configure the producer's behavior. It is accessed by clicking on the "Producer Configuration" tab of the "WSRP" page of the "admin" portal. The image below is what you should see with the default configuration:
You can specify whether or not the producer will send the full service description to unregistered consumers, and, if it requires registration, which RegistrationPolicy to use (and, if needed, which RegistrationPropertyValidator), along with required registration property description for which consumers must provide acceptable values to successfully register.
The WSDL URLs to access the Portal's WSRP producer are displayed either in WSRP 1 or WSRP 2 mode.

29.9.3. Registration configuration

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.
To configure the producer starting with a blank state:
You will allow unregistered consumers to see the list of offered portlets so you leave the first checkbox ("Access to full service description requires consumers to be registered.") unchecked. You will, however, specify that consumers will need to be registered to be able to interact with the 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. Keep the default value. See Section 29.9.3.1, “Customization of Registration handling behavior” for further information about other available values.
Add 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:
Click "Save" to record the modifications.

Note

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

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. You saw the consumer side of that process in Section 29.8.1.2, “Registration modification on producer error”.

29.9.3.1. Customization of Registration handling behavior

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 for registration properties that are passed by a consumer to a producer.
Please refer to the Javadoc™ for org.gatein.registration.RegistrationPolicy and org.gatein.registration.policies.RegistrationPropertyValidator for more details on what is expected of each method.
Defining a registration policy is required for the producer to be correctly configured. If you don't provide one, the DefaultRegistrationPolicy associated to the DefaultRegistrationPropertyBehavior is used.
DefaultRegistrationPolicy

Figure 29.2. DefaultRegistrationPolicy


JBoss Portal Platform can automatically detect deployed implementations of RegistrationPolicy and RegistrationPropertyValidator, assuming they conform to the following rules:
  • The implementations must follow the Java ServiceLoader architecture:
    • They must be packaged as JARs
    • For RegistrationPolicy implementations, they must contain a special META-INF/services/org.gatein.registration.RegistrationPolicy file.
    • For RegistrationPropertyValidator implementations they must containMETA-INF/services/org.gatein.registration.policies.RegistrationPropertyValidator and include a line with the fully qualified name of the implementation class.

    Note

    It is possible to package several implementations in the same JAR file, provided that each implementation class is referenced on its own line in the appropriate service definition file. To make things easier, we provide an example project of a RegistrationPolicy implementation and packaging at https://github.com/gatein/gatein-wsrp/tree/master/examples/policy.
  • The implementation classes must provide a default, no-argument constructor for dynamic instantiation.
  • The implementation JARs must be deployed in JPP_HOME/gatein/extensions.
  • The implementation JARs should use the .wsrp.jar extension. This is not mandatory but helps process the file faster by marking it as a WSRP extension.
If you deployed the example RegistrationPolicy provided from the github repository (registration-policy-example.wsrp.jar) to the JPP_HOME/gatein/extensions directory, it will appear in the list of available policies in the producer configuration screen.

29.9.4. WSRP validation mode

The lack of conformance kit, and the wording of the WSRP specification leaves room for differing interpretations, resulting in interoperability issues. It is therefore possible to encounter issues when using consumers from different vendors.
A way to relax the validation that the WSRP producer performs on the data provided by consumers has been introduced to help with interoperability by accepting data that would normally be invalid. Note that this validation algorithm is only relaxed on aspects of the specification that are deemed harmless such as invalid language codes.
By default, the WSRP producer is configured in strict mode. If you experience issues with a given consumer, you might want to try to relax the validation mode. This is accomplished by unchecking the "Use strict WSRP compliance." checkbox on the Producer configuration screen.

29.10. Working with WSRP Extensions

29.10.1. Overview

The WSRP specifications allows for implementations to extend the protocol using Extensions . JBoss Portal Platform, as of its WSRP implementation version 2.2.0, provides a way for client code (e.g. portlets) to interact with such extensions in the form of several classes and interfaces gathered within the org.gatein.wsrp.api.extensions package , the most important ones being InvocationHandlerDelegate , ConsumerExtensionAccessor and ProducerExtensionAccessor .
To be able to use this API, you will need to include the wsrp-integration-api-$WSRP_VERSION.jar file to your project, where $WSRP_VERSION is the version of the JBoss Portal Platform WSRP implementation you wish to use, 2.2.2.Final being the current one. This can be done by adding the following dependency to your maven project:
<dependency>
  <groupId>org.gatein.wsrp</groupId>
  <artifactId>wsrp-integration-api</artifactId>
  <version>$WSRP_VERSION</version>
</dependency>

29.10.1.1. InvocationHandlerDelegate infrastructure

Using the InvocationHandlerDelegate infrastructure, custom behavior can now be inserted on either consumer or producer sides to enrich WSRP applications before and/or after portlet requests and/or responses. Please refer to the Javadoc for org.gatein.wsrp.api.extensions.InvocationHandlerDelegate for more details on this interface and how to implement it.

Warning

Since InvocationHandlerDelegate is a very generic interface, it could potentially be used for more than simply working with WSRP extensions. Moreover, since it has access to internal JBoss Portal Platform classes, it is important to be treat access to these internal classes as read-only to prevent any un-intentional side-effects.

29.10.1.2. Injecting InvocationHandlerDelegate implementations

Implementations of InvocationHandlerDelegate must follow the same constraints as RegistrationPolicy implementations as detailed in Customization of Registration handling behavior section of the Configuring GateIn's WSRP Producer chapter, in essence, they must follow the Java ServiceLoader architectural pattern and be deployed in the appropriate JPP_HOME/gatein/extensions directory.
You can specify only one InvocationHandlerDelegate implementation per side: one implementation for the consumer and another one for the producer. This is accomplished by passing the fully classified class name to the appropriate system property when the portal is started:
WSRP side
System property
consumer
org.gatein.wsrp.consumer.handlers.delegate
producer
org.gatein.wsrp.producer.handlers.delegate
29.10.1.2.1. Example
./standalone.sh -Dorg.gatein.wsrp.consumer.handlers.delegate=com.example.FooInvocationHandlerDelegate
will inject the com.example.FooInvocationHandlerDelegate class on the consumer side, assuming that class implements the org.gatein.wsrp.api.extensions.InvocationHandlerDelegate interface and is packaged and deployed appropriately as explained above.

29.10.1.3. Accessing extensions from client code

You can access extensions from client code using ConsumerExtensionAccessor and ProducerExtensionAccessor on the consumer and producer, respectively. Each interface provides several methods but you should only have to ever call two of them on each, as shown in the following table:
Interface
Relevant methods
ConsumerExtensionAccessor
  • public void addRequestExtension(Class targetClass, Object extension)
  • public List<UnmarshalledExtension> getResponseExtensionsFrom(Class responseClass)
ProducerExtensionAccessor
  • List<UnmarshalledExtension> getRequestExtensionsFor(Class targetClass)
  • void addResponseExtension(Class wsrpResponseClass, Object extension)
Please refer to the Javadoc for these classes for more details.

Note

We currently only support adding and accessing extensions from a core subset of WSRP classes pertaining to markup, interaction, resource or event requests and responses, as follows:
Request classes
Response classes
org.oasis.wsrp.v2.InteractionParams
org.oasis.wsrp.v2.MarkupResponse
org.oasis.wsrp.v2.EventParams
org.oasis.wsrp.v2.BlockingInteractionResponse
org.oasis.wsrp.v2.MarkupParams
org.oasis.wsrp.v2.HandleEventsResponse
org.oasis.wsrp.v2.ResourceParams
org.oasis.wsrp.v2.ResourceResponse

Note

We strongly recommend that you use org.w3c.dom.Element values as extensions for interoperability purposes.

29.10.2. Example implementation

We also provide a complete, end-to-end example to get you started, which you can find at https://github.com/gatein/gatein-wsrp/tree/master/examples/invocation-handler-delegate . This example shows how ExampleConsumerInvocationHandlerDelegate , a consumer-side InvocationHandlerDelegate implementation, can add information extracted from the consumer and pass it along to the producer, working in conjunction with ExampleProducerInvocationHandlerDelegate , the associated producer-side InvocationHandlerDelegate , to establish a round-trip communication channel outside of the standard WSRP protocol, implementing the following scenario:
  • ExampleConsumerInvocationHandlerDelegate attaches to the consumer to add the current session id as an extension to render requests sent to the producer.
  • ExampleProducerInvocationHandlerDelegate provides the counterpart of ExampleConsumerInvocationHandlerDelegate on the producer. It checks incoming render requests for potential extensions matching what ExampleConsumerInvocationHandlerDelegate sends and adds an extension of its own to the render response so that the consumer-side delegate can know that the information it passed was properly processed.

Part V. Advanced Development Foundations

Chapter 30. The eXo Kernel

30.1. eXo Kernel

JBoss Portal Platform is built as a set of services on top of a dependency injection kernel. The kernel provides configuration, life-cycle handling, component scopes and some core services.
The portal kernel and the JCR used in JBoss Portal Platform use the Inversion of Control (IoC) system to control service instantiation. This method is also known as 'dependency injection'.
In this system, services are not responsible for the instantiation of the components on which they depend. It prevents objects creating the instances of any objects referenced. This task is delegated to the container. The below image illustrates this:
Inversion of Control

Figure 30.1. Inversion of Control


There are two ways to inject a dependency :
Using a constructor:
public ServiceA(ServiceB serviceB)
Using setter methods:
public void setServiceB(ServiceB serviceB)
When a client service can not be stored in the container then the service locator pattern is used:
public ServiceA(){
this.serviceB =Container.getSInstance().getService(ServiceB.class);
}
The container package is responsible of building a hierarchy of containers. Each service will then be registered in one container or the other according to the XML configuration file it is defined in. It is important to understand that there can be several PortalContainer instances that all are children of the RootContainer.
The behavior of the hierarchy is similar to a class loader one, hence when you will lookup a service that depends on another one, the container will look for it in the current container and if it cannot be found, then it will look in the parent container. That way you can load all the reusable business logic components in the same container (here the RootContainer) and differentiate the service implementation from one portal instance to the other by just loading different service implementations in two sibling PortalContainers.
Therefore, if you look at the Portal Container as a service repository for all the business logic in a portal instance, then you understand why several PortalContainers allows you to manage several portals (each one deployed as a single war) in the same server by just changing XML configuration files.
The default configuration XML files are packaged in the service jar. There are three configuration.xml files, one for each container type. In that XML file, we define the list of services and their init parameters that will be loaded in the corresponding container.
Service components exist in two scopes. The first scope is represented by the RootContainer. It contains services that exist independently of any portal, and can be accessed by all portals.
The second scope, the PortalContainer, is portal-specific. Each portal exists in an instance of the PortalContainer. This scope contains services that are common for a set of portals, and services which should not be shared by all portals.
  • RootContainer: This is a base container. This container plays an important role during startup, but you should not use it directly.
  • PortalContainer: Created at the startup of the portal web application (in the init() method of the PortalController servlet)
Portal Containers

Figure 30.2. Portal Containers


As there can be several portal container instances per JVM. it is important to be able to configure the loaded services per instance. Therefore all the default configuration files located in the service impl jar can be overridden from the portal war.
Whenever a specific service is looked up through the PortalContainer, and the service is not available, the lookup is delegated further up to the RootContainer.
JBoss Portal Platform can have default instances of a certain component in the RootContainer, and portal specific instances in some or all PortalContainers, that override the default instance.
Whenever your portal application has to be integrated more closely with eXo services, these services can be looked up through the PortalContainer.

Important

Only officially documented services should be accessed this way, and used according to documentation, as most of the services are an implementation detail of eXo, and subject to change without notice.

30.1.1. Kernel configuration namespace

To be effective, the namespace URI http://www.exoplaform.org/xml/ns/kernel_1_2.xsd must be target namespace of the XML configuration file.
<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
           xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"
           xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd">


...
</configuration>

Note

Any values in the configuration files can be created thanks to variables since the eXo kernel resolves them, for example the following configuration will be well interpreted:
<configuration
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"
xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd">

<import>${db.configuration.path}/db.xml</import>
<import>${java.io.tmpdir}/bindfile.xml</import>
<import>simple.xml</import>

</configuration>
The variables that are supported, are System properties and variables that are specific to your portal container, see next sections for more details.

30.2. Configuration Retrieval

The container performs the following steps for configuration retrieval, depending on the container type.
The container is initialized by looking into different locations. This container is used by portal applications. Configurations are overloaded in the following lookup sequence:
  1. Services default RootContainer configurations from JAR files /conf/configuration.xml.
  2. Services default PortalContainer configurations from JAR files /conf/portal/configuration.xml.
  3. Web applications configurations from WAR files /WEB-INF/conf/configuration.xml

Note

The search looks for a configuration file in each JAR/WAR available from the classpath using the current thread context classloader. During the search these configurations are added to a set. If the service was configured previously and the current JAR contains a new configuration of that service the latest (from the current JAR/WAR) will replace the previous one. The last one will be applied to the service during the services start phase.

Warning

Take care to have no dependencies between configurations from JAR files (/conf/portal/configuration.xml and /conf/configuration.xml) since we have no way to know in advance the loading order of those configurations. In other words, if you want to overload some configuration located in the file /conf/portal/configuration.xml of a given JAR file, you must not do it from the file /conf/portal/configuration.xml of another JAR file but from another configuration file loaded after configurations from JAR files /conf/portal/configuration.xml.
After the processing of all configurations available in system, the container will initialize it and start each service in order of the dependency injection (DI).
The user/developer should be careful when configuring the same service in different configuration files. It's recommended to configure a service in its own JAR only. Or, in case of a portal configuration, strictly reconfigure the services in portal WAR files or in an external configuration.
There are services that can be (or should be) configured more than one time. This depends on business logic of the service. A service may initialize the same resource (shared with other services) or may add a particular object to a set of objects (shared with other services too). In the first case, it's critical who will be the last, i.e. whose configuration will be used. In the second case, it's no matter who is the first and who is the last (if the parameter objects are independent).
In case of problems with service configuration, it's important to know from which JAR/WAR it comes. For that purpose, the JVM system property org.exoplatform.container.configuration.debug can be used.
java -Dorg.exoplatform.container.configuration.debug ...
If the property is enabled, the container configuration manager will log the configuration adding process at INFO level.
......
   Add configuration jar:file:/D:/Projects/eXo/dev/exo-working/exo-tomcat/lib/exo.kernel.container-trunk.jar!/conf/portal/configuration.xml
   Add configuration jar:file:/D:/Projects/eXo/dev/exo-working/exo-tomcat/lib/exo.kernel.component.cache-trunk.jar!/conf/portal/configuration.xml
   Add configuration jndi:/localhost/portal/WEB-INF/conf/configuration.xml
        import jndi:/localhost/portal/WEB-INF/conf/common/common-configuration.xml
        import jndi:/localhost/portal/WEB-INF/conf/database/database-configuration.xml
        import jndi:/localhost/portal/WEB-INF/conf/ecm/jcr-component-plugins-configuration.xml
        import jndi:/localhost/portal/WEB-INF/conf/jcr/jcr-configuration.xml 
   ......
The effective configuration of the StandaloneContainer, RootContainer and/or PortalContainer can be known thanks to the method getConfigurationXML() that is exposed through JMX at the container's level. This method will give you the effective configuration in XML format that has been really interpreted by the kernel. This could be helpful to understand how a given component or plug-in has been initialized.

30.3. Advanced concepts for the PortalContainers

Since eXo JCR 1.12, we added a set of new features that have been designed to extend portal applications such as JBoss Portal Platform.

30.3.1. Add new configuration files from a WAR file

A ServletContextListener called org.exoplatform.container.web.PortalContainerConfigOwner has been added in order to notify the application that a given web application provides some configuration to the portal container, and this configuration file is the file WEB-INF/conf/configuration.xml available in the web application itself.
If your war file contains some configuration to add to the PortalContainer simply add the following lines in your web.xml file.
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
                 "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
...
  <!-- ================================================================== -->
  <!--           LISTENER                                                 -->
  <!-- ================================================================== -->
  <listener>
    <listener-class>org.exoplatform.container.web.PortalContainerConfigOwner</listener-class>
  </listener>
...
</web-app>

30.3.2. Creating your PortalContainers from a WAR file

A ServletContextListener called org.exoplatform.container.web.PortalContainerCreator has been added in order to create the current portal containers that have been registered. We assume that all the web applications have already been loaded before calling PortalContainerCreator.contextInitialized.

Note

In JBoss Portal Platform, the PortalContainerCreator is already managed by the file starter.war/ear.

30.3.3. Defining a PortalContainer with its dependencies and its settings

Now we can define precisely a portal container and its dependencies and settings thanks to the PortalContainerDefinition that currently contains the name of the portal container, the name of the rest context, the name of the realm, the web application dependencies ordered by loading priority (i.e. the first dependency must be loaded at first and so on..) and the settings.
To be able to define a PortalContainerDefinition, we need to ensure first of all that a PortalContainerConfig has been defined at the RootContainer level, see an example below:
  <component>
    <!-- The full qualified name of the PortalContainerConfig -->
    <type>org.exoplatform.container.definition.PortalContainerConfig</type>
    <init-params>
      <!-- The name of the default portal container -->
      <value-param>
        <name>default.portal.container</name>
        <value>myPortal</value>
      </value-param>
      <!-- The name of the default rest ServletContext -->
      <value-param>
        <name>default.rest.context</name>
        <value>myRest</value>
      </value-param>
      <!-- The name of the default realm -->
      <value-param>
        <name>default.realm.name</name>
        <value>my-exo-domain</value>
      </value-param>
     <!-- Indicates whether the unregistered webapps have to be ignored -->
     <value-param>
        <name>ignore.unregistered.webapp</name>
        <value>true</value>
     </value-param>
      <!-- The default portal container definition -->
      <!-- It cans be used to avoid duplicating configuration -->
      <object-param>
        <name>default.portal.definition</name>
        <object type="org.exoplatform.container.definition.PortalContainerDefinition">
          <!-- All the dependencies of the portal container ordered by loading priority -->
          <field name="dependencies">
            <collection type="java.util.ArrayList">
              <value>
                <string>foo</string>
              </value>
              <value>
                <string>foo2</string>
              </value>
              <value>
                <string>foo3</string>
              </value>
            </collection>
          </field>        
          <!-- A map of settings tied to the default portal container -->
          <field name="settings">
            <map type="java.util.HashMap">
              <entry>
                <key>
                  <string>foo5</string>
                </key>
                <value>
                  <string>value</string>
                </value>
              </entry>
              <entry>
                <key>
                  <string>string</string>
                </key>
                <value>
                  <string>value0</string>
                </value>
              </entry>
              <entry>
                <key>
                  <string>int</string>
                </key>
                <value>
                  <int>100</int>
                </value>
              </entry>
            </map>
          </field>
          <!-- The path to the external properties file -->
          <field name="externalSettingsPath">
            <string>classpath:/org/exoplatform/container/definition/default-settings.properties</string>
          </field>
        </object>
      </object-param>
    </init-params>
  </component>

Table 30.1. Descriptions of the fields of PortalContainerConfig

default.portal.container (*) The name of the default portal container. This field is optional.
default.rest.context (*) The name of the default rest ServletContext. This field is optional.
default.realm.name (*) The name of the default realm. This field is optional.
ignore.unregistered.webapp (*) Indicates whether the unregistered webapps have to be ignored. If a webapp has not been registered as a dependency of any portal container, the application will use the value of this parameter to know what to do:
  • If it is set to false, this webapp will be considered by default as a dependency of all the portal containers.
  • If it is set to true, this webapp won't be considered by default as a dependency of any portal container, it will be simply ignored.
This field is optional and by default this parameter is set to false.
default.portal.definition The definition of the default portal container. This field is optional. The expected type is org.exoplatform.container. definition.PortalContainerDefinition that is described below. Allow the parameters defined in this default PortalContainerDefinition will be the default values.

Note

All the value of the parameters marked with a (*) can be defined thanks to System properties like any values in configuration files but also thanks to variables loaded by the PropertyConfigurator. For example in JBoss Portal Platform by default, it would be all the variables defined in the file configuration.properties.
A new PortalContainerDefinition can be defined at the RootContainer level thanks to an external plug-in, see an example below:
  <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>myPortal</string>
            </field>
            <!-- The name of the context name of the rest web application -->
            <field name="restContextName">
              <string>myRest</string>
            </field>
            <!-- The name of the realm -->
            <field name="realmName">
              <string>my-domain</string>
            </field>
            <!-- All the dependencies of the portal container ordered by loading priority -->
            <field name="dependencies">
              <collection type="java.util.ArrayList">
                <value>
                  <string>foo</string>
                </value>
                <value>
                  <string>foo2</string>
                </value>
                <value>
                  <string>foo3</string>
                </value>
              </collection>
            </field>
            <!-- A map of settings tied to the portal container -->
            <field name="settings">
              <map type="java.util.HashMap">
                <entry>
                  <key>
                    <string>foo</string>
                  </key>
                  <value>
                    <string>value</string>
                  </value>
                </entry>
                <entry>
                  <key>
                    <string>int</string>
                  </key>
                  <value>
                    <int>10</int>
                  </value>
                </entry>
                <entry>
                  <key>
                    <string>long</string>
                  </key>
                  <value>
                    <long>10</long>
                  </value>
                </entry>
                <entry>
                  <key>
                    <string>double</string>
                  </key>
                  <value>
                    <double>10</double>
                  </value>
                </entry>
                <entry>
                  <key>
                    <string>boolean</string>
                  </key>
                  <value>
                    <boolean>true</boolean>
                  </value>
                </entry>                                
              </map>
            </field>            
            <!-- The path to the external properties file -->
            <field name="externalSettingsPath">
              <string>classpath:/org/exoplatform/container/definition/settings.properties</string>
            </field>
          </object>
        </object-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>

Table 30.2. Descriptions of the fields of a PortalContainerDefinition when it is used to define a new portal container

name (*) The name of the portal container. This field is mandatory .
restContextName (*) The name of the context name of the rest web application. This field is optional. The default value will be defined at the PortalContainerConfig level.
realmName (*) The name of the realm. This field is optional. The default value will be defined at the PortalContainerConfig level.
dependencies All the dependencies of the portal container ordered by loading priority. This field is optional. The default value will be defined at the PortalContainerConfig level. The dependencies are in fact the list of the context names of the web applications from which the portal container depends. This field is optional. The dependency order is really crucial since it will be interpreted the same way by several components of the platform. All those components, will consider the 1st element in the list less important than the second element and so on. It is currently used to:
  • Know the loading order of all the dependencies.
  • If we have several PortalContainerConfigOwner
    • The ServletContext of all the PortalContainerConfigOwner will be unified, if we use the unified ServletContext (PortalContainer.getPortalContext()) to get a resource, it will try to get the resource in the ServletContext of the most important PortalContainerConfigOwner (i.e. last in the dependency list) and if it cans find it, it will try with the second most important PortalContainerConfigOwner and so on.
    • The ClassLoader of all the PortalContainerConfigOwner will be unified, if we use the unified ClassLoader (PortalContainer.getPortalClassLoader()) to get a resource, it will try to get the resource in the ClassLoader of the most important PortalContainerConfigOwner (i.e. last in the dependency list) and if it can find it, it will try with the second most important PortalContainerConfigOwner and so on.
settings A java.util.Map of internal parameters that we would like to tie the portal container. Those parameters could have any type of value. This field is optional. If some internal settings are defined at the PortalContainerConfig level, the two maps of settings will be merged. If a setting with the same name is defined in both maps, it will keep the value defined at the PortalContainerDefinition level.
externalSettingsPath The path of the external properties file to load as default settings to the portal container. This field is optional. If some external settings are defined at the PortalContainerConfig level, the two maps of settings will be merged. If a setting with the same name is defined in both maps, it will keep the value defined at the PortalContainerDefinition level. The external properties files can be either of type "properties" or of type "xml". The path will be interpreted as follows:
  1. The path doesn't contain any prefix of type "classpath:", "jar:" or "file:", we assume that the file could be externalized so we apply the following rules:
    1. A file exists at ${exo-conf-dir}/portal/${portalContainerName}/${externalSettingsPath}, we will load this file.
    2. No file exists at the previous path, we then assume that the path can be interpreted by the ConfigurationManager.
  2. The path contains a prefix, we then assume that the path can be interpreted by the ConfigurationManager.

Table 30.3. Descriptions of the fields of a PortalContainerDefinition when it is used to define the default portal container

name (*) The name of the portal container. This field is optional. The default portal name will be:
  1. If this field is not empty, then the default value will be the value of this field.
  2. If this field is empty and the value of the parameter default.portal.container is not empty, then the default value will be the value of the parameter.
  3. If this field and the parameter default.portal.container are both empty, the default value will be "portal".
restContextName (*) The name of the context name of the rest web application. This field is optional. The default value will be:
  1. If this field is not empty, then the default value will be the value of this field.
  2. If this field is empty and the value of the parameter default.rest.context is not empty, then the default value will be the value of the parameter.
  3. If this field and the parameter default.rest.context are both empty, the default value will be "rest".
realmName (*) The name of the realm. This field is optional. The default value will be:
  1. If this field is not empty, then the default value will be the value of this field.
  2. If this field is empty and the value of the parameter default.realm.name is not empty, then the default value will be the value of the parameter.
  3. If this field and the parameter default.realm.name are both empty, the default value will be "exo-domain".
dependencies All the dependencies of the portal container ordered by loading priority. This field is optional. If this field has a non empty value, it will be the default list of dependencies.
settings A java.util.Map of internal parameters that we would like to tie the default portal container. Those parameters could have any type of value. This field is optional.
externalSettingsPath The path of the external properties file to load as default settings to the default portal container. This field is optional. The external properties files can be either of type "properties" or of type "xml". The path will be interpreted as follows:
  1. The path doesn't contain any prefix of type "classpath:", "jar:" or "file:", we assume that the file could be externalized so we apply the following rules:
    1. A file exists at ${exo-conf-dir}/portal/${externalSettingsPath}, we will load this file.
    2. No file exists at the previous path, we then assume that the path can be interpreted by the ConfigurationManager.
  2. The path contains a prefix, we then assume that the path can be interpreted by the ConfigurationManager.

Note

All the value of the parameters marked with a (*) can be defined thanks to System properties like any values in configuration files but also thanks to variables loaded by the PropertyConfigurator. For example in JBoss Portal Platform by default, it would be all the variables defined in the file configuration.properties.
Internal and external settings are both optional, but if we give a non empty value for both the application will merge the settings. If the same setting name exists in both settings, we apply the following rules:
  1. The value of the external setting is null, we ignore the value.
  2. The value of the external setting is not null and the value of the internal setting is null, the final value will be the external setting value that is of type String.
  3. Both values are not null, we will have to convert the external setting value into the target type which is the type of the internal setting value, thanks to the static method valueOf(String), the following sub-rules are then applied:
    1. The method cannot be found, the final value will be the external setting value that is of type String.
    2. The method can be found and the external setting value is an empty String, we ignore the external setting value.
    3. The method can be found and the external setting value is not an empty String but the method call fails, we ignore the external setting value.
    4. The method can be found and the external setting value is not an empty String and the method call succeeds, the final value will be the external setting value that is of type of the internal setting value.

30.3.4. PortalContainer settings

We can inject the value of the portal container settings into the portal container configuration files thanks to the variables which name start with "portal.container.", so to get the value of a setting called "foo", just use the following syntax ${portal.container.foo}. You can also use internal variables, such as:

Table 30.4. Definition of the internal variables

portal.container.name Gives the name of the current portal container.
portal.container.rest Gives the context name of the rest web application of the current portal container.
portal.container.realm Gives the realm name of the current portal container.

You can find below an example of how to use the variables:
<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"
  xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd">
  <component>
    <type>org.exoplatform.container.TestPortalContainer$MyComponent</type>
    <init-params>
      <!-- The name of the portal container -->
      <value-param>
        <name>portal</name>
        <value>${portal.container.name}</value>
      </value-param>
      <!-- The name of the rest ServletContext -->
      <value-param>
        <name>rest</name>
        <value>${portal.container.rest}</value>
      </value-param>
      <!-- The name of the realm -->
      <value-param>
        <name>realm</name>
        <value>${portal.container.realm}</value>
      </value-param>
      <value-param>
        <name>foo</name>
        <value>${portal.container.foo}</value>
      </value-param>
      <value-param>
        <name>before foo after</name>
        <value>before ${portal.container.foo} after</value>
      </value-param>
    </init-params>
  </component>
</configuration>
In the properties file corresponding to the external settings, you can reuse variables previously defined (in the external settings or in the internal settings) to create a new variable. In this case, the prefix "portal.container." is not needed, see an example below:
my-var1=value 1
my-var2=value 2
complex-value=${my-var1}-${my-var2}
In the external and internal settings, you can also use create variables based on value of System parameters. The System parameters can either be defined at launch time or thanks to the PropertyConfigurator (see next section for more details). See an example below:
temp-dir=${java.io.tmpdir}${file.separator}my-temp
However, for the internal settings, you can use System parameters only to define settings of type java.lang.String.
It cans be also very useful to define a generic variable in the settings of the default portal container, the value of this variable will change according to the current portal container. See below an example:
my-generic-var=value of the portal container "${name}"
If this variable is defined at the default portal container level, the value of this variable for a portal container called "foo" will be value of the portal container "foo".

30.3.5. Adding dynamically settings and/or dependencies to a PortalContainer

It is possible to use component-plugin elements in order to dynamically change a PortalContainerDefinition. In the example below, we add the dependency foo to the default portal container and to the portal containers called foo1 and foo2:
<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>Change PortalContainer Definitions</name>
    <!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
    <set-method>registerChangePlugin</set-method>
    <!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
    <type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
    <init-params>
      <value-param>
        <name>apply.default</name>
        <value>true</value>
      </value-param>
      <values-param>
        <name>apply.specific</name>
        <value>foo1</value>
        <value>foo2</value>
      </values-param>  
      <object-param>
        <name>change</name>
        <object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependencies">
          <!-- The list of name of the dependencies to add -->
          <field name="dependencies">
            <collection type="java.util.ArrayList">
              <value>
                <string>foo</string>
              </value>
            </collection>
          </field>
        </object>
      </object-param>     
    </init-params>
  </component-plugin>
</external-component-plugins>

Table 30.5. Descriptions of the fields of a PortalContainerDefinitionChangePlugin

apply.all (*) Indicates whether the changes have to be applied to all the portal containers or not. The default value of this field is false. This field is a ValueParam and is not mandatory.
apply.default (*) Indicates whether the changes have to be applied to the default portal container or not. The default value of this field is false. This field is a ValueParam and is not mandatory.
apply.specific (*) A set of specific portal container names to which we want to apply the changes. This field is a ValuesParam and is not mandatory.
Rest of the expected parameters The rest of the expected parameters are ObjectParam of type PortalContainerDefinitionChange. Those parameters are in fact the list of changes that we want to apply to one or several portal containers. If the list of changes is empty, the component plug-in will be ignored. The supported implementations of PortalContainerDefinitionChange are described later in this section.

Note

All the value of the parameters marked with a (*) can be defined thanks to System properties like any values in configuration files but also thanks to variables loaded by the PropertyConfigurator. For example in JBoss Portal Platform by default, it would be all the variables defined in the file configuration.properties.
To identify the portal containers to which the changes have to be applied, we use the following algorithm:
  1. The parameter apply.all has been set to true. The corresponding changes will be applied to all the portal containers. The other parameters will be ignored.
  2. The parameter apply.default has been set to true and the parameter apply.specific is null. The corresponding changes will be applied to the default portal container only.
  3. The parameter apply.default has been set to true and the parameter apply.specific is not null. The corresponding changes will be applied to the default portal container and the given list of specific portal containers.
  4. The parameter apply.default has been set to false or has not been set and the parameter apply.specific is null. The corresponding changes will be applied to the default portal container only.
  5. The parameter apply.default has been set to false or has not been set and the parameter apply.specific is not null. The corresponding changes will be applied to the given list of specific portal containers.

30.3.5.1. The existing implementations of PortalContainerDefinitionChange

The modifications that can be applied to a PortalContainerDefinition must be a class of type PortalContainerDefinitionChange. The product proposes out of the box some implementations that we describe in the next sub sections.
30.3.5.1.1.  AddDependencies
This modification adds a list of dependencies at the end of the list of dependencies defined into the PortalContainerDefinition. The full qualified name is org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependencies.

Table 30.6. Descriptions of the fields of an AddDependencies

dependencies A list of String corresponding to the list of name of the dependencies to add. If the value of this field is empty, the change will be ignored.

See an example below, that will add foo at the end of the dependency list of the default portal container:
<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>Change PortalContainer Definitions</name>
    <!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
    <set-method>registerChangePlugin</set-method>
    <!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
    <type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
    <init-params>
      <value-param>
        <name>apply.default</name>
        <value>true</value>
      </value-param>
      <object-param>
        <name>change</name>
        <object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependencies">
          <!-- The list of name of the dependencies to add -->
          <field name="dependencies">
            <collection type="java.util.ArrayList">
              <value>
                <string>foo</string>
              </value>
            </collection>
          </field>
        </object>
      </object-param>     
    </init-params>
  </component-plugin>
</external-component-plugins>
30.3.5.1.2.  AddDependenciesBefore
This modification adds a list of dependencies before a given target dependency defined into the list of dependencies of the PortalContainerDefinition. The full qualified name is org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependenciesBefore.

Table 30.7. Descriptions of the fields of an AddDependenciesBefore

dependencies A list of String corresponding to the list of name of the dependencies to add. If the value of this field is empty, the change will be ignored.
target The name of the dependency before which we would like to add the new dependencies. If this field is null or the target dependency cannot be found in the list of dependencies defined into the PortalContainerDefinition, the new dependencies will be added in first position to the list.

See an example below, that will add foo before foo2 in the dependency list of the default portal container:
<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>Change PortalContainer Definitions</name>
    <!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
    <set-method>registerChangePlugin</set-method>
    <!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
    <type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
    <init-params>
      <value-param>
        <name>apply.default</name>
        <value>true</value>
      </value-param>
      <object-param>
        <name>change</name>
        <object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependenciesBefore">
          <!-- The list of name of the dependencies to add -->
          <field name="dependencies">
            <collection type="java.util.ArrayList">
              <value>
                <string>foo</string>
              </value>
            </collection>
          </field>
          <!-- The name of the target dependency -->
          <field name="target">
            <string>foo2</string>
          </field>
        </object>
      </object-param>     
    </init-params>
  </component-plugin>
</external-component-plugins>
30.3.5.1.3.  AddDependenciesAfter
This modification adds a list of dependencies before a given target dependency defined into the list of dependencies of the PortalContainerDefinition. The full qualified name is org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependenciesAfter.

Table 30.8. Descriptions of the fields of an AddDependenciesAfter

dependencies A list of String corresponding to the list of name of the dependencies to add. If the value of this field is empty, the change will be ignored.
target The name of the dependency after which we would like to add the new dependencies. If this field is null or the target dependency cannot be found in the list of dependencies defined into the PortalContainerDefinition, the new dependencies will be added in last position to the list.

See an example below, that will add foo after foo2 in the dependency list of the default portal container:
<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>Change PortalContainer Definitions</name>
    <!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
    <set-method>registerChangePlugin</set-method>
    <!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
    <type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
    <init-params>
      <value-param>
        <name>apply.default</name>
        <value>true</value>
      </value-param>
      <object-param>
        <name>change</name>
        <object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependenciesAfter">
          <!-- The list of name of the dependencies to add -->
          <field name="dependencies">
            <collection type="java.util.ArrayList">
              <value>
                <string>foo</string>
              </value>
            </collection>
          </field>
          <!-- The name of the target dependency -->
          <field name="target">
            <string>foo2</string>
          </field>
        </object>
      </object-param>     
    </init-params>
  </component-plugin>
</external-component-plugins>
30.3.5.1.4.  AddSettings
This modification adds new settings to a PortalContainerDefinition. The full qualified name is org.exoplatform.container.definition.PortalContainerDefinitionChange$AddSettings.

Table 30.9. Descriptions of the fields of an AddSettings

settings A map of <String, Object> corresponding to the settings to add. If the value of this field is empty, the change will be ignored.

See an example below, that will add the settings string and stringX to the settings of the default portal container:
<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>Change PortalContainer Definitions</name>
    <!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
    <set-method>registerChangePlugin</set-method>
    <!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
    <type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
    <init-params>
      <value-param>
        <name>apply.default</name>
        <value>true</value>
      </value-param>
      <object-param>
        <name>change</name>
        <object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddSettings">
          <!-- The settings to add to the to the portal containers -->
          <field name="settings">
            <map type="java.util.HashMap">
              <entry>
                <key>
                  <string>string</string>
                </key>
                <value>
                  <string>value1</string>
                </value>
              </entry>
              <entry>
                <key>
                  <string>stringX</string>
                </key>
                <value>
                  <string>value1</string>
                </value>
              </entry>
            </map>
          </field>
        </object>
      </object-param>     
    </init-params>
  </component-plugin>
</external-component-plugins>

30.3.6. Disable dynamically a portal container

It is possible to use component-plugin elements in order to dynamically disable one or several portal containers. In the example below, we disable the portal container named foo:
<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>Disable a PortalContainer</name>
    <!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
    <set-method>registerDisablePlugin</set-method>
    <!-- The full qualified name of the PortalContainerDefinitionDisablePlugin -->
    <type>org.exoplatform.container.definition.PortalContainerDefinitionDisablePlugin</type>
    <init-params>
      <!-- The list of the name of the portal containers to disable -->
      <values-param>
        <name>names</name>
        <value>foo</value>
      </values-param>
    </init-params>
  </component-plugin>
</external-component-plugins>

Table 30.10. Descriptions of the fields of a PortalContainerDefinitionDisablePlugin

names (*) The list of the name of the portal containers to disable.

Note

All the value of the parameters marked with a (*) can be defined thanks to System properties like any values in configuration files but also thanks to variables loaded by the PropertyConfigurator. For example in JBoss Portal Platform by default, it would be all the variables defined in the file configuration.properties.
To prevent any accesses to a web application corresponding to PortalContainer that has been disabled, you need to make sure that the following Http Filter (or a sub class of it) has been added to your web.xml in first position as below:
<filter>
  <filter-name>PortalContainerFilter</filter-name>
  <filter-class>org.exoplatform.container.web.PortalContainerFilter</filter-class>
</filter>  

<filter-mapping>
  <filter-name>PortalContainerFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Note

It is only possible to disable a portal container when at least one PortalContainerDefinition has been registered.

30.4. Runtime configuration profiles

The kernel configuration is able to handle configuration profiles at runtime (as opposed to packaging time).

30.4.1. Profiles activation

An active profile list is obtained during the boot of the root container and is composed of the system property exo.profiles sliced according the "," delimiter and also a server specific profile value (tomcat for tomcat, jboss for jboss, etc...).
# runs JBoss Portal Platform on Tomcat with the profiles tomcat and foo
sh JBoss Portal Platform.sh -Dexo.profiles=foo

# runs JBoss Portal Platform on JBoss with the profiles jboss, foo and bar
sh run.sh -Dexo.profiles=foo,bar

30.4.2. Profiles configuration

Profiles are configured in the configuration files of the eXo kernel.

30.4.2.1. Profiles definition

Profile activation occurs at XML to configuration object unmarshalling time. It is based on an "profile" attribute that is present on some of the XML element of the configuration files. To enable this, the kernel configuration schema has been upgraded to kernel_1_2.xsd. The configuration is based on the following rules:
  1. Any kernel element with the no profiles attribute will create a configuration object
  2. Any kernel element having a profiles attribute containing at least one of the active profiles will create a configuration object
  3. Any kernel element having a profiles attribute matching none of the active profile will not create a configuration object
  4. Resolution of duplicates (such as two components with same type) is left up to the kernel

30.4.2.2. Profiles capable configuration elements

A <configuration> element is profiles-capable when it carries a <profiles> element.
30.4.2.2.1. <component> element
The <component> element declares a component when activated. It will shadow any element with the same key declared before in the same configuration file:
<component>
  <key>Component</key>
  <type>Component</type>
</component>

<component profiles="foo">
  <key>Component</key>
  <type>FooComponent</type>
</component>
30.4.2.2.2. <component-plugin> element
The <component-plugin> element is used to dynamically extend the configuration of a given component. Thanks to the profiles the <component-plugin> elements can be enabled or disabled:
<external-component-plugins>
  <target-component>Component</target-component>
  <component-plugin profiles="foo">
    <name>foo</name>
    <set-method>addPlugin</set-method>
    <type>type</type>
    <init-params>
      <value-param>
        <name>param</name>
        <value>empty</value>
      </value-param>
    </init-params>
  </component-plugin>
</external-component-plugins>
30.4.2.2.3. <import> element
The <import> element imports a referenced configuration file when activated:
<import>empty</import>
<import profiles="foo">foo</import>
<import profiles="bar">bar</import>
30.4.2.2.4. <init-params> element
The <init-params> element configures the parameter argument of the construction of a component service:
<component>
  <key>Component</key>
  <type>ComponentImpl</type>
  <init-params>
    <value-param>
      <name>param</name>
      <value>empty</value>
    </value-param>
    <value-param profiles="foo">
      <name>param</name>
      <value>foo</value>
    </value-param>
    <value-param profiles="bar">
      <name>param</name>
      <value>bar</value>
    </value-param>
  </init-params>
</component>
30.4.2.2.5. <value-collection> element
The <value-collection> element configures one of the value of collection data:
<object type="org.exoplatform.container.configuration.ConfigParam">
  <field name="role">
    <collection type="java.util.ArrayList">
      <value><string>manager</string></value>
      <value profiles="foo"><string>foo_manager</string></value>
      <value profiles="foo,bar"><string>foo_bar_manager</string></value>
    </collection>
  </field>
</object>
30.4.2.2.6. <field-configuration> element
The <field-configuration> element configures the field of an object:
<object-param>
  <name>test.configuration</name>
  <object type="org.exoplatform.container.configuration.ConfigParam">
    <field name="role">
      <collection type="java.util.ArrayList">
        <value><string>manager</string></value>
      </collection>
    </field>
    <field name="role" profiles="foo,bar">
      <collection type="java.util.ArrayList">
        <value><string>foo_bar_manager</string></value>
      </collection>
    </field>
    <field name="role" profiles="foo">
      <collection type="java.util.ArrayList">
        <value><string>foo_manager</string></value>
      </collection>
    </field>
  </object>
</object-param>

30.5. Component request life cycle

30.5.1. Component request life cycle contract

The component request life cycle is an interface that defines a contract for a component for being involved into a request:
public interface ComponentRequestLifecycle
{
   /**
    * Start a request.
    * @param container the related container
    */
   void startRequest(ExoContainer container);
 
   /**
    * Ends a request.
    * @param container the related container
    */
   void endRequest(ExoContainer container);
}
The container passed is the container to which the component is related. This contract is often used to setup a thread local based context that will be demarcated by a request.
For instance in the portal context, a component request life cycle is triggered for user requests. Another example is the initial data import in GateIn that demarcates using callbacks made to that interface.

30.5.2. Request life cycle

The RequestLifeCycle class has several statics methods that are used to schedule the component request life cycle of components. Its main responsibility is to perform scheduling while respecting the constraint to execute the request life cycle of a component only once even if it can be scheduled several times.

30.5.2.1. Scheduling a component request life cycle

RequestLifeCycle.begin(component);
try
{
   // Do something
}
finally
{
   RequestLifeCycle.end();
}

30.5.2.2. Scheduling a container request life cycle

Scheduling a container triggers the component request life cycle of all the components that implement the interface ComponentRequestLifeCycle. If one of the component has already been scheduled before and then that component will not be scheduled again. When the local value is true, then the looked components will be those of the container, when it is false then the scheduler will also look at the components in the ancestor containers.
RequestLifeCycle.begin(container, local);
try
{
   // Do something
}
finally
{
   RequestLifeCycle.end();
}

30.5.2.3. When request life cycle is triggered

30.5.2.3.1. Portal request life cycle
Each portal request triggers the life cycle of the associated portal container.
30.5.2.3.2. JMX request Life Cycle
When a JMX bean is invoked, the request life cycle of the container to which it belongs it scheduled. Indeed JMX is an entry point of the system that may need component to have a request life cycle triggered.

30.6. Configuring Services

The eXo Kernel uses dependency injection to create services based on configuration.xml configuration files. The location of the configuration files determines if services are placed into the RootContainer scope, or into the PortalContainer scope.
When creating a service, you also should declare its existence to the Container. This can be done by creating a simple configuration file.
Copy the following code to a configuration.xml file and save this file in a /conf subdirectory of your service base folder. The container looks for a /conf/configuration.xml file in each jar-file.
All configuration.xml files located at conf/configuration.xml in the classpath (any directory, or any jar in the classpath) will have their services configured in the RootContainer scope.
All configuration.xml files located at conf/portal/configuration.xml in the classpath will have their services configured at the PortalContainer scope.
Additionally, portal extensions can use configuration information stored in JPP_DIST/gatein/gatein.ear/portal.war/WEB-INF/conf/configuration.xml, and will also have their services configured in the PortalContainer scope.
When eXo kernel reads a configuration, it loads the file from the kernel jar using the classloader and does not use an internet connection to resolve the file.

Note

Portal extensions are described later in this document.

30.6.1. Configuration syntax

30.6.1.1. Components

A service component is defined in configuration.xml by using a <component> element.
Only one piece of information is required when defining a service; the service implementation class. This is specified using <type>
Every component has a <key> that identifies it. If not explicitly set, a key defaults to the value of <type>. If a key can be loaded as a class, a class object is used as a key, otherwise a string is used.
The usual approach is to specify an interface as a key.
<?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_2.xsd
                          http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"
      xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd">
   <component>
      <key>org.exoplatform.services.database.HibernateService</key>
      <type>org.exoplatform.services.database.impl.HibernateServiceImpl</type>

      ...

   </component>
</configuration>
The configuration found inside the jar file is considered as the default configuration. If you want to override this default configuration you can do it in different places outside the jar. When the container finds several configurations for the same service, the configuration which is found later replaces completely the one found previously. Let's call this the configuration override mechanism.
All custom configuration is made in the webapps/portal/WEB-INF/conf/configuration.xml file. Use <component> elements to contain the configuration information. The <key> element defines the interface, and the <type> tag defines the implementation. While the <key> tag is not mandatory, it can lead to a small performance improvement.
<!-- Portlet container hooks -->
  <component>
    <key>org.exoplatform.services.portletcontainer.persistence.PortletPreferencesPersister</key>
    <type>org.exoplatform.services.portal.impl.PortletPreferencesPersisterImpl</type>
  </component>
Register plug-ins that can act as listeners or external plug-in to bundle some plug-in classes in other jar modules. The usual example is the hibernate service to which we can add hbm mapping files even if those are deployed in an other maven artifact.
<external-component-plugins>
  <target-component>org.exoplatform.services.database.HibernateService</target-component>
  <component-plugin> 
    <name>add.hibernate.mapping</name>
    <set-method>addPlugin</set-method>
    <type>org.exoplatform.services.database.impl.AddHibernateMappingPlugin</type>
    <init-params>
      <values-param>
        <name>hibernate.mapping</name>
        <value>org/exoplatform/services/portal/impl/PortalConfigData.hbm.xml</value>
        <value>org/exoplatform/services/portal/impl/PageData.hbm.xml</value>
        <value>org/exoplatform/services/portal/impl/NodeNavigationData.hbm.xml</value>
      </values-param>        
    </init-params>
  </component-plugin>
</external-component-plugins>
In that sample we target the HibernateService and we will call its addPlugin() method with an argument of the type AddHibernateMappingPlugin. That object will first have been filled with the init parameters.
Therefore, it is possible to define services that will be able to receive plug-ins without implementing any framework interface.
Another example of use is the case of listeners as in the following code where a listener is added to the OrganisationService and will be called each time a new user is created:
<external-component-plugins>
  <target-component>org.exoplatform.services.organization.OrganizationService</target-component>
  <component-plugin>
    <name>portal.new.user.event.listener</name>
    <set-method>addListenerPlugin</set-method>
    <type>org.exoplatform.services.portal.impl.PortalUserEventListenerImpl</type>
    <description>this listener create the portal configuration for the new user</description>
    <init-params>
      <object-param>
        <name>configuration</name>
        <description>description</description>
        <object type="org.exoplatform.services.portal.impl.NewPortalConfig">
          <field  name="predefinedUser">
            <collection type="java.util.HashSet">
              <value><string>admin</string></value>
              <value><string>exo</string></value>
              <value><string>company</string></value>
              <value><string>community</string></value>
              <value><string>portal</string></value>
              <value><string>exotest</string></value>
            </collection>
          </field>
          <field  name="templateUser"><string>template</string></field>
          <field  name="templateLocation"><string>war:/conf/users</string></field>
        </object>
      </object-param>
    </init-params>
  </component-plugin>
...
In the previous XML configuration, we refer the organization service and we will call its method addListenerPlugin with an object of type PortalUserEventListenerImpl. Each time a new user will be created (apart the predefined ones in the list above) methods of the PortalUserEventListenerImpl will be called by the service.
As you can see, there are several types of init parameters, from a simple value param which binds a key with a value to a more complex object mapping that fills a JavaBean with the info defined in the XML.
Many other examples exist such as for the Scheduler Service where you can add a job with a simple XML configuration or the JCR Service where you can add a NodeType from your own configuration.xml file.
30.6.1.1.1. RootContainer
As PortalContainer depends on the RootContainer, we will start by looking into this one.
The retrieval sequence in short:
  1. Services default RootContainer configurations from JAR files /conf/configuration.xml
  2. External RootContainer configuration, to be found at exo-tomcat/exo-conf/configuration.xml

Note

Naturally you always have to replace exo-tomcat by your own folder name.
The RootContainer creates a java HashTable which contains key-value pairs for the services. The qualified interface name of each service is used as key for the hashtable. Hopefully you still remember that the <key> tag of the configuration file contains the interface name? The value of each hashtable pair is an object that contains the service configuration (yes, this means the whole structure between the <component> tags of your configuration.xml file).
The RootContainer runs over all jar files you find in exo-tomcat/lib and looks if there is a configuration file at /conf/configuration.xml, the services configured in this file are added to the hashtable. That way - at the end of this process - the default configurations for all services are stored in the hashtable.

Note

What happens if the same service - recognized by the same qualified interface name - is configured in different jars? As the service only can exist one time the configuration of the jar found later overrides the previous configuration. You know that the loading order of the jars is unpredictable you must not depend on this.
If you wish to provide your own configurations for one or several services, you can do it in a general configuration file that has to be placed at exo-tomcat/exo-conf/configuration.xml. Do not search for such a file on your computer - you won't find one, because this option is not used in the default installation. Here again the same rule applies: The posterior configuration replaces the previous one.
The further configuration retrieval depends on the container type.
30.6.1.1.2. PortalContainer
The PortalContainer takes the hashtable filled by the RootContainer and continues to look in some more places. Here you get the opportunity to replace RootContainer configurations by those which are specific to your portal. Again, the configurations are overridden whenever necessary.
In short PortalContainer configurations are retrieved in the following lookup sequence :
  1. Take over the configurations of the RootContainer
  2. Default PortalContainer configurations from all JAR files (folder /conf/portal/configuration.xml)
  3. Web application configurations from the portal.war file - or the portal web app (folder /WEB-INF/conf/configuration.xml)
  4. External configuration for services of a named portal, it will be found at exo-tomcat/exo-conf/portal/$portal_name/configuration.xml (as of Portal 2.5)
You see, here the /conf/portal/configuration.xml file of each jar enters the game, they are searched at first. Next, there is nearly always a configuration.xml in the portal.war file (or in the portal webapp folder), you find this file at /WEB-INF/conf/configuration.xml. If you open it, you will find a lot of import statements that point to other configuration files in the same portal.war (or portal webapp).
Multiple Portals Be aware that you might set up several different portals ("admin", "mexico", etc.), and each of these portals will use a different PortalContainer. And each of these PortalContainers can be configured separately. As of JBoss Portal Platform 6.0 you also will be able to provide configurations from outside the jars and wars or webapps. Put a configuration file in exo-tomcat/exo-conf/portal/$portal_name/configuration.xml where $portal_name is the name of the portal you want to configure for . But normally you only have one portal which is called "portal" so you use exo-tomcat/exo-conf/portal/portal/configuration.xml.

Note

As of JBoss Portal Platform 6.0 you can override the external configuration location with the system property exo.conf.dir. If the property exists its value will be used as path to the eXo configuration directory, that means this is an alternative to exo-tomcat/exo-conf. Just put this property in the command line: java -Dexo.conf.dir=/path/to/exo/conf or use eXo.bat or eXo.sh. In this particular use case, you have no need to use any prefixes in your configuration file to import other files. For example, if your configuration file is exo-tomcat/exo-conf/portal/PORTAL_NAME/configuration.xml and you want to import the configuration file exo-tomcat/exo-conf/portal/PORTAL_NAME/mySubConfDir/myConfig.xml, you can do it by adding <import>mySubConfDir/myConfig.xml</import> to your configuration file.

Note

Under JBoss application server exo-conf will be looked up in directory described by JBoss System property jboss.server.config.url. If the property is not found or empty exo-jboss/exo-conf will be asked (since kernel 2.0.4).
30.6.1.1.3. External Plug-ins
The eXo Kernel supports non-component objects that can be configured, instantiated, and injected into registered components using method calls. This 'plugin' method allows portal extensions to add additional configurations to core services.
An external plug-in is defined by using the <external-component-plugin> wrapper element which contains one or more <component-plugin> definitions.
The <external-component-plugin> element 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 the target component to use for injection (<set-method>).
A plug-in implementation class has to implement the org.exoplatform.container.component. ComponentPlugin interface.
In the following example the PortalContainerDefinitionPlugin implements the 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_2.xsd
                          http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"
      xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.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>
The <target-component> defines the service for which the plug-in is defined. The configuration is injected by the container using a method that is defined in <set-method>. The method has exactly one argument of the type org.exoplatform.services.cms.categories.impl.TaxonomyPlugin:
  • addTaxonomyPlugin(org.exoplatform.services.cms.categories.impl.TaxonomyPlugin plugin)
The content of <init-params> corresponds to the structure of the TaxonomyPlugin object.

Note

You can configure the component CategoriesService using the addTaxonomyPlugin as often as you wish, you can also call addTaxonomyPlugin in different configuration files. The method addTaxonomyPlugin is then called several times, everything else depends on the implementation of the method.
30.6.1.1.4. Service instantiation
As you have already learned the services are all singletons, so that the container creates only one single instance of each container. The services are created by calling the constructors (called constructor injection). If there are only zero-arguments constructors (Foo public Foo(){}) there are no problems to be expected. That's easy.
This JDBC implementation of BaseOrganizationService interface has only one constructor:
public OrganizationServiceImpl(ListenerService listenerService, DatabaseService dbService);
You see this service depends on two other services. In order to be able to call this constructor the container first needs a ListenerService and a DatabaseService. Therefore these services must be instantiated before BaseOrganizationService, because BaseOrganizationService depends on them.
For this purpose the container first looks at the constructors of all services and creates a matrix of service dependencies in order to call the services in a proper order. If for any reason there are interdependencies or circular dependencies you will get a java Exception. In this way the dependencies are injected by the container.

Note

What happens if one service has more than one constructor? The container always tries first to use the constructor with a maximum of arguments, if this is not possible the container continues step by step with constructors that have less arguments until arriving at the zero-argument constructor (if there is one).
30.6.1.1.5. Service Access
As you want to follow the principle of Inversion of Control, you must not access the service directly. You need a Container to access the service.
With this command you get your current container:
  • ExoContainer myContainer = ExoContainerContext.getCurrentContainer();
Whenever you need one of the services that you have configured use the method:
  • myContainer.getComponentInstance(class)
In our case:
  • ArticleStatsService statsService = (ArticleStatsService) myContainer.getComponentInstance(ArticleStatsService.class);
Recapitulation:
package com.laverdad.common;

import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import com.laverdad.services.*;

public class Statistics {

  public int makeStatistics(String articleText) {
    ExoContainer myContainer = ExoContainerContext.getCurrentContainer();
    ArticleStatsService statsService = (ArticleStatsService)
        myContainer.getComponentInstance(ArticleStatsService.class);    
    int numberOfSentences = statsService.calcSentences(articleText);
    return numberOfSentences;
  }
  
  public static void main( String args[]) {
   Statistics stats = new Statistics();
   String newText = "This is a normal text. The method only counts the number of periods. "
   + "You can implement your own implementation with a more exact counting. "
   + "Let`s make a last sentence.";
  System.out.println("Number of sentences: " + stats.makeStatistics(newText));
  }
}
30.6.1.1.6. Includes, and special URLs
It is possible to divide the configuration.xml file into many smaller files, which are then included into the main configuration file.
The included files must be valid xml files; they cannot be fragments of text.
Below is an example configuration.xml that 'outsources' its content into several files:
<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd
                          http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"
      xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd">
   <!-- Comment #1 -->
   <import>war:/conf/sample-ext/jcr/jcr-configuration.xml</import>
   <import>war:/conf/sample-ext/portal/portal-configuration.xml</import>

</configuration>
Comment #1: This line is being used to reference another configuration file. The war: URL schema indicates that the following path is to be resolved relative to the current PortalContainer's servlet context resource path, starting with WEB-INF as a root.

Note

The current PortalContainer is really a newly created PortalContainer, as war: URLs only make sense for PortalContainer scoped configuration.
Through the extension mechanism the servlet context used for resource loading is a unified servlet context (this is explained in a later section).
To have an 'include' path resolved relative to current classpath (context classloader), use a 'jar:' URL schema.
30.6.1.1.7. Special variables
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; JNDI names, Database/DataSource names and JCR repository names, for example.
This variable is only defined when there is a current PortalContainer available and is only available for PortalContainer scoped services.
A good example of this is the 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_2.xsd http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"
               xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.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.cache.region.jbc2.query.localonly" value="true" />
        <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.jbc2.MultiplexedJBossCacheRegionFactory" />
        <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup" />
        <property name="hibernate.show_sql" value="false"/>
        <property name="hibernate.current_session_context_class" value="thread"/>
        <property name="hibernate.cache.use_second_level_cache" value="true"/>
        <property name="hibernate.cache.use_query_cache" value="true"/>
        <property name="hibernate.connection.datasource" value="${gatein.idm.datasource.name}${container.name.suffix}"/>
        <property name="hibernate.connection.autocommit" value="true"/>
        <!--
        	Should be automatically detected. Force otherwise 
        <property name="hibernate.dialect" value="org.hibernate.dialect.XXXDialect"/>
         -->
      </properties-param>
    </init-params>
  </component>
...
</configuration>

  

30.6.1.2. <init-params> configuration element

<init-params> is a configuration element that is essentially a map of key-value pairs, where key is always a String, and value can be any type that can be described using the kernel XML configuration.
Service components that form the JBoss Portal Platform infrastructure use <init-params> elements to configure themselves. A component can have one instance of <init-params> injected at most.
If the service component's constructor takes <init-params> as any of the parameters it will automatically be injected at component instantiation time.
The XML configuration for a service component that expects an <init-params> element must have an <init-params> element present, however this element can be left empty.
Below is an example of how the kernel XML configuration syntax looks when creating <init-params> instances:
<component>
 <key>org.exoplatform.services.naming.InitialContextInitializer</key>
 <type>org.exoplatform.commons.InitialContextInitializer2</type>
 <init-params>
   <properties-param>
     <name>default-properties</name>
     <description>Default initial context properties</description>
   </properties-param>
 </init-params>
</component>
An <init-params> element description begins with an <init-params> element.
It can have zero or more children elements, each of which is one of the following:
  • <value-param>
  • <values-param>
  • <properties-param>
  • <object-param>
Each of these child elements takes a <name> that serves as a map entry key, and an optional <description>. It also takes a type-specific value specification.
The value specification for the <properties-param> defines one or more <property> elements, each of which specifies two strings; a property name and a property value. This is evident in the two previous examples.
Each <properties-params> defines one java.util.Properties instance.

Example 30.1. <properties-param> Hibernate Example

  <component>
    <key>org.exoplatform.services.database.HibernateService</key>
    <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"/>
        <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/>
...
      </properties-param>
    </init-params>
  </component>
In the org.exoplatform.services.database.impl.HibernateServiceImpl you will find that the name "hibernate.properties" of the properties-param is used to access the properties.
package org.exoplatform.services.database.impl;

public class HibernateServiceImpl implements HibernateService, ComponentRequestLifecycle {
  public HibernateServiceImpl(InitParams initParams, CacheService cacheService) {
    PropertiesParam param = initParams.getPropertiesParam("hibernate.properties");
...
}

The value specification for <value-param> elements is a <value> element which defines a String instance.
<component>
  <key>org.exoplatform.services.resources.ResourceBundleService</key>
  <type>org.exoplatform.services.resources.impl.SimpleResourceBundleService</type>
    <init-params>
      <values-param>
        <name>classpath.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>init.resources</name>
        <description>Store the following resources into the db for  the first launch </description>
        <value>locale.test.resources.test</value>
      </values-param>

      <values-param>
        <name>portal.resource.names</name>
        <description>The properties files of  the portal ,  those file will be merged
          into one ResourceBundle properties </description>
        <value>local.portal.portal</value>
        <value>local.portal.custom</value>
      </values-param>
    </init-params>
</component>
The value specification for <values-param> requires one or more <value> elements. Each <value> represents one String instance. All String values are then collected into a java.util.List instance.
<component>
   <key>org.exoplatform.services.cache.CacheService</key>
   <jmx-name>cache:type=CacheService</jmx-name>
   <type>org.exoplatform.services.cache.impl.CacheServiceImpl</type>
   <init-params>
      <object-param>
         <name>cache.config.default</name>
         <description>The default cache configuration</description>
         <object type="org.exoplatform.services.cache.ExoCacheConfig">
            <field name="name">
               <string>default</string>
            </field>
            <field name="maxSize">
               <int>300</int>
            </field>
            <field name="liveTime">
               <long>300</long>
            </field>
            <field name="distributed">
               <boolean>false</boolean>
            </field>
            <field name="implementation">
               <string>org.exoplatform.services.cache.concurrent.ConcurrentFIFOExoCache</string>
            </field>
         </object>
      </object-param>
   </init-params>
</component>

Example 30.2. <value-param> Example

  <component>
    <key>org.exoplatform.portal.config.UserACL</key>
    <type>org.exoplatform.portal.config.UserACL</type>   
    <init-params>      
...
      <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> 
...
  </component>
The UserACL class accesses to the <value-param> in its constructor.
package org.exoplatform.portal.config;
public class UserACL {

  public UserACL(InitParams params) {
    UserACLMetaData md = new UserACLMetaData();
    ValueParam accessControlWorkspaceParam = params.getValueParam("access.control.workspace");
    if(accessControlWorkspaceParam != null) md.setAccessControlWorkspace(accessControlWorkspaceParam.getValue());
...

For <object-param> entries, the value specification consists of an <object> element which is used for plain Java style object specification (specifying an implementation class - <type>, and property values - <field>).

Example 30.3. <object-param> and LDAP Example

Let's have a look at the configuration of the LDAPService. It is not important to understand LDAP, we only discuss the parameters as they relate to <object-param>.
<component>
    <key>org.exoplatform.services.LDAP.LDAPService</key>
    <type>org.exoplatform.services.LDAP.impl.LDAPServiceImpl</type>
    <init-params>
      <object-param>
        <name>LDAP.config</name>
        <description>Default LDAP config</description>
        <object type="org.exoplatform.services.LDAP.impl.LDAPConnectionConfig">         
   <field  name="providerURL"><string>LDAPs://10.0.0.3:636</string></field>
   <field  name="rootdn"><string>CN=Administrator,CN=Users,DC=exoplatform,DC=org</string></field>
   <field  name="password"><string>exo</string></field>
   <field  name="version"><string>3</string></field>
     <field  name="minConnection"><int>5</int></field>
       <field  name="maxConnection"><int>10</int></field>    
       <field  name="referralMode"><string>ignore</string></field>
       <field  name="serverName"><string>active.directory</string></field>
       </object>
      </object-param>
    </init-params>
</component>
You see here an <object-param> element is being used to pass the parameters inside an object (actually a java bean). It consists of a name, a description and exactly one object. The object defines the type and a number of fields.
Here you see how the service accesses the object:
package org.exoplatform.services.LDAP.impl;

public class LDAPServiceImpl implements LDAPService {
...
  public LDAPServiceImpl(InitParams params) {
    LDAPConnectionConfig config = (LDAPConnectionConfig) params.getObjectParam("LDAP.config")
                                                               .getObject();
...
The passed object is LDAPConnectionConfig which is a classic java bean. It contains all fields and also the appropriate getters and setters (not listed here). You also can provide default values. The container creates a new instance of your bean and calls all setters whose values are configured in the configuration file.
package org.exoplatform.services.LDAP.impl;

public class LDAPConnectionConfig {
  private String providerURL        = "LDAP://127.0.0.1:389";
  private String rootdn;
  private String password;                                  
  private String version;                                   
  private String authenticationType = "simple";
  private String serverName         = "default";
  private int    minConnection;
  private int    maxConnection;
  private String referralMode       = "follow";
...
You see that the types (String, int) of the fields in the configuration correspond with the bean. A short glance in the kernel_1_2.xsd file let us discover more simple types:
  • string, int, long, boolean, date, double

The following section has an example of specifying a field of with a Collection type.
The <init-params> structure (the names and types of entries) is specific for each service, as it is the code inside a service components' class that defines which entry names to look up and what types it expects to find.
30.6.1.2.1. Collection
You also can use java collections to configure your service. In order to see an example, let's open the database-organization-configuration.xml file. This file defines a default user organization (users, groups, memberships/roles) of your portal. They use component-plugins which are explained later. You will see that object-param is used again.
There are two collections: The first collection is an ArrayList. This ArrayList contains only one value, but there could be more. The only value is an object which defines the field of the NewUserConfig$JoinGroup bean.
The second collection is a HashSet that is a set of strings.
    <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>/platform/users</string></field>
                    <field  name="membership"><string>member</string></field>
                  </object>
                </value>               
              </collection>
            </field>
            <field  name="ignoredUser">
              <collection type="java.util.HashSet">
                <value><string>root</string></value>
                <value><string>john</string></value>
                <value><string>marry</string></value>
                <value><string>demo</string></value>
                <value><string>james</string></value>
              </collection>
            </field>
          </object>
        </object-param>
      </init-params>
    </component-plugin>
Let's look at the org.exoplatform.services.organization.impl.NewUserConfig bean:
public class NewUserConfig {
  private List    role;
  private List    group;
  private HashSet ignoredUser;

  ...

  public void setIgnoredUser(String user) {
    ignoredUser.add(user);

  ...

  static public class JoinGroup {
    public String  groupId;
    public String  membership;
  ...
}
You see the values of the HashSet are set one by one by the container, and it's the responsibility of the bean to add these values to its HashSet.
The JoinGroup object is just an inner class and implements a bean of its own. It can be accessed like any other inner class using NewUserConfig.JoinGroup.

30.6.1.3. Component Plug-in Priority

Since kernel version 2.0.6 it is possible to setup order of loading for ComponentPlugin. Use the ' priority' tag to define plug-in's load priority. By default all plug-ins get priority '0'; they will be loaded in the container's natural way. If you want one plug-in to be loaded later than the others then just set priority for it higher than zero.
Simple example of fragment of a configuration.xml.
...
<component>
  <type>org.exoplatform.services.Component1</type>
</component>

<external-component-plugins>
  <target-component>org.exoplatform.services.Component1</target-component>

  <component-plugin>
    <name>Plugin1</name>
    <set-method>addPlugin</set-method>
    <type>org.exoplatform.services.plugins.Plugin1</type>
    <description>description</description>
    <priority>1</priority>
  </component-plugin>

  <component-plugin>
    <name>Plugin2</name>
    <set-method>addPlugin</set-method>
    <type>org.exoplatform.services.plugins.Plugin2</type>
    <description>description</description>
    <priority>2</priority>
  </component-plugin>

</external-component-plugins>

<external-component-plugins>
  <target-component>org.exoplatform.services.Component1</target-component>
  <component-plugin>
    <name>Plugin3</name>
    <set-method>addPlugin</set-method>
    <type>org.exoplatform.services.plugins.Plugin3</type>
    <description>description</description>
  </component-plugin>
</external-component-plugins>
...
In the above example plug-in 'Plugin3' will be loaded first because it has the default priority '0'. Then, plug-in 'Plugin1' will be loaded and last one is plug-in 'Plugin2'.

30.6.1.4. Configuration Logging

In case you need to solve problems with your service configuration, you have to know from which JAR/WAR causes your troubles. Add the JVM system property org.exoplatform.container.configuration.debug to your eXo.bat or eXo.sh file (exo-tomcat/bin/).
set EXO_CONFIG_OPTS="-Dorg.exoplatform.container.configuration.debug"
If this property is set the container configuration manager reports during startup the configuration retrieval process to the standard output (System.out).
......
Add configuration jar:file:/D:/Projects/eXo/dev/exo-working/exo-tomcat/lib/exo.kernel.container-trunk.jar!/conf/portal/configuration.xml
Add configuration jar:file:/D:/Projects/eXo/dev/exo-working/exo-tomcat/lib/exo.kernel.component.cache-trunk.jar!/conf/portal/configuration.xml
Add configuration jndi:/localhost/portal/WEB-INF/conf/configuration.xml import jndi:/localhost/portal/WEB-INF/conf/common/common-configuration.xml
import jndi:/localhost/portal/WEB-INF/conf/database/database-configuration.xml import jndi:/localhost/portal/WEB-INF/conf/ecm/jcr-component-plugins-configuration.xml
import jndi:/localhost/portal/WEB-INF/conf/jcr/jcr-configuration.xml
......

30.6.1.5. Import

The import tag allows to link to other configuration files. These imported files can be placed anywhere. If you write a default configuration which is part of your jar file you should not import files from outside your jar.
  • war: Imports from portal.war/WEB-INF
  • jar or classpath: Uses the classloader, you can use this prefix in the default configuration for importing an other configuration file which is accessible by the classloader.
  • file: Uses an absolute path, you also can put a URL.
  • without any prefix:
If you open the portal/trunk/web/portal/src/main/webapp/WEB-INF/conf/configuration.xml you will see that it consists only of imports:
<import>war:/conf/common/common-configuration.xml</import>
<import>war:/conf/common/logs-configuration.xml</import>
<import>war:/conf/database/database-configuration.xml</import>
<import>war:/conf/jcr/jcr-configuration.xml</import>
<import>war:/conf/common/portlet-container-configuration.xml</import>
...

30.6.1.6. System properties

Since kernel 2.0.7 and 2.1, it is possible to use system properties in literal values of component configuration meta data. This makes it possible to resolve properties at runtime instead of providing a value at packaging time.
In portal/trunk/web/portal/src/main/webapp/WEB-INF/conf/database/database-configuration.tmpl.xml you find an example for system properties:
  <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.connection.url" value="${connectionUrl}"/>
        <property name="hibernate.connection.driver_class" value="${driverClass}"/>
        <property name="hibernate.connection.username" value="${username}"/>
        <property name="hibernate.connection.password" value="${password}"/>
        <property name="hibernate.dialect" value="${dialect}"/>
...
      </properties-param>
    </init-params>
  </component>
As these are system properties you use the -D command: java -DconnectionUrl=jdbc:hsqldb:file:../temp/data/exodb -DdriverClass=org.hsqldb.jdbcDriver Or better use the parameters of eXo.bat / eXo.sh when you start JBoss Portal Platform: set EXO_OPTS="-DconnectionUrl=jdbc:hsqldb:file:../temp/data/exodb -DdriverClass=org.hsqldb.jdbcDriver"

30.7. Specific Services

30.7.1. ListenerService

30.7.1.1. Asynchronous Event Broadcast

Basically, ListenerService used to store Listeners and broadcast events to them.
ListenerService event broadcasting works in next way - it takes a destination listeners and executes event on those listeners.
But, some events may take a lot of time, so idea to make event processing asynchronous is useful.
What do I need to make my listener asynchronous?
- It's very simple, just mark your Listener implementation as @Asynchronous.
@Asynchronous
class AsynchListenerWithException<S,D> extends Listener<S,D>
{
   @Override
   public void onEvent(Event<S,D> event) throws Exception
   {
      // some expensive operation
   }
}
Now, our AsynchListener will be executed in separate thread by ExecutorService.
By default, ExecutoreService configured with thread pool size 1, you can change it in configuration:
   <component>
      <key>org.exoplatform.services.listener.ListenerService</key>
      <type>org.exoplatform.services.listener.ListenerService</type>
      
      <init-params>
         <value-param>
            <name>asynchPoolSize</name>
            <value>5</value>
         </value-param>
      </init-params>

   </component>

30.7.2. Understanding the ListenerService

30.7.2.1. Objectives

This article will first describe how the ListenerService works and then it will show you how to configure the ListenerService.

30.7.2.2. What is the ListenerService ?

Inside eXo, an event mechanism allows to trigger and listen to events under specific conditions. This mechanism is used in several places in eXo such as login/logout time.

30.7.2.3. How does it work?

Listeners must be subclasses of org.exoplatform.services.listener.Listener registered by the ListenerService.
30.7.2.3.1. Registering a listener
To register a listener, you need to call the addListener() method.
/**
 * This method is used to register a listener with the service. The method
 * should: 1. Check to see if there is a list of listener with the listener
 * name, create one if the listener list doesn't exit 2. Add the new listener
 * to the listener list
 * 
 * @param listener
*/
public void addListener(Listener listener) {
   ...
}
By convention, we use the listener name as the name of the event to listen to.
30.7.2.3.2. Triggering an event
To trigger an event, an application can call one of the broadcast() methods of ListenerService.
/**
 * This method is used to broadcast an event. This method should: 1. Check if
 * there is a list of listener that listen to the event name. 2. If there is a
 * list of listener, create the event object with the given name , source and
 * data 3. For each listener in the listener list, invoke the method
 * onEvent(Event)
 * 
 * @param <S> The type of the source that broadcast the event
 * @param <D> The type of the data that the source object is working on
 * @param name The name of the event
 * @param source The source object instance
 * @param data The data object instance
 * @throws Exception 
 */
public <S, D> void broadcast(String name, S source, D data) throws Exception {
   ...
}

/**
 * This method is used when a developer want to implement his own event object
 * and broadcast the event. The method should: 1. Check if there is a list of
 * listener that listen to the event name. 2. If there is a list of the
 * listener, For each listener in the listener list, invoke the method
 * onEvent(Event)
 * 
 * @param <T> The type of the event object, the type of the event object has
 *          to be extended from the Event type
 * @param event The event instance
 * @throws Exception
 */
public <T extends Event> void broadcast(T event) throws Exception {
   ...
}
The broadcast() methods retrieve the name of the event and find the registered listeners with the same name and call the method onEvent() on each listener found.
Each listener is a class that extends org.exoplatform.services.listener.Listener, as you can see below:
public abstract class Listener<S, D> extends BaseComponentPlugin {

   /**
    * This method should be invoked when an event with the same name is
    * broadcasted
    */
   public abstract void onEvent(Event<S, D> event) throws Exception;
}

Warning

As you can see we use generics to limit the source of the event to the type 'S' and the data of the event to the type 'D', so we expect that listeners implement the method onEvent() with the corresponding types
Each listener is also a ComponentPlugin with a name and a description, in other words, the name of the listener will be the name given in the configuration file, for more details see the next section.
public interface ComponentPlugin {
   public String getName();

   public void setName(String name);

   public String getDescription();

   public void setDescription(String description);
}

30.7.2.4. How to configure a listener?

All listeners are in fact a ComponentPlugin so it must be configured as below:
<?xml version="1.0" encoding="ISO-8859-1"?>
<configuration>
...
  <external-component-plugins>
    <!-- The full qualified name of the ListenerService --> 
    <target-component>org.exoplatform.services.listener.ListenerService</target-component>

    <component-plugin>
    <!-- The name of the listener that is also the name of the target event -->
      <name>${name-of-the-target-event}</name>
      <!-- The name of the method to call on the ListenerService in order to register the Listener -->
      <set-method>addListener</set-method>
      <!-- The full qualified name of the Listener -->
      <type>${the-FQN-of-the-listener}</type>
    </component-plugin>

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

30.7.2.5. Concrete Example

The org.exoplatform.services.security.ConversationRegistry uses the ListenerService to notify that a user has just signed in or just left the application. For example, when a new user signs in, the following code is called:
listenerService.broadcast("exo.core.security.ConversationRegistry.register", this, state);
This code will in fact create a new Event which name is "exo.core.security.ConversationRegistry.register", which source is the current instance of ConversationRegistry and which data is the given state. The ListenerService will call the method onEvent(Event<ConversationRegistry, ConversationState> event) on all the listeners which name is "exo.core.security.ConversationRegistry.register".
In the example below, we define a Listener that will listen the event "exo.core.security.ConversationRegistry.register".
<?xml version="1.0" encoding="ISO-8859-1"?>
<configuration>
...
  <external-component-plugins>
    <!-- The full qualified name of the ListenerService --> 
    <target-component>org.exoplatform.services.listener.ListenerService</target-component>

    <component-plugin>
      <!-- The name of the listener that is also the name of the target event -->
      <name>exo.core.security.ConversationRegistry.register</name>
      <!-- The name of the method to call on the ListenerService in order to register the Listener -->
      <set-method>addListener</set-method>
      <!-- The full qualified name of the Listener -->
      <type>org.exoplatform.forum.service.AuthenticationLoginListener</type>
    </component-plugin>

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

30.7.3. Job Schedule

30.7.3.1. What is Job Scheduler?

Job scheduler defines a job to execute a given number of times during a given period. It is a service that is in charge of unattended background executions, commonly known for historical reasons as batch processing. It is used to create and run jobs automatically and continuously, to schedule event-driven jobs and reports.

30.7.3.2. How does Job Scheduler work?

Jobs are scheduled to run when a given Trigger occurs. Triggers can be created with nearly any combination of the following directives:
  • at a certain time of day (to the millisecond)
  • on certain days of the week
  • on certain days of the month
  • on certain days of the year
  • not on certain days listed within a registered Calendar (such as business holidays)
  • repeated a specific number of times
  • repeated until a specific time/date
  • repeated indefinitely
  • repeated with a delay interval
Jobs are given names by their creator and can also be organized into named groups. Triggers may also be given names and placed into groups, in order to easily organize them within the scheduler. Jobs can be added to the scheduler once, but registered with multiple Triggers. Within a J2EE environment, Jobs can perform their work as part of a distributed (XA) transaction.
(Source: quartz-scheduler.org)
30.7.3.2.1. How can Job Scheduler Service be used in Kernel?
Kernel leverages Quartz for its scheduler service and wraps org.quartz.Scheduler in org.exoplatform.services.scheduler.impl.QuartzSheduler for easier service wiring and configuration like any other services. To work with Quartz in Kernel, you will mostly work with org.exoplatform.services.scheduler.JobSchedulerService (implemented by org.exoplatform.services.scheduler.impl.JobSchedulerServiceImpl.
To use JobSchedulerService, you can configure it as a component in the configuration.xml. Because JobSchedulerService requires QuartzSheduler and QueueTasks, you also have to configure these two components.
<?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_2.xsd http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"
  xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd">

  <component>
    <type>org.exoplatform.services.scheduler.impl.QuartzSheduler</type>
  </component>

  <component>
    <type>org.exoplatform.services.scheduler.QueueTasks</type>
  </component>

  <component>
    <key>org.exoplatform.services.scheduler.JobSchedulerService</key>
    <type>org.exoplatform.services.scheduler.impl.JobSchedulerServiceImpl</type>
  </component>

</configuration>
30.7.3.2.2. Samples

Note

You can download the project code from here
Work with JobSchedulerService by creating a sample project and use JBoss Portal Platform for testing.
Firstly, create a project by using maven archetype plug-in:
mvn archetype:generate
  • For project type: select maven-archetype-quickstart
  • For groupId: select org.exoplatform.samples
  • For artifactId: select exo.samples.scheduler
  • For version: select 1.0.0-SNAPSHOT
  • For package: select org.exoplatform.samples.scheduler
Edit the pom.xml as follows:
<project
  xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <parent>
    <artifactId>exo.portal.parent</artifactId>
    <groupId>org.exoplatform.portal</groupId>
    <version>3.1.0-GA</version>
  </parent>

  <groupId>org.exoplatform.samples</groupId>
  <artifactId>exo.samples.scheduler</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <name>eXo Samples For Scheduler</name>
  <description>eXo Samples Code For Scheduler</description>
</project>
Generate an eclipse project by using maven eclipse plug-in and then import into eclipse:
mvn eclipse:eclipse
eXo Kernel makes it easier to work with job scheduler service. All you need is just to define your "job" class to be performed by implementing org.quartz.Job interface and add configuration for it.
30.7.3.2.2.1. Define a job
To define a job, do as follows:
Define your job to be performed. For example, the job DumbJob is defined as follows:
package org.exoplatform.samples.scheduler.jobs;

import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

/**
 * DumbJob for executing a defined dumb job.
 */
public class DumbJob implements Job {

  /**
   * The logger
   */
  private static final Log LOG = ExoLogger.getLogger(DumbJob.class);

  /**
   * The job of the DumbJob will be done by executing this method.
   *
   * @param context
   * @throws JobExecutionException
   */
  public void execute(JobExecutionContext context) throws JobExecutionException {
    LOG.info("DumbJob is executing...");
  }
}
All jobs are required to implement the method execute from org.quartz.Job interface. This method will be called whenever a job is performed. With DumbJob, you just use logging to see that it will work. By looking at the terminal, you will see the log message: "DumbJob is executing..."
30.7.3.2.2.2. Job configuration
After defining the "job", the only next step is to configure it by using external-component-plugin configuration for org.exoplatform.services.scheduler.JobSchedulerService. You can use these methods below for setting component plug-in:
public void addPeriodJob(ComponentPlugin plugin) throws Exception;
The component plug-in for this method must be the type of org.exoplatform.services.scheduler.PeriodJob. This type of job is used to perform actions that are executed in a period of time. You have to define when this job is performed, when it ends, when it performs the first action, how many times it is executed and the period of time to perform the action. See the configuration sample below to understand more clearly:
<external-component-plugins>
   <target-component>org.exoplatform.services.scheduler.JobSchedulerService</target-component>
    <component-plugin>
      <name>PeriodJob Plugin</name>
      <set-method>addPeriodJob</set-method>
      <type>org.exoplatform.services.scheduler.PeriodJob</type>
      <description>period job configuration</description>
      <init-params>
        <properties-param>
          <name>job.info</name>
          <description>dumb job executed  periodically</description>
          <property name="jobName" value="DumbJob"/>
          <property name="groupName" value="DumbJobGroup"/>
          <property name="job" value="org.exoplatform.samples.scheduler.jobs.DumbJob"/>
          <property name="repeatCount" value="0"/>
          <property name="period" value="60000"/>
          <property name="startTime" value="+45"/>
          <property name="endTime" value=""/>
        </properties-param>
      </init-params>
    </component-plugin>
 </external-component-plugins>
public void addCronJob(ComponentPlugin plugin) throws Exception;
The component plug-in for this method must be the type of org.exoplatform.services.scheduler.CronJob. This type of job is used to perform actions at specified time with Unix 'cron-like' definitions. The plug-in uses "expression" field for specifying the 'cron-like' definitions to execute the job. This is considered as the most powerful and flexible job to define when it will execute. For example, at 12pm every day => "0 0 12 * * ?"; or at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday => "0 15 10 ? * MON-FRI". To see more about Cron expression, please refer to this article: CRON expression. See the configuration sample below to understand more clearly:
<external-component-plugins>
    <target-component>org.exoplatform.services.scheduler.JobSchedulerService</target-component>
    <component-plugin>
      <name>CronJob Plugin</name>
      <set-method>addCronJob</set-method>
      <type>org.exoplatform.services.scheduler.CronJob</type>
      <description>cron job configuration</description>
      <init-params>
        <properties-param>
          <name>job.info</name>
          <description>dumb job executed by cron expression</description>
          <property name="jobName" value="DumbJob"/>
          <property name="groupName" value="DumbJobGroup"/>
          <property name="job" value="org.exoplatform.samples.scheduler.jobs.DumbJob"/>
          <!-- The job will be performed at 10:15am every day -->
          <property name="expression" value="0 15 10 * * ?"/> 
        </properties-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>
public void addGlobalJobListener(ComponentPlugin plugin) throws Exception;
public void addJobListener(ComponentPlugin plugin) throws Exception;
The component plug-in for two methods above must be the type of org.quartz.JobListener. This job listener is used so that it will be informed when a org.quartz.JobDetail executes.
public void addGlobalTriggerListener(ComponentPlugin plugin) throws Exception;
public void addTriggerListener(ComponentPlugin plugin) throws Exception;
The component plug-in for two methods above must be the type of org.quartz.TriggerListener. This trigger listener is used so that it will be informed when a org.quartz.Trigger fires.
30.7.3.2.2.3. Run the project
Create conf.portal package in your sample project. Add the configuration.xml file with the content as follows:
<?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_2.xsd http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"
  xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd">

  <component>
    <type>org.exoplatform.services.scheduler.impl.QuartzSheduler</type>
  </component>
  <component>
    <type>org.exoplatform.services.scheduler.QueueTasks</type>
  </component>
  <component>
    <key>org.exoplatform.services.scheduler.JobSchedulerService</key>
    <type>org.exoplatform.services.scheduler.impl.JobSchedulerServiceImpl</type>
  </component>

  <external-component-plugins>
    <target-component>org.exoplatform.services.scheduler.JobSchedulerService</target-component>
    <component-plugin>
      <name>PeriodJob Plugin</name>
      <set-method>addPeriodJob</set-method>
      <type>org.exoplatform.services.scheduler.PeriodJob</type>
      <description>period job configuration</description>
      <init-params>
        <properties-param>
          <name>job.info</name>
          <description>dumb job executed periodically</description>
          <property name="jobName" value="DumbJob"/>
          <property name="groupName" value="DumbJobGroup"/>
          <property name="job" value="org.exoplatform.samples.scheduler.jobs.DumbJob"/>
          <property name="repeatCount" value="0"/>
          <property name="period" value="60000"/>
          <property name="startTime" value="+45"/>
          <property name="endTime" value=""/>
        </properties-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>
</configuration>
mvn clean install the project. Copy .jar file to lib in tomcat bundled with JBoss Portal Platform 6. Run bin/gatein.sh to see the DumbJob to be executed on the terminal when portal containers are initialized. Please look at the terminal to see the log message of DumbJob.
From now on, you can easily create any job to be executed in JBoss Portal Platform's portal by defining your job and configuring it.

30.7.4. The data source provider

30.7.4.1. Description

The DataSourceProvider is a service used to give access to a data source in an uniform manner in order to be able to support data sources that are managed by the application server.

Table 30.11. List methods

getDataSource(String dataSourceName) Tries to get the data source from a JNDI lookup. If it can be found and the data source is defined as managed, the service will wrap the original DataSource instance in a new DataSource instance that is aware of its managed state otherwise it will return the original DataSource instance.
isManaged(String dataSourceName) Indicates whether or not the given data source is managed.

30.7.4.2. Configuration

The configuration of the DataSourceProvider should be defined only if you use managed data sources since by default all the data sources are considered as not managed. See below the default configuration
<configuration>
....  
   <component>
      <key>org.exoplatform.services.jdbc.DataSourceProvider</key>
      <type>org.exoplatform.services.jdbc.impl.DataSourceProviderImpl</type>
      <init-params>
         <!--  Indicates that the data source needs to check if a tx is active
              to decide if the provided connection needs to be managed or not.
              If it is set to false, the data source will provide only
              managed connections if the data source itself is managed.  -->
         <!--value-param>
            <name>check-tx-active</name>
            <value>true</value>
         </value-param-->
         <!-- Indicates that all the data sources are managed 
              If set to true the parameter never-managed and 
              managed-data-sources will be ignored -->
         <!--value-param>
            <name>always-managed</name>
            <value>true</value>
         </value-param-->
         <!-- Indicates the list of all the data sources that are 
              managed, each value tag can contain a list of
              data source names separated by a comma, in the
              example below we will register ds-foo1, ds-foo2 
              and ds-foo3 as managed data source. If always-managed
              and/or never-managed is set true this parameter is ignored -->
         <!--values-param>
            <name>managed-data-sources</name>
            <value>ds-foo1, ds-foo2</value>
            <value>ds-foo3</value>
         </values-param-->
      </init-params>
   </component>  
...
</configuration>

Table 30.12. Fields description

check-tx-active This parameter indicates that the data source needs to check if a transaction is active to decide if the provided connection needs to be managed or not. If it is set to false, the data source will provide only managed connections if the data source itself is managed. By default, this parameter is set to true. If this parameter is set to true, it will need the TransactionService to work properly, so please ensure that the TransactionService is defined in your configuration.
always-managed This parameter indicates that all the data sources are managed. If set to true the parameter never-managed and managed-data-sources will be ignored, so it will consider all the data sources as managed. By default, this parameter is set to false.
managed-data-sources This parameter indicates the list of all the data sources that are managed, each value tag can contain a list of data source names separated by a comma. If always-managed and/or never-managed is set true this parameter is ignored.

30.8. Configuring a portal container

A portal container is defined by several attributes:
Portal Container Name
This attribute is always equal to the URL context to which the current portal is bound.
REST Context Name
This attribute is used for REST access to portal application; every portal has one unique REST context name.
Realm Name
This is the name of the security realm used for authentication when users log into the portal.
Dependencies
This is a list of other web applications whose resources are visible to the current portal (via the extension mechanism described later), and are searched for 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_2.xsd
                       http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"
   xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.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>
Dependencies are part of the extension mechanism which is discussed later in this document.
Every PortalContainer is represented by a PortalContainer instance, which contains:
eXoContainerContext
This contains information about the portal.
Unified Servlet Context
This deals with web-archive-relative resource loading.
Unified Classloader
For classpath based resource loading.
Various methods for retrieving services
The Unified servlet context and unified classloader are part of the extension mechanism (which is detailed in the next section).
They provide the standard API (ServletContext, ClassLoader) with specific resource loading behavior, such as visibility into associated web application archives, configured with the dependencies property of PortalContainerDefinition.
Resources from other web applications are queried in the order specified by the dependencies. The later entries in the list override the previous ones.

30.9. System property configuration

A new property configuration service has been developed for taking care of configuring system properties from the inline kernel configuration or from specified property files.
The service is scoped at the root container level because it is used by all the services in the different portal containers in the application runtime.

30.9.1. Properties <init-param>

The properties init param takes a property declared to configure various properties.
<component>
  <key>PropertyManagerConfigurator</key>
  <type>org.exoplatform.container.PropertyConfigurator</type>
  <init-params>
    <properties-param>
      <name>properties</name>
      <property name="foo" value="bar"/>
    </properties-param>
  </init-params>
</component>

30.9.2. Properties URL <init-param>

The properties URL init param allow to load an external file by specifying its URL. Both property and XML format are supported, see the javadoc of the java.util.Properties class for more information. When a property file is loaded the various property declarations are loaded in the order in which the properties are declared sequentially in the file.
<component>
  <key>PropertyManagerConfigurator</key>
  <type>org.exoplatform.container.PropertyConfigurator</type>
  <init-params>
    <value-param>
      <name>properties.url</name>
      <value>classpath:configuration.properties</value>
    </value-param>
  </init-params>
</component>
In the properties file corresponding to the external properties, you can reuse variables before defining to create a new variable. In this case, the prefix "portal.container." is not needed, see an example below:
my-var1=value 1
my-var2=value 2
complex-value=${my-var1}-${my-var2}

30.9.3. System Property configuration of the properties URL

It is possible to replace the properties URL init param by a system property that overwrites it. The name of that property is exo.properties.url.

30.9.4. Variable Syntaxes

All the variables that we described in the previous sections can be defined thanks to 2 possible syntaxes which are ${variable-name} or ${variable-name:default-value}. The first syntax doesn't define any default value so if the variable has not be set the value will be ${variable-name} to indicate that it could not be resolved. The second syntax allows you to define the default value after the semi colon so if the variable has not be set the value will be the given default value.

30.10. The Extension Mechanism and Portal Extensions

The Extension mechanism makes it possible to override portal resources in a way similar to hardware plug-and-play functionalities.
Customizations can be implemented without unpacking and repacking the original portal .war archives by adding a .war archive to the resources and configuring its position in the portal's classpath. Custom .war archives can be created with new resources that override the resources in the original archive.
These archives, packaged for use through the extension mechanism, are called portal extensions.

Procedure 30.1. Creating a portal extension

  1. Declare the PortalConfigOwner servlet context listener in the web.xml of your web application.
    This example shows a portal extension called sample-ext.
    <?xml version="1.0" encoding="ISO-8859-1" ?>
    <!DOCTYPE web-app PUBLIC -//Sun Microsystems, Inc.//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>
    
  2. Add the application's servlet context name to the PortalContainerDefinition's list of dependencies. This must be done for each portal container that you want to have access to the new application.
    The application's position in these lists will dictate its priority when the portal loads resources. The later your application appears in the list, the higher its resource priority will be.
  3. At this point your new web archive will be on both the portal's unified classpath and unified servlet context resource path.

Example

Refer to the code extract in Section 30.8, “Configuring a portal container” for an example of a PortalContainerDefinition that has sample-ext in its list of dependencies.

30.10.1. Running Multiple Portals

It is possible to run several independent portal containers, each bound to a different URL context, within the same JVM instance.
This method of deployment allows for efficient administration and resource consumption by allowing coexisting portals to reuse configuration arrangements through the extension mechanism.
Portals can inherit resources and configuration from existing web archives and add extra resources as needed, overriding those that need to be changed by including modified copies.
In order for a portal application to function correctly when deployed within a multiple portal deployment, it 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 life-cycle event processing, an application can retrieve this information through org.exoplatform.container.eXoContainerContext.
Sometimes an application must ensure that the proper PortalContainer is associated with the current eXoContainerContext call.
If the portal application contains servlets or servlet filters that need to access portal specific resources during their request processing, the servlet or filter must be associated with the current container.
A servlet in this instance should extend the org.exoplatform.container.web.AbstractHttpServlet class so as to properly initialize the current PortalContainer.
This will also set the current thread's context Classloader to one that looks for resources in associated web applications in the order specified by the dependencies configuration (as seen in Section 30.10, “The Extension Mechanism and Portal Extensions”).
Filter classes need to extend the org.exoplatform.container.web.AbstractFilter.
Both AbstractHttpServlet, and AbstractFilter have a getContainer() method, which returns the current PortalContainer.
If your servlet handles the requests by implementing a service() method, that method must be renamed to match the following signature:
/**
 * Use this method instead of Servlet.service()
 */
protected void onService(ExoContainer container, HttpServletRequest req,
      HttpServletResponse res) throws ServletException, IOException;

Note

This ensures that AbstractHttpServlet's service() interception is not overwritten.
An application may also need access to portal information within the HttpSessionListener. Ensure the abstract class org.exoplatform.container.web.AbstractHttpSessionListener is extended.
In this instance, modify the method signatures as follows:
/**
 * Use this method instead of HttpSessionListener.sessionCreated()
 */
protected void onSessionCreated(ExoContainer container, HttpSessionEvent event);

/**
 * Use this method instead of HttpSessionListener.sessionDestroyed()
 */
protected void onSessionDestroyed(ExoContainer container, HttpSessionEvent event);
Another method must also be implemented 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 the current thread's context Classloader is set up according to the 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 both AbstractHttpServlet and AbstractFilter. This is a default implementation that automatically returns true when it detects there is a current PortalContainer present and false otherwise.
ServletContextListener-based initialization access to PortalContainer
JBoss Portal Platform has no direct control over the deployment of application archives (.war and .ear files); it is the application server that performs the deployment.
However, applications in the dependencies configuration must be deployed before the portal that depends on them is initialized in order for the extension mechanism to work properly.
Conversely, some applications may require an already initialized PortalContainer to properly initialize themselves. This gives rise to a recursive dependency problem.
A mechanism of initialization tasks and task queues has been implemented in JBoss Portal Platform to resolve this dependency issue.
Web applications that depend on a current PortalContainer to initialize must avoid performing their initialization directly on a ServletContextListener executed during their deployment (before any PortalContainer was initialized).
To ensure this, a web application should package its initialization logic into an init task of an appropriate type and only use ServletContextListener to insert the init task instance into the proper init tasks queue.
An example of this is the Gadgets application which registers Google gadgets with the current PortalContainer. This example uses PortalContainerPostInitTask which is executed after the portal container has been initialized.
public class GadgetRegister implements ServletContextListener
{
   public void contextInitialized(ServletContextEvent event)
   {
      // Create a new post-init task
      final PortalContainerPostInitTask task = new PortalContainerPostInitTask() {

         public void execute(ServletContext context, PortalContainer portalContainer)
         {
            try
            {
               SourceStorage sourceStorage =
               (SourceStorage) portalContainer.getComponentInstanceOfType(SourceStorage.class);
               ...
            }
            catch (RuntimeException e)
            {
               throw e;
            }
            catch (Exception e)
            {
               throw new RuntimeException("Initialization failed: ", e);
            }
         }
      };

      // Add post-init task for execution on all the portal containers
      // that depend on the given ServletContext according to 
      // PortalContainerDefinitions (via Dependencies configuration)
      PortalContainer.addInitTask(event.getServletContext(), task);
   }
}
In some situations initialization may be required after the portal container is instantiated but before it has initialized. PortalContainerPreInitTask can be used in that case.
Use PortalContainerPostCreateTask if initialization is required after all the post-init tasks have been executed.
LoginModules
If some custom LoginModules require the current eXoContainer for initialization ensure they extend org.exoplatform.services.security.jaas.AbstractLoginModule.
AbstractLoginModule enforces some basic configuration. It recognizes two initialization options; portalContainerName and realmName.
The values for these options can be accessed via protected fields of the same name.

30.11. Manageability

30.11.1. Introduction

The kernel has a framework for exposing a management view of the various sub systems of the platform. The management view is a lose term for defining how we can access relevant information about the system and how we can apply management operations. JMX is the de facto standard for exposing a management view in the Java Platform but we take in consideration other kind of views such as REST web services. Therefore, the framework is not tied to JMX, yet it provides a JMX part to define more precisely details related to the JMX management view. The legacy framework is still in use but is deprecated in favor of the new framework as it is less tested and less efficient. It will be removed by sanitization in the future.

30.11.2. Managed framework API

The managed frameworks define an API for exposing a management view of objects. The API is targeted for internal use and is not a public API. The framework leverages Java 5 annotations to describe the management view from an object.

30.11.2.1. Annotations

30.11.2.1.1. @org.exoplatform.management.annotations.Managed annotation
The @Managed annotates elements that wants to expose a management view to a management layer.
@Managed for objects
The framework will export a management view for the objects annotated.
@Managed for getter/setter
Defines a managed property. An annotated getter defines a read property, an annotated setter defines a write property and if matching getter/setter are annotated it defines a read/write property.
@Managed on method
Defines a managed operation.
30.11.2.1.2. @org.exoplatform.management.annotations.ManagedDescription
The @ManagedDescription annotation provides a description of a managed element. It is valid to annotated object or methods. It takes as sole argument a string that is the description value.
30.11.2.1.3. @org.exoplatform.management.annotations.ManagedName
The @ManagedName annotation provides an alternative name for managed properties. It is used to accomodate legacy methods of an object that can be renamed for compatibility reasons. It takes as sole argument a string that is the name value.
30.11.2.1.4. @org.exoplatform.management.annotations.ManagedBy
The @ManagedBy annotation defines a delegate class for exposing a management view. The sole argument of the annotation are class literals. The delegate class must provide a constructor with the managed object as argument.

30.11.3. JMX Management View

30.11.3.1. JMX Annotations

30.11.3.1.1. @org.exoplatform.management.jmx.annotations.Property annotation
The @Property annotation is used to within other annotations such as @NameTemplate or @NamingContext. It should be seen as a structural way for a list of properties. A property is made of a key and a value. The value can either be a string literal or it can be surrounded by curly brace to be a dynamic property. A dynamic property is resolved against the instance of the object at runtime.
30.11.3.1.2. @org.exoplatform.management.jmx.annotations.NameTemplate annotation
The @NameTemplate defines a template that is used at registration time of a managed object to create the JMX object name. The template is formed of properties.
@NameTemplate({
  @Property(key="container", value="workspace"),
  @Property(key="name", value="{Name}")})
30.11.3.1.3. @org.exoplatform.management.jmx.annotations.NamingContext annotation
The @NamingContext annotation defines a set of properties which are used within a management context. It allows to propagate properties down to managed objects which are defined by an object implementing the ManagementAware interface. The goal is to scope different instances of the same class that would have the same object name otherwise.
@NamingContext(@Property(key="workspace", value="{Name}"))

30.11.4. Example

30.11.4.1. CacheService example

The cache service delegates most of the work to the CacheServiceManaged class by using the @ManagedBy annotation. At runtime when a new cache is created, it calls the CacheServiceManaged class in order to let the CacheServiceManaged object register the cache.
@ManagedBy(CacheServiceManaged.class)
public class CacheServiceImpl implements CacheService {

  CacheServiceManaged managed;
  ...
  synchronized private ExoCache createCacheInstance(String region) throws Exception {
    ...
    if (managed != null) {
      managed.registerCache(simple);
    }
    ...
  }
}
The ExoCache interface is annotated to define its management view. The @NameTemplate is used to produce object name values when ExoCache instance are registered.
@Managed
@NameTemplate({@Property(key="service", value="cache"), @Property(key="name", value="{Name}")})
@ManagedDescription("Exo Cache")
public interface ExoCache {

  @Managed
  @ManagedName("Name")
  @ManagedDescription("The cache name")
  public String getName();

  @Managed
  @ManagedName("Capacity")
  @ManagedDescription("The maximum capacity")
  public int getMaxSize();

  @Managed
  @ManagedDescription("Evict all entries of the cache")
  public void clearCache() throws Exception;

  ...
}
The CacheServiceManaged is the glue code between the CacheService and the management view. The main reason is that only exo services are registered automatically against the management view. Any other managed bean must be registered manually for now. Therefore, it needs to know about the management layer via the management context. The management context allows an object implementing the ManagementAware interface to receive a context to perform further registration of managed objects.
@Managed
public class CacheServiceManaged implements ManagementAware {

  /** . */
  private ManagementContext context;

  /** . */
  private CacheServiceImpl cacheService;

  public CacheServiceManaged(CacheServiceImpl cacheService) {
    this.cacheService = cacheService;

    //
    cacheService.managed = this;
  }

  public void setContext(ManagementContext context) {
    this.context = context;
  }

  void registerCache(ExoCache cache) {
    if (context != null) {
      context.register(cache);
    }
  }
}

Part VI. The Java Content Repository (JCR)

Table of Contents

31. Introduction
31.1. Concepts
32. Implementation
32.1. Workspace Data Model
33. JCR configuration
33.1. Portal configuration
33.1.1. JCR Configuration
33.1.2. Repository service configuration (JCR repositories configuration)
33.1.3. Workspace configuration:
33.1.4. Workspace data container configuration:
33.1.5. Value Storage plug-in configuration (for data container):
33.1.6. Initializer configuration (optional):
33.1.7. Cache configuration:
33.1.8. Query Handler configuration:
33.1.9. Lock Manager configuration:
34. Multi-language Support
34.1. Oracle
34.2. DB2
34.3. MySQL
34.4. PostgreSQL
35. Configuring Search
35.1. Global Search Index
35.2. IndexingConfiguration
35.3. Advanced features
36. Configuring the JDBC Data Container
36.1. Introduction
36.2. Multi-database Configuration
36.3. Single-database Configuration
36.3.1. Configuration without DataSource
36.3.2. Dynamic Workspace Creation
36.4. Simple and Complex queries
36.5. Force Query Hints
36.6. Notes for Microsoft Windows users
37. External Value Storages
37.1. Introduction
37.2. Tree File Value Storage
37.3. Disabling value storage
38. Workspace Data Container
39. Configuring Cluster
39.1. Launching Cluster
39.1.1. Configuring JCR to use external configuration
39.2. Requirements
39.2.1. Environment requirements
39.2.2. Configuration requirements
40. Configuring JBoss Cache
40.1. Indexer, lock manager and data container configuration
40.2. JGroups configuration
40.3. Sharing JBoss Cache instances
40.4. Shipped JBoss Cache configuration templates
40.4.1. Data container template
40.4.2. Lock manager template
40.4.3. Query handler (indexer) template
41. LockManager
41.1. CacheableLockManagerImpl
41.1.1. Simple JBoss Cache Configuration
41.1.2. Template JBoss Cache Configuration
41.1.3. Lock Migration
42. Configuring QueryHandler
42.1. Indexing in clustered environment
42.2. Configuration
42.2.1. Query-handler configuration overview
42.2.2. Cluster-ready indexing
42.2.3. Local Index Recovery Filters
42.2.4. JBoss-Cache template configuration
42.3. Asynchronous Re-indexing
42.3.1. On startup indexing
42.3.2. Hot Asynchronous Workspace Re-indexing using JMX
42.3.3. Notices
42.4. Advanced tuning
42.4.1. Lucene tuning
43. JBossTransactionsService
43.1. Introduction
43.2. Configuration
44. JCR Query Use-cases
44.1. Introduction
44.2. Query Lifecycle
44.2.1. Query Creation and Execution
44.2.2. Query Result Processing
44.2.3. Scoring
44.3. Tips and tricks
44.3.1. XPath queries containing node names starting with a number
45. Searching Repository Content
45.1. Introduction
45.2. Bi-directional RangeIterator
45.3. Fuzzy Searches
45.4. SynonymSearch
45.5. Highlighting
45.5.1. DefaultXMLExcerpt
45.5.2. DefaultHTMLExcerpt
45.5.3. Usage
45.6. SpellChecker
45.6.1. Usage
45.7. Similarity
46. Full Text Search And Affecting Settings
46.1. Lucene Analyzers
46.2. Property Indexing
46.3. Different Analyzers
47. WebDAV
47.1. Introduction
47.2. WebDAV Configuration
47.3. Corresponding WebDAV and JCR actions
47.4. WebDAV Considerations
48. FTP
48.1. Introduction
48.2. Configuration Parameters
49. Use External Backup Tool
49.1. Repository Suspending
49.2. Backup
49.3. Repository Resuming
50. eXo JCR statistics
50.1. Statistics on the Database Access Layer
50.2. Statistics on the JCR API accesses
50.3. Statistics Manager
51. Checking repository integrity and consistency
51.1. JMX-based consistency tool
52. JCR Performance Tuning Guide
52.1. Introduction
52.2. JCR Performance and Scalability
52.2.1. Cluster configuration
52.2.2. JCR Clustered Performance
52.3. Performance Tuning Guide
52.3.1. JBoss Enterprise Application Platform 6 Tuning
52.3.2. JCR Cache Tuning
52.3.3. Clustering
53. eXo JCR with JBoss Portal Platform
53.1. How to use a Managed DataSource under JBoss Enterprise Application Platform 6
53.1.1. Configurations Steps

Chapter 31. Introduction

eXo JCR usage

The JBoss Portal Platform is using a JCR API to store its information for internal usage. We do not support usage of the JCR to store application information.
The information below is intended to assist users to understand particular low level details on how the JBoss Portal Platform works and how it can be fine-tuned.
The term JCR refers to the Java Content Repository. The JCR is the data store of JBoss Portal Platform. All content is stored and managed via the JCR.
The eXo JCR included with JBoss Portal Platform 6.0 is a (JSR-170) compliant implementation of the JCR 1.0 specification. The JCR provides versioning, textual search, access control, content event monitoring, and is used to storing text and binary data for the portal internal usage. The back-end storage of the JCR is configurable and can be a file system or a database.

31.1. Concepts

Repository
A repository is a form of data storage device. A 'repository' differs from a 'database' in the nature of the information contained. While a database holds hard data in rigid tables, a repository may access the data on a database by using less rigid meta-data. In this sense a repository operates as an 'interpreter' between the database(s) and the user.

Note

The data model for the interface (the repository) is rarely the same as the data model used by the repository's underlying storage subsystems (such as a database), however the repository is able to make persistent data changes in the storage subsystem.
Workspace
The eXo JCR uses 'workspaces' as the main data abstraction in its data model. The content is stored in a workspace as a hierarchy of items and each workspace has its own hierarchy of items.
Repositories access one or more workspaces. Persistent JCR workspaces consist of a directed acyclic graph of items where the edges represent the parent-child relation.
Items
An item is either a node or a property. Properties contain the data (either simple values or binary data). The nodes of a workspace give it its structure while the properties hold the data itself.
Nodes
Nodes are identified using accepted namespacing conventions. Changed nodes may be versioned through an associated version graph to preserve data integrity.
Nodes can have various properties or child nodes associated to them.
Properties
Properties hold data as values of predefined types, such as: String, Binary, Long, Boolean, Double, Date, Reference and Path.
The Data Model
The core of any Content Repository is the data model. The data model defines the 'data elements' (fields, columns, attributes, etc.) that are stored in the CR and the relationships between these elements.
Data elements can be singular pieces of information (the value 3.14, for example), or compound values ('pi' = 3.14). A data model uses concepts like 'nodes', 'arrays' and 'links' to define relationships between data elements.
The use and structure of these elements forms the content repository's 'data model'.
Data Abstraction
Data abstraction describes the separation between abstract and concrete properties of data stored in a repository. The concrete properties of the data refer to its implementation details.
The concrete properties of the data implementation may be changed without affecting the abstract properties of the data itself, which are read by the data client.
Consider the presentation of data in a list, graph or table. While the information implementation may change, the data itself is unaffected, and readers to whom the data is presented can perform a mental abstraction to interpret it correctly, regardless of the implementation.

Chapter 32. Implementation

The relationships between the eXo Repository Service components are illustrated below:
Exo JCR

Figure 32.1. Exo JCR


Definitions

eXo Container:
A subclass of org.exoplatform.container.ExoContainer (org.exoplatform.container.PortalContainer) holds a reference to the Repository Service.

Repository Service
This contains information about repositories. eXo JCR is able to manage many Repositories.
Repository
An implementation of javax.jcr.Repository. It holds references to one or more Workspace(s).
Workspace
Container of a single rooted tree of Items.

Note:

That here it is not exactly the same as javax.jcr.Workspace as it is not a per Session object.
The usual JCR application usecase includes two initial steps:
  1. Obtaining Repository object by getting Repository Service via JNDI lookup if eXo repository is bound to the naming context using (see Chapter 33, JCR configuration for details).
  2. Creating a javax.jcr.Session object that calls Repository.login(..).

32.1. Workspace Data Model

The following diagram explains which components of a eXo JCR implementation are used in a data flow to perform operations specified in the JCR API.
Workspace Data Model

Figure 32.2. Workspace Data Model


The Workspace Data Model can be split into four levels by data isolation and value from the JCR model point of view.
  • The eXo JCR core implements JCR API interfaces, such as Item, Node, Property. It contains JCR "logical" view on stored data.
  • Session Level: isolates transient data viewable inside one JCR Session and interacts with API level using eXo JCR internal API.
  • Session Data Manager: maintains transient session data. With data access/ modification/ validation logic, it contains Modified Items Storage to hold the data changed between subsequent save() calling and Session Items Cache.
  • Transaction Data Manager: maintains session data between save() and transaction commit/ rollback if the current session is part of a transaction.
  • Workspace Level: operates for particular workspace shared data. It contains per-Workspace objects
  • Workspace Storage Data Manager: maintains workspace data, including final validation, events firing, caching.
  • Workspace Data Container: implements physical data storage. It allows different types of backend (like RDB, FS files, etc) to be used as a storage for JCR data. With the main Data Container, other storages for persisted Property Values can be configured and used.
  • Indexer: maintains workspace data indexing for further queries.
  • Storage Level: Persistent storages for:
    • JCR Data
    • Indexes (Apache Lucene)
    • Values (e.g., for BLOBs) if different from the main Data Container

Chapter 33. JCR configuration

The JCR configuration is defined in an XML file which is constructed as per the DTD below:
<!ELEMENT repository-service (repositories)>
<!ATTLIST repository-service default-repository NMTOKEN #REQUIRED>
<!ELEMENT repositories (repository)>
<!ELEMENT repository (security-domain,access-control,session-max-age,authentication-policy,workspaces)>
<!ATTLIST repository
  default-workspace NMTOKEN #REQUIRED
  name NMTOKEN #REQUIRED
  system-workspace NMTOKEN #REQUIRED
>
<!ELEMENT security-domain (#PCDATA)>
<!ELEMENT access-control (#PCDATA)>
<!ELEMENT session-max-age (#PCDATA)>
<!ELEMENT authentication-policy (#PCDATA)>
<!ELEMENT workspaces (workspace+)>
<!ELEMENT workspace (container,initializer,cache,query-handler)>
<!ATTLIST workspace name NMTOKEN #REQUIRED>
<!ELEMENT container (properties,value-storages)>
<!ATTLIST container class NMTOKEN #REQUIRED>
<!ELEMENT value-storages (value-storage+)>
<!ELEMENT value-storage (properties,filters)>
<!ATTLIST value-storage class NMTOKEN #REQUIRED>
<!ELEMENT filters (filter+)>
<!ELEMENT filter EMPTY>
<!ATTLIST filter property-type NMTOKEN #REQUIRED>
<!ELEMENT initializer (properties)>
<!ATTLIST initializer class NMTOKEN #REQUIRED>
<!ELEMENT cache (properties)>
<!ATTLIST cache 
  enabled NMTOKEN #REQUIRED
  class NMTOKEN #REQUIRED
>
<!ELEMENT query-handler (properties)>
<!ATTLIST query-handler class NMTOKEN #REQUIRED>
<!ELEMENT access-manager (properties)>
<!ATTLIST access-manager class NMTOKEN #REQUIRED>
<!ELEMENT lock-manager (time-out,persister)>
<!ELEMENT time-out (#PCDATA)>
<!ELEMENT persister (properties)>
<!ELEMENT properties (property+)>
<!ELEMENT property EMPTY>

33.1.  Portal configuration

JCR services are registered in the Portal container.
Below is an example configuration from the JPP_DIST/gatein/gatein.ear/portal.war/WEB-INF/conf/jcr/jcr-configuration.xml file.
<component>
  <key>org.exoplatform.services.jcr.RepositoryService</key>
  <type>org.exoplatform.services.jcr.impl.RepositoryServiceImpl</type>
  <component-plugins>
    <component-plugin>
      <name>add.namespaces</name>
      <set-method>addPlugin</set-method>
      <type>org.exoplatform.services.jcr.impl.AddNamespacesPlugin</type>
      <init-params>
	<properties-param>
	  <name>namespaces</name>
	  <property name="test" value="http://www.apache.org/jackrabbit/test"/>
	  <property name="exojcrtest" value="http://www.exoplatform.org/jcr/test/1.0"/>
	  <property name="rma" value="http://www.rma.com/jcr/"/>
	  <property name="metadata" value="http://www.exoplatform.com/jcr/metadata/1.1/"/>
	  <property name="dc" value="http://purl.org/dc/elements/1.1/"/>
	  <property name="publication" value="http://www.exoplatform.com/jcr/publication/1.1/"/>
	</properties-param>
      </init-params>
    </component-plugin>
    <component-plugin>
      <name>add.nodeType</name>
      <set-method>addPlugin</set-method>
      <type>org.exoplatform.services.jcr.impl.AddNodeTypePlugin</type>
      <init-params>
	<values-param>
	  <name>autoCreatedInNewRepository</name>
	  <description>Node types configuration file</description>
	  <value>jar:/conf/test/nodetypes-tck.xml</value>
	  <value>jar:/conf/test/nodetypes-impl.xml</value>
	  <value>jar:/conf/test/nodetypes-usecase.xml</value>
	  <value>jar:/conf/test/nodetypes-config.xml</value>
	  <value>jar:/conf/test/nodetypes-config-extended.xml</value>  
	  <value>jar:/conf/test/wcm-nodetypes.xml</value>
	  <value>jar:/conf/test/nodetypes-publication-config.xml</value>
	  <value>jar:/conf/test/publication-plugins-nodetypes-config.xml</value>          
	</values-param>
	
	<values-param>
	  <name>testInitNodeTypesRepository</name>
	  <description>
	    Node types configuration file for repository with name testInitNodeTypesRepository
	  </description>
	  <value>jar:/conf/test/nodetypes-test.xml</value>
	</values-param>
	
	<values-param>
	  <name>testInitNodeTypesRepositoryTest2</name>
	  <description>
	    Node types configuration file for repository with name testInitNodeTypesRepositoryTest2
	  </description>
	  <value>jar:/conf/test/nodetypes-test2.xml</value>
	</values-param>
	
	<!--values-param>
	<name>testInitNodeTypesRepositoryTest3</name>
	<description>Node types from ext. Needed bacause core starup earlie than ext</description>
	<value>jar:/conf/test/nodetypes-test3_ext.xml</value>
	</values-param-->
	
      </init-params>
    </component-plugin>
  </component-plugins>
</component>

<component>
  <key>org.exoplatform.services.jcr.config.RepositoryServiceConfiguration</key>
  <type>org.exoplatform.services.jcr.impl.config.RepositoryServiceConfigurationImpl</type>
  <init-params>
    <value-param>
      <name>conf-path</name>
      <description>JCR configuration file</description>
      <value>jar:/conf/standalone/test-jcr-config-jbc.xml</value>
    </value-param>
    <properties-param>
      <name>working-conf</name>
      <description>working-conf</description>
      <property name="dialect" value="auto" />
      <property name="source-name" value="jdbcjcr"/>
      <property name="persister-class-name" value="org.exoplatform.services.jcr.impl.config.JDBCConfigurationPersister"/>
    </properties-param>
  </init-params>
</component>

33.1.1. JCR Configuration

The JCR Service can use multiple Repositories and each repository can have multiple Workspaces.
Configure the workspaces by locating the workspace you need to modify in JPP_DIST/gatein/gatein.ear/portal.war/WEB-INF/conf/jcr/repository-configuration.xml.
The repository configuration supports human-readable values. They are not case-sensitive.
Complete the appropriate element fields using the following value formats:
Number formats:
  • K or KB for kilobytes.
  • M or MB for megabytes.
  • G or GB for gigabytes.
  • T or TB for terabytes.
  • Examples: 200K or 200KB; 4M or 4MB; 1.4G or 1.4GB; 10T or 10TB.
Time formats:
  • ms for milliseconds.
  • s for seconds.
  • m for minutes.
  • h for hours.
  • d for days.
  • w for weeks.
  • The default time format is seconds if no other format is specified.
  • Examples: 500ms or 500 milliseconds; 20, 20s or 20 seconds; 30m or 30 minutes; 12h or 12 hours; 5d or 5 days; 4w or 4 weeks.

33.1.2. Repository service configuration (JCR repositories configuration)

<!-- The name of the default repository (the one returned by RepositoryService.getRepository() ). -->
<repository-service default-repository="repository">
  <!-- The list of repositories. -->
  <repositories>
    <!-- Parameters left to right: the name of a repository, the name of workspace where /jcr:system node is placed and the name of the workspace obtained using Session's login() or login(Credentials) methods (the ones without an explicit workspace name). -->
    <repository name="repository" system-workspace="system" default-workspace="portal-system">
      <!-- The name of a security domain for JAAS authentication. -->
      <security-domain>gatein-domain</security-domain>
      <!-- The name of an access control policy. There can be 3 types: optional - ACL is created on-demand(default), disable - no access control, mandatory - an ACL is created for each added node(not supported yet). -->
      <access-control>optional</access-control>
      <!-- The name of an authentication policy class. -->
      <authentication-policy>org.exoplatform.services.jcr.impl.core.access.JAASAuthenticator</authentication-policy>
      ...
      <!-- The list of workspaces. -->
      <workspaces>
      ...
        <!-- The name of the workspace. -->
        <workspace name="portal-system">
          <!-- Workspace data container (physical storage) configuration. --> 
          <container class="org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.CQJDBCWorkspaceDataContainer">
            <properties>
              <property name="source-name" value="${gatein.jcr.datasource.name}${container.name.suffix}"/>
              <property name="dialect" value="${gatein.jcr.datasource.dialect}"/>
              <property name="multi-db" value="false"/>
              <property name="update-storage" value="true"/>
              <property name="max-buffer-size" value="204800"/>
              <property name="swap-directory" value="${gatein.jcr.data.dir}/swap/portal-system${container.name.suffix}"/>
            </properties> 
            <value-storages>
              <value-storage id="portal-system" class="org.exoplatform.services.jcr.impl.storage.value.fs.TreeFileValueStorage">
                <properties>
                  <property name="path" value="${gatein.jcr.storage.data.dir}/portal-system${container.name.suffix}"/>
                </properties>
                <filters>
                  <filter property-type="Binary"/>
                </filters>
              </value-storage>
            </value-storages>
          </container>
          <!-- Workspace initializer configuration. -->
          <initializer class="org.exoplatform.services.jcr.impl.core.ScratchWorkspaceInitializer">
            <properties>
              <property name="root-nodetype" value="nt:unstructured"/>
              <property name="root-permissions" value="*:/platform/administrators read;*:/platform/administrators add_node;*:/platform/administrators set_property;*:/platform/administrators remove"/>
            </properties>
          </initializer>
          <!-- Workspace storage cache configuration. -->
          <cache enabled="true" class="org.exoplatform.services.jcr.impl.dataflow.persistent.jbosscache.JBossCacheWorkspaceStorageCache">
            <properties>
              <property name="jbosscache-configuration" value="${gatein.jcr.cache.config}" />
              <property name="jgroups-configuration" value="${gatein.jcr.jgroups.config}" />
              <property name="jgroups-multiplexer-stack" value="true" />
              <property name="jbosscache-cluster-name" value="jcr-${container.name.suffix}-portal-system" />
            </properties>
          </cache>
          <!-- Query handler configuration. -->
          <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
            <properties>
              <property name="index-dir" value="${gatein.jcr.index.data.dir}/portal-system${container.name.suffix}"/>
              <property name="changesfilter-class" value="${gatein.jcr.index.changefilterclass}" />
              <property name="jbosscache-configuration" value="${gatein.jcr.index.cache.config}" />
              <property name="jgroups-configuration" value="${gatein.jcr.jgroups.config}" />
              <property name="jgroups-multiplexer-stack" value="true" />
              <property name="jbosscache-cluster-name" value="jcrindexer-${container.name.suffix}-portal-system" />
              <property name="max-volatile-time" value="60" />
            </properties>
          </query-handler>
          <lock-manager class="org.exoplatform.services.jcr.impl.core.lock.jbosscache.CacheableLockManagerImpl">
            <properties>
              <!-- The amount of time before the unused global lock is removed. -->
              <property name="time-out" value="15m" />
              <property name="jbosscache-configuration" value="${gatein.jcr.lock.cache.config}" />
              <property name="jgroups-configuration" value="${gatein.jcr.jgroups.config}" />
              <property name="jgroups-multiplexer-stack" value="true" />
              <property name="jbosscache-cluster-name" value="jcrlock-${container.name.suffix}-portal-system" />
              <property name="jbosscache-cl-cache.jdbc.table.name" value="jcrlock_portal_system" />
              <property name="jbosscache-cl-cache.jdbc.table.create" value="true" />
              <property name="jbosscache-cl-cache.jdbc.table.drop" value="false" />
              <property name="jbosscache-cl-cache.jdbc.table.primarykey" value="pk" />
              <property name="jbosscache-cl-cache.jdbc.fqn.column" value="fqn" />
              <property name="jbosscache-cl-cache.jdbc.node.column" value="node" />
              <property name="jbosscache-cl-cache.jdbc.parent.column" value="parent" />
              <property name="jbosscache-cl-cache.jdbc.datasource" value="${gatein.jcr.datasource.name}${container.name.suffix}" />
            </properties>
          </lock-manager>
        </workspace>

session-max-age

session-max-age: This parameter is not shown in the example file above as it is not a required setting. It sets the time after which an idle session will be removed (called logout). If it is not set up, an idle session will never be removed.
lock-remover-max-threads: Number of threads that can serve LockRemover tasks. Default value is 1. Repository may have many workspaces, each workspace have own LockManager. JCR supports Locks with defined lifetime. Such a lock must be removed is it become expired. That is what LockRemovers does. But LockRemovers is not an independent timer-threads, its a task that executed each 30 seconds. Such a task is served by ThreadPoolExecutor which may use different number of threads.

33.1.3. Workspace configuration:

name
The name of a workspace.
auto-init-root-nodetype
DEPRECATED in JCR 1.9 (use initializer). The node type for root node initialization.
container
Workspace data container (physical storage) configuration.
initializer
Workspace initializer configuration.
cache
Workspace storage cache configuration.
query-handler
Query handler configuration.
auto-init-permissions
DEPRECATED in JCR 1.9 (use initializer). Default permissions of the root node. It is defined as a set of semicolon-delimited permissions containing a group of space-delimited identities and the type of permission.
For example, any read; :/admin read;:/admin add_node; :/admin set_property;:/admin remove means that users from group admin have all permissions and other users have only a 'read' permission.

33.1.4. Workspace data container configuration:

class
A workspace data container class name.
properties
The list of properties (name-value pairs) for the concrete Workspace data container.

Table 33.1. Parameter Descriptions

Parameter Description
trigger_events_for_descendents_on_rename indicates if need to trigger events for descendants on rename or not. It allows to increase performance on rename operation but in same time Observation is not notified, has default value true
lazy-node-iterator-page-size the page size for lazy iterator. Indicates how many nodes can be retrieved from storage per request. The default value is 100
acl-bloomfilter-false-positive-probability ACL Bloom-filter desired false positive probability. Range [0..1]. Default value 0.1d. (See the note below)
acl-bloomfilter-elements-number Expected number of ACL-elements in the Bloom-filter. Default value 1000000. (See the note below)

Note

Bloom filters are not supported by all the cache implementations so far only the implementation for infinispan supports it.

value-storage
The list of value storage plug-ins.

33.1.5. Value Storage plug-in configuration (for data container):

Note

The value-storage element is optional. If you do not include it, the values will be stored as BLOBs inside the database.

value-storage
Optional value Storage plug-in definition.
class
A value storage plug-in class name (attribute).
properties
The list of properties (name-value pairs) for a concrete Value Storage plug-in.
filters
The list of filters defining conditions when this plug-in is applicable.

33.1.6. Initializer configuration (optional):

class
Initializer implementation class.
properties
The list of properties (name-value pairs). Properties are supported.
root-nodetype
The node type for root node initialization.
root-permissions
Default permissions of the root node. It is defined as a set of semicolon-delimited permissions containing a group of space-delimited identities (user, group etc, see Organization service documentation for details) and the type of permission. For example any read; :/admin read;:/admin add_node; :/admin set_property;:/admin remove means that users from group admin have all permissions and other users have only a 'read' permission.
Configurable initializer adds a capability to override workspace initial startup procedure (used for Clustering). Also it replaces workspace element parameters auto-init-root-nodetype and auto-init-permissions with root-nodetype and root-permissions.

33.1.7. Cache configuration:

enabled
If workspace cache is enabled or not.
class
Cache implementation class, optional from 1.9. Default value is. org.exoplatform.services.jcr.impl.dataflow.persistent.LinkedWorkspaceStorageCacheImpl.
Cache can be configured to use concrete implementation of WorkspaceStorageCache interface. JCR core has two implementation to use:
  • LinkedWorkspaceStorageCacheImpl - default, with configurable read behavior and statistic.
  • WorkspaceStorageCacheImpl - pre 1.9, still can be used.
properties
The list of properties (name-value pairs) for Workspace cache.
max-size
Cache maximum size (maxSize prior to v.1.9).
live-time
Cached item live time (liveTime prior to v.1.9).
From 1.9 LinkedWorkspaceStorageCacheImpl supports additional optional parameters.
statistic-period
Period (time format) of cache statistic thread execution, 5 minutes by default.
statistic-log
If true cache statistic will be printed to default logger (log.info), false by default or not.
statistic-clean
If true cache statistic will be cleaned after was gathered, false by default or not.
cleaner-period
Period of the eldest items remover execution, 20 minutes by default.
blocking-users-count
Number of concurrent users allowed to read cache storage, 0 - unlimited by default.

33.1.8. Query Handler configuration:

class
A Query Handler class name.
properties
The list of properties (name-value pairs) for a Query Handler (indexDir).
Properties and advanced features described in Search Configuration.

33.1.9. Lock Manager configuration:

time-out
Time after which the unused global lock will be removed.
persister
A class for storing lock information for future use. For example, remove lock after jcr restart.
path
A lock folder. Each workspace has its own one.

Chapter 34. Multi-language Support

Whenever a relational database is used to store multilingual text data in the eXo Java Content Repository the configuration must be adapted to support UTF-8 encoding. Dialect is automatically detected for certified database. You can still enforce it in case of failure, see below.
The following sections describe enabling UTF-8 support with various databases.

Note

  • The configuration file to be modified for these changes is JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/jcr/repository-configuration.xml.
  • The datasource jdbcjcr used in the following examples can be configured via the InitialContextInitializer component.

34.1. Oracle

In order to run multilanguage JCR on an Oracle backend Unicode encoding for characters set should be applied to the database. Other Oracle globalization parameters do not have any effect. The property to modify is NLS_CHARACTERSET.
The NLS_CHARACTERSET = AL32UTF8 entry has been successfully tested with many European and Asian languages.
Example of database configuration:
NLS_LANGUAGE             AMERICAN
NLS_TERRITORY            AMERICA
NLS_CURRENCY             $
NLS_ISO_CURRENCY         AMERICA
NLS_NUMERIC_CHARACTERS   .,
NLS_CHARACTERSET         AL32UTF8
NLS_CALENDAR             GREGORIAN
NLS_DATE_FORMAT          DD-MON-RR
NLS_DATE_LANGUAGE        AMERICAN
NLS_SORT                 BINARY
NLS_TIME_FORMAT          HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT     DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT       HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT  DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY        $
NLS_COMP                 BINARY
NLS_LENGTH_SEMANTICS     BYTE
NLS_NCHAR_CONV_EXCP      FALSE
NLS_NCHAR_CHARACTERSET   AL16UTF16
Create database with Unicode encoding and use Oracle dialect for the Workspace Container:
<workspace name="collaboration">
          <container class="org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer">
            <properties>
              <property name="source-name" value="jdbcjcr" />
              <property name="dialect" value="oracle" />
              <property name="multi-db" value="false" />
              <property name="max-buffer-size" value="200k" />
              <property name="swap-directory" value="target/temp/swap/ws" />
            </properties>
          .....

34.2. DB2

DB2 Universal Database (DB2 UDB) supports UTF-8 and UTF-16/UCS-2. When a Unicode database is created, CHAR, VARCHAR and LONG VARCHAR data are stored in UTF-8 form.
This enables JCR multi-lingual support.
Below is an example of creating a UTF-8 database using the db2 dialect for a workspace container with DB2 version 9 and higher:
DB2 CREATE DATABASE dbname USING CODESET UTF-8 TERRITORY US
<workspace name="collaboration">
          <container class="org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer">
            <properties>
              <property name="source-name" value="jdbcjcr" />
              <property name="dialect" value="db2" />
              <property name="multi-db" value="false" />
              <property name="max-buffer-size" value="200k" />
              <property name="swap-directory" value="target/temp/swap/ws" />
            </properties>
          .....

Note

For DB2 version 8.x support change the property "dialect" to db2v8.

34.3. MySQL

Using JCR with a MySQL-back end requires a special dialect MySQL-UTF8 to be used for internationalization support.
The database default charset should be latin1 so as to use limited index space effectively (767 for InnoDB).
If the database default charset is multibyte, a JCR database initialization error is encountered concerning index creation failure.
JCR can work on any single byte default charset of database, with UTF8 supported by MySQL server. However it has only been tested using the latin1 charset.
An example entry:
<workspace name="collaboration">
          <container class="org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer">
            <properties>
              <property name="source-name" value="jdbcjcr" />
              <property name="dialect" value="mysql-utf8" />
              <property name="multi-db" value="false" />
              <property name="max-buffer-size" value="200k" />
              <property name="swap-directory" value="target/temp/swap/ws" />
            </properties>
          .....

34.4. PostgreSQL

Multilingual support can be enabled with a PostgreSQL-back end in different ways:
  1. Using the locale features of the operating system to provide locale-specific collation order, number formatting, translated messages, and other aspects.
    UTF-8 is widely used on Linux distributions by default, so it can be useful in such cases.
  2. Providing a number of different character sets defined in the PostgreSQL server, including multiple-byte character sets, to support storing text any language, and providing character set translation between client and server.
    Using UTF-8 database charset is recommended as it will allow any-to-any conversations and make this issue transparent for the JCR.
Example of a database with UTF-8 encoding using PgSQL dialect for the Workspace Container:
<workspace name="collaboration">
          <container class="org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer">
            <properties>
              <property name="source-name" value="jdbcjcr" />
              <property name="dialect" value="pgsql" />
              <property name="multi-db" value="false" />
              <property name="max-buffer-size" value="200k" />
              <property name="swap-directory" value="target/temp/swap/ws" />
            </properties>
          .....

Chapter 35. Configuring Search

The search function in JCR can be configured to perform in specific ways. This section will discuss configuring the search function to improve search performance and results.
Below is an example of the configuration file that governs search behaviors. Refer to Section 35.1, “Global Search Index” for how searching operates in JCR and information about customized searches.
The JCR index configuration file is located at JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/jcr/repository-configuration.xml.
A code example is included below with a list of the configuration parameters shown below that.
<repository-service default-repository="db1">
  <repositories>
    <repository name="db1" system-workspace="ws" default-workspace="ws">
       ....
      <workspaces>
        <workspace name="ws">
       ....
          <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
            <properties>
              <property name="index-dir" value="${java.io.tmpdir}/temp/index/db1/ws" />
              <property name="synonymprovider-class" value="org.exoplatform.services.jcr.impl.core.query.lucene.PropertiesSynonymProvider" />
              <property name="synonymprovider-config-path" value="/synonyms.properties" />
              <property name="indexing-config-path" value="/indexing-configuration.xml" />
              <property name="query-class" value="org.exoplatform.services.jcr.impl.core.query.QueryImpl" />
            </properties>
          </query-handler>
        ... 
        </workspace>
     </workspaces>
    </repository>        
  </repositories>
</repository-service>
The table below outlines some of the Configuration Parameters available, their default setting, which version of eXo JCR they were implemented in and other useful information (further parameters are explained in Section 40.1, “Indexer, lock manager and data container configuration”):

Table 35.1. Configuration parameters

Parameter
Default
Description
Implemented in Version
index-dir
none
The location of the index directory. This parameter is mandatory. It is called "indexDir" in versions prior to eXo JCR version 1.9.
1.0
use-compoundfile
true
Advises lucene to use compound files for the index files.
1.9
min-merge-docs
100
The minimum number of nodes in an index until segments are merged.
1.9
volatile-idle-time
3
Idle time in seconds until the volatile index part is moved to a persistent index even though minMergeDocs is not reached.
1.9
max-merge-docs
Integer.MAX_VALUE
The maximum number of nodes in segments that will be merged. The default value changed to Integer.MAX_VALUE in eXo JCR version 1.9.
1.9
merge-factor
10
Determines how often segment indices are merged.
1.9
max-field-length
10000
The number of words that are full-text indexed at most per property.
1.9
cache-size
1000
Size of the document number cache. This cache maps UUID to lucene document numbers
1.9
force-consistencycheck
false
Runs a consistency check on every start up. If false, a consistency check is only performed when the search index detects a prior forced shutdown.
1.9
auto-repair
true
Errors detected by a consistency check are automatically repaired. If false, errors are only written to the log.
1.9
query-class QueryImpl
Classname that implements the javax.jcr.query.Query interface.
This class must also extend from the class: org.exoplatform.services.jcr.impl.core. query.AbstractQueryImpl.
1.9
document-order
true
If true and the query does not contain an 'order by' clause, result nodes will be in document order. For better performance set to 'false' when queries return many nodes.
1.9
result-fetch-size
Integer.MAX_VALUE
The number of results when a query is executed. Default value: Integer.MAX_VALUE.
1.9
excerptprovider-class
DefaultXMLExcerpt
The name of the class that implements org.exoplatform.services.jcr.impl.core. query.lucene.ExcerptProvider.
This should be used for the rep:excerpt() function in a query.
1.9
support-highlighting
false
If set to true additional information is stored in the index to support highlighting using the rep:excerpt() function.
1.9
synonymprovider-class
none
The name of a class that implements org.exoplatform.services.jcr.impl.core. query.lucene.SynonymProvider.
The default value is null.
1.9
synonymprovider-config-path
none
The path to the synonym provider configuration file. This path is interpreted relative to the path parameter. If there is a path element inside the SearchIndex element, then this path is interpreted relative to the root path of the path. Whether this parameter is mandatory depends on the synonym provider implementation. The default value is null.
1.9
indexing-configuration-path
none
The path to the indexing configuration file.
1.9
indexing-configuration-class
IndexingConfigurationImpl
The name of the class that implements org.exoplatform.services.jcr.impl.core. query.lucene.IndexingConfiguration.
1.9
force-consistencycheck
false
If set to true a consistency check is performed depending on the parameter forceConsistencyCheck. If set to false no consistency check is performed on start up, even if a redo log had been applied.
1.9
spellchecker-class
none
The name of a class that implements org.exoplatform.services.jcr.impl.core. query.lucene.SpellChecker.
1.9
errorlog-size
50(KB)
The default size of error log file in KB.
1.9
upgrade-index
false
Allows JCR to convert an existing index into the new format. It is also possible to set this property via system property.
Indexes prior to eXo JCR 1.12 will not run with eXo JCR 1.12. You must run an automatic migration.
Start eXo JCR with:
 -Dupgrade-index=true
The old index format is then converted in the new index format. After the conversion the new format is used.
On subsequent starts this option is no longer needed. The old index is replaced and a back conversion is not possible
It is recommended that a backup of the index be made before conversion. (Only for migrations from JCR 1.9 and later.)
1.12
analyzer
org.apache.lucene.analysis. standard.StandardAnalyzer
Class name of a lucene analyzer to use for full-text indexing of text.
1.12

35.1. Global Search Index

By default eXo JCR uses the Lucene standard Analyzer to index contents. This analyzer uses some standard filters in the method that analyzes the content

Example 35.1. Standard Analyzed Filters

public TokenStream tokenStream(String fieldName, Reader reader) {
    StandardTokenizer tokenStream = new StandardTokenizer(reader, replaceInvalidAcronym);
    tokenStream.setMaxTokenLength(maxTokenLength);
    // Comment #1
    TokenStream result = new StandardFilter(tokenStream);
    // Comment #2
    result = new LowerCaseFilter(result);
    // Comment #3
    result = new StopFilter(result, stopSet);
    return result;
  }
Comment #1: The first filter (StandardFilter) removes possessive apostrophes ('s) from the end of words and removes periods (.) from acronyms.
Comment #2: The second filter (LowerCaseFilter) normalizes token text to lower case.
Comment #3: The last filter (StopFilter) removes stop words from a token stream. The stop set is defined in the analyzer.

The global search index is configured in the JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/jcr/repository-configuration.xml configuration file within the "query-handler" tag.
<query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
The same analyzer should always be used for indexing and for querying in lucene otherwise results may be unpredictable. eXo JCR does this automatically. The StandardAnalyzer (configured by default) can, however, be replaced with another.
A customized QueryHandler can also be easily created.
Customized Search Indexes and Analyzers
By default Exo JCR uses the Lucene standard Analyzer to index contents. This analyzer uses some standard filters in the method that analyzes the content:
public TokenStream tokenStream(String fieldName, Reader reader) {
    StandardTokenizer tokenStream = new StandardTokenizer(reader, replaceInvalidAcronym);
    tokenStream.setMaxTokenLength(maxTokenLength);
    TokenStream result = new StandardFilter(tokenStream);
    result = new LowerCaseFilter(result);
    result = new StopFilter(result, stopSet);
    return result;
  }
  • The first one (StandardFilter) removes 's (as 's in "Peter's") from the end of words and removes dots from acronyms.
  • The second one (LowerCaseFilter) normalizes token text to lower case.
  • The last one (StopFilter) removes stop words from a token stream. The stop set is defined in the analyzer.
Additional filters can be used in specific cases. The ISOLatin1AccentFilter filter, for example, which replaces accented characters in the ISO Latin 1 character set (ISO-8859-1) by their unaccented equivalents.
The ISOLatin1AccentFilter is not present in the current lucene version used by eXo.
In order to use a different filter, a new analyzer must be created, as well as new search index to use the analyzer. These are packaged into a jar file, which is then deployed with the application.

Procedure 35.1. Create a new filter, analyzer and search index

  1. Create a new filter with the method:
    public final Token next(final Token reusableToken) throws java.io.IOException
    
    This defines how characters are read and used by the filter.
  2. Create the analyzer.
    The analyzer must extend org.apache.lucene.analysis.standard.StandardAnalyzer and overload the method.
    Use the following to use new filters.
    public TokenStream tokenStream(String fieldName, Reader reader)
    
  3. To create the new search index, extend org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex and write the constructor to set the correct analyzer.
    Use the method below to return your analyzer:
    public Analyzer getAnalyzer() {
    return MyAnalyzer;
    }
    

Note

In eXo JCR version 1.12 (and later) the analyzer can be directly set in the configuration. For users with this version the creation of a new SearchIndex for new analyzers is redundant.
To configure an application to use a new SearchIndex, replace the following code:
<query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">

in JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/jcr/repository-configuration.xml with the new class:
<query-handler class="mypackage.indexation.MySearchIndex>

To configure an application to use a new analyzer, add the analyzer parameter to each query-handler configuration in JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/jcr/repository-configuration.xml:
<query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
   <properties>
      ...
      <property name="analyzer" value="org.exoplatform.services.jcr.impl.core.MyAnalyzer"/>
      ...
   </properties>
</query-handler>
The new SearchIndex will start to index contents with the specified filters when the JCR is next started.

35.2. IndexingConfiguration

From version 1.9, the default search index implementation in JCR allows user control over which properties of a node are indexed. Different analyzers can also be set for different nodes.
The configuration parameter is called indexingConfiguration and is not set by default. This means all properties of a node are indexed.
To configure the indexing behavior add a parameter to the query-handler element in your configuration file.
<param name="indexing-configuration-path" value="/indexing_configuration.xml"/>

Node Scope Limit
The node scope can be limited so that only certain properties of a node type are indexed. This can optimize the index size.
With the configuration below only properties named Text are indexed for nt:unstructured node types. This configuration also applies to all nodes whose type extends from nt:unstructured.
<?xml version="1.0"?>
<!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <index-rule nodeType="nt:unstructured">
    <property>Text</property>
  </index-rule>
</configuration>

Namespace Prefixes

The namespace prefixes must be declared throughout the XML file in the configuration element that is being used.
Indexing Boost Value
It is also possible to configure a boost value for the nodes that match the index rule. The default boost value is 1.0. Higher boost values (a reasonable range is 1.0 - 5.0) will yield a higher score value and appear as more relevant.
<?xml version="1.0"?>
<!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <index-rule nodeType="nt:unstructured"
              boost="2.0">
    <property>Text</property>
  </index-rule>
</configuration>
If you do not wish to boost the complete node, but only certain properties, you can also provide a boost value for the listed properties:
<?xml version="1.0"?>
<!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <index-rule nodeType="nt:unstructured">
    <property boost="3.0">Title</property>
    <property boost="1.5">Text</property>
  </index-rule>
</configuration>
Conditional Index Rules
You may also add a condition to the index rule and have multiple rules with the same nodeType. The first index rule that matches will apply and all remaining ones are ignored:
<?xml version="1.0"?>
<!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <index-rule nodeType="nt:unstructured"
              boost="2.0"
              condition="@priority = 'high'">
    <property>Text</property>
  </index-rule>
  <index-rule nodeType="nt:unstructured">
    <property>Text</property>
  </index-rule>
</configuration>
In the above example the first rule only applies if the nt:unstructured node has a priority property with a value high. The condition syntax only supports the equals operator and a string literal.
Properties may also be referenced on the condition that are not on the current node:
<?xml version="1.0"?>
<!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <index-rule nodeType="nt:unstructured"
              boost="2.0"
              condition="ancestor::*/@priority = 'high'">
    <property>Text</property>
  </index-rule>
  <index-rule nodeType="nt:unstructured"
              boost="0.5"
              condition="parent::foo/@priority = 'low'">
    <property>Text</property>
  </index-rule>
  <index-rule nodeType="nt:unstructured"
              boost="1.5"
              condition="bar/@priority = 'medium'">
    <property>Text</property>
  </index-rule>
  <index-rule nodeType="nt:unstructured">
    <property>Text</property>
  </index-rule>
</configuration>
The indexing configuration allows the type of a node in the condition to be specified. Please note however that the type match must be exact. It does not consider sub types of the specified node type.
<?xml version="1.0"?>
<!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <index-rule nodeType="nt:unstructured"
              boost="2.0"
              condition="element(*, nt:unstructured)/@priority = 'high'">
    <property>Text</property>
  </index-rule>
</configuration>
Exclusion from the Node Scope Index
All configured properties are full-text indexed by default (if they are of type STRING and included in the node scope index).
A node scope search normally finds all nodes of an index. That is to say; jcr:contains(., 'foo') returns all nodes that have a string property containing the word 'foo'.
Properties can be explicitly excluded from the node scope index with:
<?xml version="1.0"?>
<!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <index-rule nodeType="nt:unstructured">
    <property nodeScopeIndex="false">Text</property>
  </index-rule>
</configuration>
Index Aggregates
Sometimes it is useful to include the contents of descendant nodes into a single node to more easily search on content that is scattered across multiple nodes.
JCR allows the definition of index aggregates based on relative path patterns and primary node types.
The following example creates an index aggregate on nt:file that includes the content of the jcr:content node:
<?xml version="1.0"?>
<!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:jcr="http://www.jcp.org/jcr/1.0"
               xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <aggregate primaryType="nt:file">
    <include>jcr:content</include>
  </aggregate>
</configuration>
Included nodes can also be restricted to a certain type:
<?xml version="1.0"?>
<!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:jcr="http://www.jcp.org/jcr/1.0"
               xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <aggregate primaryType="nt:file">
    <include primaryType="nt:resource">jcr:content</include>
  </aggregate>
</configuration>
The * wild-card can be used to match all child nodes:
<?xml version="1.0"?>
<!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:jcr="http://www.jcp.org/jcr/1.0"
               xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <aggregate primaryType="nt:file">http://wiki.exoplatform.com/xwiki/bin/edit/JCR/Search+Configuration
    <include primaryType="nt:resource">*</include>
  </aggregate>
</configuration>
Nodes to a certain depth below the current node can be included by adding multiple include elements. The nt:file node may contain a complete XML document under jcr:content for example:
<?xml version="1.0"?>
<!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:jcr="http://www.jcp.org/jcr/1.0"
               xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <aggregate primaryType="nt:file">
    <include>*</include>
    <include>*/*</include>
    <include>*/*/*</include>
  </aggregate>
</configuration>
Property-Level Analyzers
How a property has to be analyzed can be defined in the following configuration section. If there is an analyzer configuration for a property, this analyzer is used for indexing and searching of this property. For example:
<?xml version="1.0"?>
<!DOCTYPE configuration SYSTEM "http://www.exoplatform.org/dtd/indexing-configuration-1.0.dtd">
<configuration xmlns:nt="http://www.jcp.org/jcr/nt/1.0">
  <analyzers> 
        <analyzer class="org.apache.lucene.analysis.KeywordAnalyzer">
            <property>mytext</property>
        </analyzer>
        <analyzer class="org.apache.lucene.analysis.WhitespaceAnalyzer">
            <property>mytext2</property>
        </analyzer>
  </analyzers> 
</configuration>
The configuration above sets lucene KeywordAnalyzer to index and search the property "mytext" across the entire workspace while the "mytext2" property is searched with the WhitespaceAnalyzer.
The WhitespaceAnalyzer tokenizes a property, the KeywordAnalyzer takes the property as a whole.
Using different analyzers for different languages can be particularly useful.
Characteristics of Node Scope Searches
Unexpected behavior may be encountered when using analyzers to search within a property compared to searching within a node scope. This is because the node scope always uses the global analyzer.
For example: the property "mytext" contains the text; "testing my analyzers" but no analyzers have been configured for this property (and the default analyzer in SearchIndex has not been changed).
If the query is:
xpath = "//*[jcr:contains(mytext,'analyzer')]"
The xpath does not return a result in the node with the property above and default analyzers.
Also, if a search is done on the node scope as follows:
xpath = "//*[jcr:contains(.,'analyzer')]"
No result will be returned.
Only specific analyzers can be set on a node property, and the node scope indexing and analyzing is always done with the globally defined analyzer in the SearchIndex element.
If the analyzer used to index the "mytext" property above is changed to:
<analyzer class="org.apache.lucene.analysis.Analyzer.GermanAnalyzer">
<property>mytext</property>
</analyzer>
The search below would return a result because of the word stemming (analyzers - analyzer).
xpath = "//*[jcr:contains(mytext,'analyzer')]"
The second search in the example:
xpath = "//*[jcr:contains(.,'analyzer')]"
Would still not give a result, since the node scope is indexed with the global analyzer, which in this case does not take into account any word stemming.
Be aware that when using analyzers for specific properties, a result may be found in a property for certain search text, but the same search text in the node scope of the property may not find a result.

Note

Both index rules and index aggregates influence how content is indexed in JCR. If the configuration is changed, the existing content is not automatically re-indexed according to the new rules.
Content must be manually re-indexed when the configuration is changed.

35.3. Advanced features

eXo JCR supports some advanced features, which are not specified in JSR 170:

Chapter 36. Configuring the JDBC Data Container

36.1. Introduction

eXo JCR persistent data container can work in two configuration modes:
  • Multi-database: One database for each workspace (used in standalone eXo JCR service mode)
  • Single-database: All workspaces persisted in one database (used in embedded eXo JCR service mode, e.g. in eXo portal)
The data container uses the JDBC driver to communicate with the actual database software, i.e. any JDBC-enabled data storage can be used with eXo JCR implementation.
Currently the data container is tested with the following RDBMS:

Table 36.1. Supported databases

Database Driver Version
IBM DB2 9.7 (FP5) IBM DB2 JDBC Universal Driver Architecture 4.13.80
Oracle 11g R1 (11.1.0.7.0) Oracle JDBC Driver 11.1.0.7
Oracle 11g R1 RAC (11.1.0.7.0) Oracle JDBC Driver 11.1.0.7
Oracle 11g R2 (11.2.0.3.0) Oracle JDBC Driver v11.2.0.3.0
Oracle 11g R2 RAC (11.2.0.3.0) Oracle JDBC Driver v11.2.0.3.0
MySQL 5.1 MySQL Connector/J 5.1.21
MySQL 5.5 MySQL Connector/J 5.1.21
Microsoft SQL Server 2008 Microsoft SQL Server JDBC Driver 3.0.1301.101, Microsoft SQL Server JDBC Driver 4.0.2206.100
Microsoft SQL Server 2008 R2 Microsoft SQL Server JDBC Driver 3.0.1301.101, Microsoft SQL Server JDBC Driver 4.0.2206.100
PostgreSQL 8.4.8 JDBC4 Postgresql Driver, Version 8.4-703
PostgreSQL 9.1.0 JDBC4 Postgresql Driver, Version 9.1-903
Sybase ASE 15.7 Sybase jConnect JDBC driver v7

Isolation Levels

The JCR requires at least the READ_COMMITED isolation level and other RDBMS configurations can cause some side-effects and issues. So, please, make sure proper isolation level is configured on database server side.

Note

One more mandatory JCR requirement for underlying databases is a case sensitive collation. Microsoft SQL Server both 2005 and 2008 customers must configure their server with collation corresponding to personal needs and requirements, but obligatorily case sensitive. For more information please refer to Microsoft SQL Server documentation page "Selecting a SQL Server Collation" here.

Note

Be aware that JCR does not support MyISAM storage engine for the MySQL relational database management system.
Each database software supports ANSI SQL standards but also has its own specifics. Therefore each database has its own configuration setting in the eXo JCR as a database dialect parameter. More detailed configuration of the database can be set by editing the metadata SQL-script files.
You can find SQL-scripts in conf/storage/ directory of the JPP_HOME/modules/org/gatein/lib/main/exo.jcr.component.core-1.15.1-CP01-redhat-1.jar file .
The following tables show the correspondence between the scripts and databases:

Table 36.2. Single-database

Database Script
MySQL DB jcr-sjdbc.mysql.sql
MySQL DB with utf-8 jcr-sjdbc.mysql-utf8.sql
PostgresSQL jcr-sjdbc.pqsql.sql
Oracle DB jcr-sjdbc.ora.sql
DB2 9.7 jcr-sjdbc.db2.sql
MS SQL Server jcr-sjdbc.mssql.sql
Sybase jcr-sjdbc.sybase.sql
HSQLDB jcr-sjdbc.sql

Table 36.3. Multi-database

Database Script
MySQL DB jcr-mjdbc.mysql.sql
MySQL DB with utf-8 jcr-mjdbc.mysql-utf8.sql
PostgresSQL jcr-mjdbc.pqsql.sql
Oracle DB jcr-mjdbc.ora.sql
DB2 9.7 jcr-mjdbc.db2.sql
MS SQL Server jcr-mjdbc.mssql.sql
Sybase jcr-mjdbc.sybase.sql
HSQLDB jcr-mjdbc.sql

If a non-ANSI node name is used, you must use a database with MultiLanguage support. Some JDBC drivers need additional parameters for establishing a Unicode friendly connection. For example under mysql it is necessary to add an additional parameter for the JDBC driver at the end of JDBC URL:
There are preconfigured configuration files for HSQLDB. Look for these files in /conf/portal and /conf/standalone folders of the jar-file exo.jcr.component.core-1.15.1-CP01-redhat-1.jar or source-distribution of eXo JCR implementation.

Example 36.1. Example Parameter

jdbc:mysql://exoua.dnsalias.net/portal?characterEncoding=utf8

The configuration files are located in service jars /conf/portal/configuration.xml (eXo services including JCR Repository Service) and exo-jcr-config.xml (repositories configuration) by default. In JBoss Portal Platform, the JCR is configured in portal web application portal/WEB-INF/conf/jcr/jcr-configuration.xml (JCR Repository Service and related services) and repository-configuration.xml (repositories configuration).

36.2. Multi-database Configuration

You need to configure each workspace in a repository as part of multi-database configuration. Databases may reside on remote servers as required.

Procedure 36.1. 

  1. Configure the data containers in the org.exoplatform.services.naming.InitialContextInitializer service. It's the JNDI context initializer which registers (binds) naming resources (DataSources) for data containers.
    For example (two data containers jdbcjcr - local HSQLDB, jdbcjcr1 - remote MySQL):
      <external-component-plugins>
        <target-component>org.exoplatform.services.naming.InitialContextInitializer</target-component>
        <component-plugin>
          <name>bind.datasource</name>
          <set-method>addPlugin</set-method>
          <type>org.exoplatform.services.naming.BindReferencePlugin</type>
          <init-params>
            <value-param>
              <name>bind-name</name>
              <value>jdbcjcr</value>
            </value-param>
            <value-param>
              <name>class-name</name>
              <value>javax.sql.DataSource</value>
            </value-param>
            <value-param>
              <name>factory</name>
              <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
            </value-param>
            <properties-param>
              <name>ref-addresses</name>
              <description>ref-addresses</description>
              <property name="driverClassName" value="${all.driverClassName:org.hsqldb.jdbcDriver}"/>
              <!-- MVCC configured to prevent possible deadlocks when a global Tx is active -->
              <property name="url" value="${jdbcjcr.url:jdbc:hsqldb:file:target/temp/data/portal;hsqldb.tx=mvcc}"/>
              <property name="username" value="${jdbcjcr.username:sa}"/>
              <property name="password" value="${jdbcjcr.password:}"/>
            </properties-param>
          </init-params>
        </component-plugin>
        <component-plugin>
          <name>bind.datasource</name>
          <set-method>addPlugin</set-method>
          <type>org.exoplatform.services.naming.BindReferencePlugin</type>
          <init-params>
            <value-param>
              <name>bind-name</name>
              <value>jdbcjcr1</value>
            </value-param>
            <value-param>
              <name>class-name</name>
              <value>javax.sql.DataSource</value>
            </value-param>
            <value-param>
              <name>factory</name>
              <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
            </value-param>
            <properties-param>
              <name>ref-addresses</name>
              <description>ref-addresses</description>
              <property name="driverClassName" value="${all.driverClassName:org.hsqldb.jdbcDriver}"/>
              <property name="url" value="${jdbcjcr1.url:jdbc:hsqldb:file:target/temp/data/jcr}"/>
              <property name="username" value="${jdbcjcr1.username:sa}"/>
              <property name="password" value="${jdbcjcr1.password:}"/>
            </properties-param>
          </init-params>
        </component-plugin>
        <!-- Unnecessary plugins not relevant to this section removed for clarity -->
      </external-component-plugins>
    
    1. Configure the database connection parameters:
      • driverClassName, e.g. "org.hsqldb.jdbcDriver", "com.mysql.jdbc.Driver", "org.postgresql.Driver"
      • url, e.g. "jdbc:hsqldb:file:target/temp/data/portal", "jdbc:mysql://exoua.dnsalias.net/jcr"
      • username, e.g. "sa", "exoadmin"
      • password, e.g. "", "exo12321"
    There can be connection pool configuration parameters (org.apache.commons.dbcp.BasicDataSourceFactory):
  2. Configure the repository service. Each workspace will be configured for its own data container.
    For example (two workspaces ws - jdbcjcr, ws1 - jdbcjcr1):
    <workspaces>
      <workspace name="ws" auto-init-root-nodetype="nt:unstructured">
        <container class="org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer">
        <properties>
          <property name="source-name" value="jdbcjcr"/>
          <property name="dialect" value="hsqldb"/>
          <property name="multi-db" value="true"/>
          <property name="max-buffer-size" value="200K"/>
          <property name="swap-directory" value="target/temp/swap/ws"/>   
        </properties>
        </container>
        <cache enabled="true">
          <properties>
            <property name="max-size" value="10K"/><!-- 10Kbytes -->
            <property name="live-time" value="30m"/><!-- 30 min -->
          </properties>
        </cache>
        <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
        <properties>
          <property name="index-dir" value="target/temp/index"/>
        </properties>
        </query-handler>
        <lock-manager>
        <time-out>15m</time-out><!-- 15 min -->
        <persister class="org.exoplatform.services.jcr.impl.core.lock.FileSystemLockPersister">
          <properties>
          <property name="path" value="target/temp/lock/ws"/>
          </properties>
        </persister>
        </lock-manager>
      </workspace>
      <workspace name="ws1" auto-init-root-nodetype="nt:unstructured">
        <container class="org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer">
        <properties>
          <property name="source-name" value="jdbcjcr1"/>
          <property name="dialect" value="mysql"/>
          <property name="multi-db" value="true"/>
          <property name="max-buffer-size" value="200K"/>
          <property name="swap-directory" value="target/temp/swap/ws1"/>   
        </properties>
        </container>
        <cache enabled="true">
          <properties>
            <property name="max-size" value="10K"/>
            <property name="live-time" value="5m"/>
          </properties>
        </cache>
        <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
        <properties>
          <property name="index-dir" value="target/temp/index"/>
        </properties>
        </query-handler>
        <lock-manager>
        <time-out>15m</time-out><!-- 15 min -->
        <persister class="org.exoplatform.services.jcr.impl.core.lock.FileSystemLockPersister">
          <properties>
          <property name="path" value="target/temp/lock/ws1"/>
          </properties>
        </persister>
        </lock-manager>
      </workspace>
    </workspaces>
    • source-name: A javax.sql.DataSource name configured in InitialContextInitializer component (was sourceName prior JCR 1.9);
    • dialect: A database dialect, one of hsqldb, mysql, mysql-utf8, pgsql, oracle, oracle-oci, mssql, sybase, derby, db2, db2v8 or auto for dialect autodetection;
    • multi-db: Enable multi-database container with this parameter (set value "true");
    • max-buffer-size: A a threshold (in bytes) after which a javax.jcr.Value content will be swapped to a file in a temporary storage. A swap for pending changes, for example.
    • swap-directory: A path in the file system used to swap the pending changes.
This procedure configures two workspace which will be persistent in two different databases (ws in HSQLDB and ws1 in MySQL).

36.3. Single-database Configuration

Configuring a single-database data container is easier than configuring a multi-database data container as only one naming resource must be configured.

Example 36.2. jdbcjcr Data Container

<external-component-plugins>
    <target-component>org.exoplatform.services.naming.InitialContextInitializer</target-component>
    <component-plugin>
        <name>bind.datasource</name>
        <set-method>addPlugin</set-method>
        <type>org.exoplatform.services.naming.BindReferencePlugin</type>
        <init-params>
          <value-param>
            <name>bind-name</name>
            <value>jdbcjcr</value>
          </value-param>
          <value-param>
            <name>class-name</name>
            <value>javax.sql.DataSource</value>
          </value-param>
          <value-param>
            <name>factory</name>
            <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
          </value-param>
          <properties-param>
            <name>ref-addresses</name>
            <description>ref-addresses</description>
            <property name="driverClassName" value="org.postgresql.Driver"/>
            <property name="url" value="jdbc:postgresql://exoua.dnsalias.net/portal"/>
            <property name="username" value="exoadmin"/>
            <property name="password" value="exo12321"/>
            <property name="maxActive" value="50"/>
            <property name="maxIdle" value="5"/>
            <property name="initialSize" value="5"/>
          </properties-param>
        </init-params>
    </component-plugin>
  </external-component-plugins>

Configure repository workspaces with this one database. The multi-db parameter must be set as false.
For example (two workspaces ws - jdbcjcr, ws1 - jdbcjcr):

Example 36.3. Example

<workspaces>
  <workspace name="ws" auto-init-root-nodetype="nt:unstructured">
    <container class="org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer">
    <properties>
      <property name="source-name" value="jdbcjcr"/>
      <property name="dialect" value="pgsql"/>
      <property name="multi-db" value="false"/>
      <property name="max-buffer-size" value="200K"/>
      <property name="swap-directory" value="target/temp/swap/ws"/>
    </properties>
    </container>
    <cache enabled="true">
    <properties>
      <property name="max-size" value="10K"/>
      <property name="live-time" value="30m"/>
    </properties>
    </cache>
    <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
    <properties>
      <property name="index-dir" value="../temp/index"/>
    </properties>
    </query-handler>
    <lock-manager>
    <time-out>15m</time-out>
    <persister class="org.exoplatform.services.jcr.impl.core.lock.FileSystemLockPersister">
      <properties>
      <property name="path" value="target/temp/lock/ws"/>
      </properties>
    </persister>
    </lock-manager>
  </workspace>
  <workspace name="ws1" auto-init-root-nodetype="nt:unstructured">
    <container class="org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer">
    <properties>
      <property name="source-name" value="jdbcjcr"/>
      <property name="dialect" value="pgsql"/>
      <property name="multi-db" value="false"/>
      <property name="max-buffer-size" value="200K"/>
      <property name="swap-directory" value="target/temp/swap/ws1"/>
    </properties>
    </container>
    <cache enabled="true">
    <properties>
      <property name="max-size" value="10K"/>
      <property name="live-time" value="5m"/>
    </properties>
    </cache>
    <lock-manager>
    <time-out>15m</time-out>
    <persister class="org.exoplatform.services.jcr.impl.core.lock.FileSystemLockPersister">
      <properties>
      <property name="path" value="target/temp/lock/ws1"/>
      </properties>
    </persister>
    </lock-manager>
  </workspace>
</workspaces>

This configures two persistent workspaces in one database (PostgreSQL).

36.3.1. Configuration without DataSource

It is possible to configure the repository without binding javax.sql.DataSource in the JNDI service if you have a dedicated JDBC driver implementation with special features like XA transactions, statements/connections pooling etc:

Procedure 36.2. 

  1. Remove the configuration in InitialContextInitializer for your database and configure a new one directly in the workspace container.
  2. Remove parameter source-name and add next lines instead. Describe your values for a JDBC driver, database URL and username.

Connection Pooling

Ensure the JDBC driver provides connection pooling. Connection pooling is strongly recommended for use with the JCR to prevent a database overload.
<workspace name="ws" auto-init-root-nodetype="nt:unstructured">
  <container class="org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer">
    <properties>
      <property name="dialect" value="hsqldb"/>
      <property name="driverliteral" value="org.hsqldb.jdbcDriver"/>
      <property name="url" value="jdbc:hsqldb:file:target/temp/data/portal"/>
      <property name="username" value="su"/>
      <property name="password" value=""/> 
      ......

36.3.2. Dynamic Workspace Creation

Workspaces can be added dynamically during runtime.
This can be performed in two steps:

Procedure 36.3. 

  1. ManageableRepository.configWorkspace(WorkspaceEntry wsConfig): Register a new configuration in RepositoryContainer and create a WorkspaceContainer.
  2. ManageableRepository.createWorkspace(String workspaceName): Creation a new workspace.

36.4. Simple and Complex queries

eXo JCR provides two ways to interact with the database;

JDBCStorageConnection
Which uses simple queries. Simple queries do not use sub queries, left or right joins. They are implemented in such a way as to support as many database dialects as possible.
CQJDBCStorageConection
Which uses complex queries. Complex queries are optimized to reduce the number of database calls.
Simple queries will be used if you chose org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer:
<workspaces>
  <workspace name="ws" auto-init-root-nodetype="nt:unstructured">
    <container class="org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer">
    ...
  </workspace>
</worksapces>
Complex queries will be used if you chose org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.CQJDBCWorkspaceDataContainer:
<workspaces>
  <workspace name="ws" auto-init-root-nodetype="nt:unstructured">
    <container class="org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.CQJDBCWorkspaceDataContainer">
    ...
  </workspace>
</worksapces>

36.5. Force Query Hints

Some databases, such as Oracle and MySQL, support hints to increase query performance. The eXo JCR has separate Complex Query implementations for the Orcale database dialect, which uses query hints to increase performance for few important queries.
To enable this option, use the following configuration property:
<workspace name="ws" auto-init-root-nodetype="nt:unstructured">
  <container class="org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer">
    <properties>
      <property name="dialect" value="oracle"/>
      <property name="force.query.hints" value="true" />
      ......
Query hints are only used for Complex Queries with the Oracle dialect. For all other dialects this parameter is ignored.

36.6. Notes for Microsoft Windows users

The current configuration of eXo JCR uses Apache DBCP connection pool (org.apache.commons.dbcp.BasicDataSourceFactory).
It is possible to set a high value for the maxActive parameter in the configuration.xml file. This creates a high use of TCP/IP ports from a client machine inside the pool (the JDBC driver, for example). As a result, the data container can throw exceptions like "Address already in use".
To solve this problem, you must configure the client's machine networking software to use shorter timeouts for open TCP/IP ports.
This is done by editing two registry keys within the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters node. Both of these keys are unset by default. To set the keys as required:

Procedure 36.4. 

  1. Set the MaxUserPort registry key to =dword:00001b58. This sets the maximum of open ports to 7000 or higher (the default is 5000).
  2. Set TcpTimedWaitDelay to =dword:0000001e. This sets TIME_WAIT parameter to 30 seconds (the default is 240).

Example 36.4. Sample Registry File

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters]
"MaxUserPort"=dword:00001b58
"TcpTimedWaitDelay"=dword:0000001e

Chapter 37. External Value Storages

37.1. Introduction

JCR values are stored in the Workspace Data container by default. The eXo JCR offers an additional option of storing JCR values separately from the Workspace Data container which can help keep Binary Large Objects (BLOBs) separate.
Tree-based storage is recommended in most cases.

37.2. Tree File Value Storage

Tree File Value Storage holds values in tree-like file system files. Path property points to the root directory to store the files.
This is a recommended type of external storage because it can contain large amount of files limited only by disk/volume free space.
However, using Tree File Value Storage can result in a higher time on value deletion, due to the removal of unused tree-nodes.

Example 37.1. Tree File Value Storage Configuration

<!-- Comment #1 -->
<value-storage id="Storage #1" 
class="org.exoplatform.services.jcr.impl.storage.value.fs.TreeFileValueStorage">
     <!-- Comment #2 -->
     <properties>
       <property name="path" value="data/values"/>
     </properties>
     <filters>
       <filter property-type="Binary" min-value-size="1M"/>
     </filters>
Comment #1: The id is the value storage unique identifier, used for linking with properties stored in a workspace container.
Comment #2: the path is a location where value files will be stored.

Each file value storage can have the filters for incoming values. A filter can match values by property-type, property-name, ancestor-path. It can also match the size of values stored (min-value-size) in bytes.
In the previous example a filter with property-type and min-value-size has been used. This results in storage for binary values with size greater of 1MB.
It is recommended that properties with large values are stored in file value storage only.
The example below shows a value storage with different locations for large files (min-value-size a 20Mb-sized filter).
A value storage uses ORed logic in the process of filter selection. This means the first filter in the list will be called first and if it is not matched the next will be called, and so on.
In this example a value matches the 20MB filter min-value-size and will be stored in the path "data/20Mvalues". All other filters will be stored in "data/values".
<value-storages>
  <value-storage id="Storage #1" class="org.exoplatform.services.jcr.impl.storage.value.fs.TreeFileValueStorage">
    <properties>
      <property name="path" value="data/20Mvalues"/>
    </properties>
    <filters>
      <filter property-type="Binary" min-value-size="20M"/>
    </filters>
  <value-storage>
  <value-storage id="Storage #2" class="org.exoplatform.services.jcr.impl.storage.value.fs.TreeFileValueStorage">
    <properties>
      <property name="path" value="data/values"/>
    </properties>
    <filters>
      <filter property-type="Binary" min-value-size="1M"/>
    </filters>
  <value-storage>
<value-storages>

37.3. Disabling value storage

The JCR allows you to disable value storage by adding the following property into its configuration.
<property name="enabled" value="false" />

Warning

It is recommended that this functionality be used for internal and testing purpose only, and with caution, as all stored values will be inaccessible.

Chapter 38. Workspace Data Container

Each Workspace of the JCR has its own persistent storage to hold that workspace's items data. The eXo JCR can be configured so that it can use one or more workspaces that are logical units of the repository content.
The physical data storage mechanism is configured using mandatory element container. The type of container is described in the attribute class = fully_qualified_name_of_org.exoplatform.services.jcr.storage.WorkspaceDataContainer_subclass.

Example 38.1. Physical Data Storage Configuration

<container class="org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer">
  <properties>
    <property name="source-name" value="jdbcjcr1"/>
    <property name="dialect" value="hsqldb"/>
    <property name="multi-db" value="true"/>
    <property name="max-buffer-size" value="200K"/>
    <property name="swap-directory" value="target/temp/swap/ws"/>
    <property name="lazy-node-iterator-page-size" value="50"/>
    <property name="acl-bloomfilter-false-positive-probability" value="0.1d"/>
    <property name="acl-bloomfilter-elements-number" value="1000000"/>
  </properties>
source-name: The JDBC data source name which is registered in JDNI by InitialContextInitializer. This was known as sourceName in versions prior to 1.9.
dialect: The database dialect. Must be one of the following: hsqldb, mysql, mysql-utf8, pgsql, oracle, oracle-oci, mssql, sybase, derby, db2 or db2v8).
multi-db: This parameter, if true, enables multi-database container.
max-buffer-size: A threshold in bytes. If a value size is greater than this setting, then it will be spooled to a temporary file.
swap-directory: A location where the value will be spooled if no value storage is configured but a max-buffer-size is exceeded.
lazy-node-iterator-page-size: "Lazy" child nodes iterator settings. Defines size of page, the number of nodes that are retrieved from persistent storage at once.
acl-bloomfilter-false-positive-probability: ACL Bloom-filter settings. ACL Bloom-filter desired false positive probability. Range [0..1]. Default value 0.1d.
acl-bloomfilter-elements-number: ACL Bloom-filter settings. Expected number of ACL-elements in the Bloom-filter. Default value 1000000.

Note

Bloom filters are not supported by all the cache implementations so far only the inplementation for infinispan supports it.
Bloom-filter used to avoid read nodes that definitely do not have ACL. acl-bloomfilter-false-positive-probability and acl-bloomfilter-elements-number used to configure such filters. Bloom filters are not supported by all the cache implementations so far only the inplementation for infinispan supports it.
More about Bloom filters you can read here http://en.wikipedia.org/wiki/Bloom_filter.
The eXo JCR has a JDBC-based, relational database, production ready Workspace Data Container.
Workspace Data Container may support external storages for javax.jcr.Value (which can be the case for BLOB values for example) using the optional element value-storages.
The Data Container will try to read or write a Value using the underlying value storage plug-in if the filter criteria (see below) match the current property.

Example 38.2. External Value Storage Configuration

<value-storages>
  <value-storage id="Storage #1" class="org.exoplatform.services.jcr.impl.storage.value.fs.TreeFileValueStorage">
    <properties>
      <property name="path" value="data/values"/>
    </properties>
    <filters>
     <filter property-type="Binary" min-value-size="1M"/><!-- Values large of 1Mbyte -->
    </filters>
.........
</value-storages>
value-storage is the subclass of org.exoplatform.services.jcr.storage.value.ValueStoragePlugin and properties are optional plug-in specific parameters.
filters: Each file value storage can have the filter(s) for incoming values. If there are several filter criteria, they all have to match (AND-Condition).

A filter can match values by property type (property-type), property name (property-name), ancestor path (ancestor-path) and/or the size of values stored (min-value-size, e.g. 1M, 4.2G, 100 (bytes)).
In a code sample, we use a filter with property-type and min-value-size only. That means that the storage is only for binary values whose size is greater than 1Mbyte.
It is recommended that you store properties with large values in a file value storage only.

Chapter 39. Configuring Cluster

39.1. Launching Cluster

39.1.1. Configuring JCR to use external configuration

  • To manually configure a repository, create a new configuration file (exo-jcr-configuration.xml for example). For details, see Chapter 33, JCR configuration.
    The configuration file must be formatted as follows:

    Example 39.1. External Configuration

    <repository-service default-repository="repository1">
       <repositories>
          <repository name="repository1" system-workspace="ws1" default-workspace="ws1">
             <security-domain>exo-domain</security-domain>
             <access-control>optional</access-control>
             <authentication-policy>org.exoplatform.services.jcr.impl.core.access.JAASAuthenticator</authentication-policy>
             <workspaces>
                <workspace name="ws1">
                   <container class="org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.CQJDBCWorkspaceDataContainer">
                      <properties>
                         <property name="source-name" value="jdbcjcr" />
                         <property name="dialect" value="oracle" />
                         <property name="multi-db" value="false" />
                         <property name="update-storage" value="false" />
                         <property name="max-buffer-size" value="200k" />
                         <property name="swap-directory" value="../temp/swap/production" />
                      </properties>
                      <value-storages>
                      <!-- Comment #1 -->
                      </value-storages>
                   </container>
                   <initializer class="org.exoplatform.services.jcr.impl.core.ScratchWorkspaceInitializer">
                      <properties>
                         <property name="root-nodetype" value="nt:unstructured" />
                      </properties>
                   </initializer>
                   <cache enabled="true" class="org.exoplatform.services.jcr.impl.dataflow.persistent.jbosscache.JBossCacheWorkspaceStorageCache">
                   <!-- Comment #2 -->     
                   </cache>
                   <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
                   <!-- Comment #3 -->
                   </query-handler>
                   <lock-manager class="org.exoplatform.services.jcr.impl.core.lock.jbosscache.CacheableLockManagerImpl">
                   <!-- Comment #4 --> 
                   </lock-manager>
                </workspace>
                <workspace name="ws2">
                            ...
                </workspace>
                <workspace name="wsN">
                            ...
                </workspace>
             </workspaces>
          </repository>
       </repositories>
    </repository-service>

  • Then, update RepositoryServiceConfiguration configuration in the exo-configuration.xml to reference your file:
    <component>
       <key>org.exoplatform.services.jcr.config.RepositoryServiceConfiguration</key>
       <type>org.exoplatform.services.jcr.impl.config.RepositoryServiceConfigurationImpl</type>
       <init-params>
          <value-param>
             <name>conf-path</name>
             <description>JCR configuration file</description>
             <value>exo-jcr-configuration.xml</value>
          </value-param>
       </init-params>
    </component>

39.2. Requirements

39.2.1. Environment requirements

  • Every node of the cluster must have the same mounted Network File System (NFS) with the read and write permissions on it.
  • Every node of cluster must use the same database.
  • The same Clusters on different nodes must have the same names.

    Example 39.2. Example

    If the Indexer cluster in the production workspace on the first node is named production_indexer_cluster, then indexer clusters in the production workspace on all other nodes must also be named production_indexer_cluster.

39.2.2. Configuration requirements

The configuration of every workspace in the repository must contain the following elements:

Example 39.3. Value Storage configuration

<value-storages>
   <value-storage id="system" class="org.exoplatform.services.jcr.impl.storage.value.fs.TreeFileValueStorage">
      <properties>
         <property name="path" value="/mnt/tornado/temp/values/production" />    <!--path within NFS where ValueStorage will hold it's data-->
      </properties>
      <filters>
         <filter property-type="Binary" />
      </filters>
   </value-storage>
</value-storages>

Example 39.4. Cache configuration

<cache enabled="true" class="org.exoplatform.services.jcr.impl.dataflow.persistent.jbosscache.JBossCacheWorkspaceStorageCache">
   <properties>
      <property name="jbosscache-configuration" value="jar:/conf/portal/test-jbosscache-data.xml" />     <!--    path to JBoss Cache configuration for data storage -->
      <property name="jgroups-configuration" value="jar:/conf/portal/udp-mux.xml" />                     <!--    path to JGroups configuration -->
      <property name="jbosscache-cluster-name" value="JCR_Cluster_cache_production" />                   <!--    JBoss Cache data storage cluster name -->
      <property name="jgroups-multiplexer-stack" value="true" />
   </properties>
</cache>

Example 39.5. Indexer configuration

<query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
   <properties>
      <property name="changesfilter-class" value="org.exoplatform.services.jcr.impl.core.query.jbosscache.JBossCacheIndexChangesFilter" />
      <property name="index-dir" value="/mnt/tornado/temp/jcrlucenedb/production" />                       <!--    path within NFS where ValueStorage will hold it's data -->
      <property name="jbosscache-configuration" value="jar:/conf/portal/test-jbosscache-indexer.xml" />    <!--    path to JBoss Cache configuration for indexer -->
      <property name="jgroups-configuration" value="jar:/conf/portal/udp-mux.xml" />                       <!--    path to JGroups configuration -->
      <property name="jbosscache-cluster-name" value="JCR_Cluster_indexer_production" />                   <!--    JBoss Cache indexer cluster name -->
      <property name="jgroups-multiplexer-stack" value="true" />
   </properties>
</query-handler>

Example 39.6. Lock Manager configuration

<lock-manager class="org.exoplatform.services.jcr.impl.core.lock.jbosscache.CacheableLockManagerImpl">
   <properties>
      <property name="time-out" value="15m" />
      <property name="jbosscache-configuration" value="jar:/conf/portal/test-jbosscache-lock.xml" />       <!--    path to JBoss Cache configuration for lock manager -->
      <property name="jgroups-configuration" value="jar:/conf/portal/udp-mux.xml" />                       <!--    path to JGroups configuration -->
      <property name="jgroups-multiplexer-stack" value="true" />
      <property name="jbosscache-cluster-name" value="JCR_Cluster_lock_production" />                      <!--    JBoss Cache locks cluster name -->
                     
      <property name="jbosscache-cl-cache.jdbc.table.name" value="jcrlocks_production"/>                   <!--    the name of the DB table where lock's data will be stored -->
      <property name="jbosscache-cl-cache.jdbc.table.create" value="true"/>
      <property name="jbosscache-cl-cache.jdbc.table.drop" value="false"/>
      <property name="jbosscache-cl-cache.jdbc.table.primarykey" value="jcrlocks_production_pk"/>
      <property name="jbosscache-cl-cache.jdbc.fqn.column" value="fqn"/>
      <property name="jbosscache-cl-cache.jdbc.node.column" value="node"/>
      <property name="jbosscache-cl-cache.jdbc.parent.column" value="parent"/>
      <property name="jbosscache-cl-cache.jdbc.datasource" value="jdbcjcr"/>
   </properties>
</lock-manager>

Chapter 40. Configuring JBoss Cache

40.1. Indexer, lock manager and data container configuration

Each mentioned component uses instances of the JBoss Cache product for caching in clustered environment. So every element has its own transport and has to be configured correctly. As usual, workspaces have similar configuration differing only in cluster-names (and, possibly, some other parameters). The simplest way to configure them is to define their own configuration files for each component in each workspace:
<property name="jbosscache-configuration" value="conf/standalone
        /test-jbosscache-lock-db1-ws1.xml" />
But if there are few workspaces, configuring them in such a way can be painful and hard-manageable. eXo JCR offers a template-based configuration for JBoss Cache instances. You can have one template for Lock Manager, one for Indexer and one for data container and use them in all the workspaces, defining the map of substitution parameters in a main configuration file. Just simply define ${jbosscache-<parameter name>} inside xml-template and list correct value in JCR configuration file just below "jbosscache-configuration", as shown:
Template:
...
<clustering mode="replication" clusterName="${jbosscache-cluster-name}">
  <stateRetrieval timeout="20000" fetchInMemoryState="false" />
...
and JCR configuration file:
...
<property name="jbosscache-configuration" value="jar:/conf/portal/jbosscache-lock.xml" />
<property name="jbosscache-cluster-name" value="JCR-cluster-locks-db1-ws" />
...

40.2. JGroups configuration

JGroups is used by JBoss Cache for network communications and transport in a clustered environment. If the property is defined in component configuration, it will be injected into the JBoss Cache instance on start up.
<property name="jgroups-configuration" value="your/path/to/modified-udp.xml" />
As outlined above, each component (lock manager, data container and query handler) for each workspace requires its own clustered environment. In other words, they have their own clusters with unique names.
Each cluster should, by default, perform multi-casts on a separate port. This configuration leads to much unnecessary overhead on cluster. This is why JGroups offers a multiplexer feature, providing ability to use one single channel for set of clusters.
The multiplexer reduces network overheads and increase performance and stability of application. To enable multiplexer stack, you should define appropriate configuration file (upd-mux.xml is pre-shipped one with eXo JCR) and set "jgroups-multiplexer-stack" into "true".
<property name="jgroups-configuration" value="jar:/conf/portal/udp-mux.xml" />
<property name="jgroups-multiplexer-stack" value="true" />

40.3. Sharing JBoss Cache instances

As a single JBoss Cache instance can be demanding on resources, and the default setup will have an instance each for the indexer, the lock manager and the data container on each workspace, an environment that uses multiple workspace may benefit from sharing a JBoss Cache instance between several instances of the same type (the lock manager instance, for example).
This feature is disabled by default and can be enabled at the component configuration level by setting the jbosscache-shareable property to true:
<property name="jbosscache-shareable" value="true" />
Once enabled, this feature will allow the JBoss Cache instance used by a component to be re-used by another components of the same type with the same JBoss Cache configuration (with the exception of the eviction configuration, which can differ).
This means that all the parameters of type jbosscache-<PARAM_NAME> must be identical between the components of same type of different workspaces.
Therefore, if you can use the same values for the parameters in each workspace, you only need three JBoss Cache instances (one instance each for the indexer, lock manager and data container) running at once. This can relieve resource stress significantly.

40.4. Shipped JBoss Cache configuration templates

The eXo JCR implementation is shipped with ready-to-use JBoss Cache configuration templates for JCR's components. They are located in JPP_HOME/gatein/gatein.ear/portal.war/WEB-INF/conf/jcr/jbosscache directory, inside either the cluster or local directory.

40.4.1. Data container template

The data container template is config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<jbosscache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:jboss:jbosscache-core:config:3.1">

   <locking useLockStriping="false" concurrencyLevel="50000" lockParentForChildInsertRemove="false"
      lockAcquisitionTimeout="20000" />

   <clustering mode="replication" clusterName="${jbosscache-cluster-name}">
      <stateRetrieval timeout="20000" fetchInMemoryState="false" />
      <jgroupsConfig multiplexerStack="jcr.stack" />
      <sync />
   </clustering>

   <!-- Eviction configuration -->
   <eviction wakeUpInterval="5000">
      <default algorithmClass="org.jboss.cache.eviction.LRUAlgorithm"
         actionPolicyClass="org.exoplatform.services.jcr.impl.dataflow.persistent.jbosscache.ParentNodeEvictionActionPolicy"
         eventQueueSize="1000000">
         <property name="maxNodes" value="1000000" />
         <property name="timeToLive" value="120000" />
      </default>
   </eviction>
</jbosscache>

40.4.2. Lock manager template

The lock manager template is lock-config.xml:
<jbosscache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:jboss:jbosscache-core:config:3.1">

   <locking useLockStriping="false" concurrencyLevel="500" lockParentForChildInsertRemove="false"
      lockAcquisitionTimeout="20000" />

   <loaders passivation="false" shared="true">
   <!-- All the data of the JCR locks needs to be loaded at startup -->
   <preload>
     <node fqn="/" />
  </preload>   
      <!--
For another cache-loader class you should use another template with
cache-loader specific parameters
-->
      <loader class="org.exoplatform.services.jcr.impl.core.lock.jbosscache.JDBCCacheLoader" async="false" fetchPersistentState="false"
         ignoreModifications="false" purgeOnStartup="false">
         <properties>
            cache.jdbc.table.name=${jbosscache-cl-cache.jdbc.table.name}
            cache.jdbc.table.create=${jbosscache-cl-cache.jdbc.table.create}
            cache.jdbc.table.drop=${jbosscache-cl-cache.jdbc.table.drop}
            cache.jdbc.table.primarykey=${jbosscache-cl-cache.jdbc.table.primarykey}
            cache.jdbc.fqn.column=${jbosscache-cl-cache.jdbc.fqn.column}
            cache.jdbc.fqn.type=${jbosscache-cl-cache.jdbc.fqn.type}
            cache.jdbc.node.column=${jbosscache-cl-cache.jdbc.node.column}
            cache.jdbc.node.type=${jbosscache-cl-cache.jdbc.node.type}
            cache.jdbc.parent.column=${jbosscache-cl-cache.jdbc.parent.column}
            cache.jdbc.datasource=${jbosscache-cl-cache.jdbc.datasource}
</properties>
      </loader>
   </loaders>
</jbosscache>

40.4.3. Query handler (indexer) template

The query handler template is called indexer-config.xml:
<jbosscache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:jboss:jbosscache-core:config:3.1">

   <locking useLockStriping="false" concurrencyLevel="500" lockParentForChildInsertRemove="false"
      lockAcquisitionTimeout="20000" />
   <!-- Configure the TransactionManager -->
   <transaction transactionManagerLookupClass="org.jboss.cache.transaction.JBossStandaloneJTAManagerLookup" />

   <clustering mode="replication" clusterName="${jbosscache-cluster-name}">
      <stateRetrieval timeout="20000" fetchInMemoryState="false" />
      <sync />
   </clustering>
</jbosscache>

Chapter 41. LockManager

The LockManager stores lock objects. It can lock or release objects as required. It is also responsible for removing stale locks.
The LockManager in JBoss Portal Platform is implemented with org.exoplatform.services.jcr.impl.core.lock.jbosscache.CacheableLockManagerImpl.
It is enabled by adding lock-manager-configuration to workspace-configuration.
For example:
<workspace name="ws">
   ...
   <lock-manager class="org.exoplatform.services.jcr.impl.core.lock.jbosscache.CacheableLockManagerImpl">
      <properties>
         <property name="time-out" value="15m" />
         ...
      </properties>
   </lock-manager>               
   ...
</workspace>

41.1. CacheableLockManagerImpl

CacheableLockManagerImpl stores lock objects in JBoss-cache (which implements JDBCCacheLoader to store locks in a database). This means its locks are replicable and can affect an entire cluster rather than just a single node.
The length of time LockManager allows a lock to remain in place can be configured with the "time-out" property.
The LockRemover thread periodically polls LockManager for locks that have passed the time-out limit and must be removed.
The time-out for LockRemover is set as follows (the default value is 30m):
<properties>
   <property name="time-out" value="10m" />
   ...
</properties>
There are a number of ways to configure CacheableLockManagerImpl. Each involves configuring JBoss Cache and JDBCCacheLoader.
Refer to http://community.jboss.org/wiki/JBossCacheJDBCCacheLoader for more information about JBoss Cache and JDBCCacheLoader.

41.1.1. Simple JBoss Cache Configuration

One method to configure the LockManager is to put a JBoss Cache configuration file path into CacheableLockManagerImpl.

Note

This is not the most efficient method for configuring the LockManager as it requires a JBoss Cache configuration file for each LockManager configuration in each workspace of each repository. The configuration set up can subsequently become quite difficult to manage.
This method is useful, however, if a single, specially configured LockManager is required.
The required configuration is shown in the example below:
<lock-manager class="org.exoplatform.services.jcr.impl.core.lock.jbosscache.CacheableLockManagerImpl">
   <properties>
     <property name="time-out" value="15m" />
     <property name="jbosscache-configuration" value="conf/standalone/cluster/test-jbosscache-lock-config.xml" />
   </properties>
</lock-manager>
Sample content of the jbosscache-lock-config.xml file specified in the jbosscache-configuration property is shown in the code example below.

Example 41.1. Sample Content of the jbosscache-lock-config.xml File

<?xml version="1.0" encoding="UTF-8"?>
<jbosscache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:jboss:jbosscache-core:config:3.2">

 <locking useLockStriping="false" concurrencyLevel="50000" lockParentForChildInsertRemove="false" lockAcquisitionTimeout="20000" />
 <!-- Comment #1 -->
 <clustering mode="replication" clusterName="JBoss-Cache-Lock-Cluster_Name">
  <stateRetrieval timeout="20000" fetchInMemoryState="false" nonBlocking="true" />
  <jgroupsConfig>

   <TCP bind_addr="127.0.0.1" start_port="9800" loopback="true" recv_buf_size="20000000" send_buf_size="640000" discard_incompatible_packets="true"
     max_bundle_size="64000" max_bundle_timeout="30" use_incoming_packet_handler="true" enable_bundling="false" use_send_queues="false" sock_conn_timeout="300"
     skip_suspected_members="true" use_concurrent_stack="true" thread_pool.enabled="true" thread_pool.min_threads="1" thread_pool.max_threads="25"
     thread_pool.keep_alive_time="5000" thread_pool.queue_enabled="false" thread_pool.queue_max_size="100" thread_pool.rejection_policy="run"
     oob_thread_pool.enabled="true" oob_thread_pool.min_threads="1" oob_thread_pool.max_threads="8" oob_thread_pool.keep_alive_time="5000"
     oob_thread_pool.queue_enabled="false" oob_thread_pool.queue_max_size="100" oob_thread_pool.rejection_policy="run" />
    <MPING timeout="2000" num_initial_members="2" mcast_port="34540" bind_addr="127.0.0.1" mcast_addr="224.0.0.1" />

   <MERGE2 max_interval="30000" min_interval="10000" />
   <FD_SOCK />
   <FD max_tries="5" shun="true" timeout="10000" />
   <VERIFY_SUSPECT timeout="1500" />
   <pbcast.NAKACK discard_delivered_msgs="true" gc_lag="0" retransmit_timeout="300,600,1200,2400,4800" use_mcast_xmit="false" />
   <UNICAST timeout="300,600,1200,2400,3600" />
   <pbcast.STABLE desired_avg_gossip="50000" max_bytes="400000" stability_delay="1000" />
   <pbcast.GMS join_timeout="5000" print_local_addr="true" shun="false" view_ack_collection_timeout="5000" view_bundling="true" />
   <FRAG2 frag_size="60000" />
   <pbcast.STREAMING_STATE_TRANSFER />
  <pbcast.FLUSH timeout="0" />

  </jgroupsConfig

  <sync />
 </clustering>

 <loaders passivation="false" shared="true">
  <preload>
   <node fqn="/" />
  </preload>
  <loader class="org.jboss.cache.loader.JDBCCacheLoader" async="false" fetchPersistentState="false" ignoreModifications="false" purgeOnStartup="false">
   <properties>
    <!-- Comment #2 -->
    cache.jdbc.table.name=jcrlocks_ws
    cache.jdbc.table.create=true
    cache.jdbc.table.drop=false
    cache.jdbc.table.primarykey=jcrlocks_ws_pk
    cache.jdbc.fqn.column=fqn
    <!-- Comment #3 -->
    cache.jdbc.fqn.type=VARCHAR(512)
    cache.jdbc.node.column=node
    <!-- Comment #3 -->
    cache.jdbc.node.type=<BLOB>  
    cache.jdbc.parent.column=parent
    cache.jdbc.datasource=jdbcjcr
   </properties>
  </loader>

 </loaders>

</jbosscache>
Comment #1: The cluster name at clustering mode="replication" clusterName="JBoss-Cache-Lock-Cluster_Name" must be unique;
Comment #2: The cache.jdbc.table.name must be unique per datasource.
Comment #3: The cache.jdbc.node.type and cache.jdbc.fqn.type parameters must be configured according to the database in use. Refer to the table below for information about data types.

Table 41.1. Data Types in Different Databases

DataBase name Node data type FQN data type
default BLOB VARCHAR(512)
HSSQL OBJECT VARCHAR(512)
MySQL LONGBLOB VARCHAR(512)
ORACLE BLOB VARCHAR2(512)
PostgreSQL bytea VARCHAR(512)
MSSQL VARBINARY(MAX) VARCHAR(512)
DB2 BLOB VARCHAR(512)
Sybase IMAGE VARCHAR(512)

41.1.2. Template JBoss Cache Configuration

Another method to configure LockManager is to use a JBoss Cache configuration template for all LockManagers.
Below is an example test-jbosscache-lock.xml template file:
<?xml version="1.0" encoding="UTF-8"?>
<jbosscache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:jboss:jbosscache-core:config:3.1">

   <locking useLockStriping="false" concurrencyLevel="50000" lockParentForChildInsertRemove="false"
      lockAcquisitionTimeout="20000" />

   <clustering mode="replication" clusterName="${jbosscache-cluster-name}">
      <stateRetrieval timeout="20000" fetchInMemoryState="false" />
      <jgroupsConfig multiplexerStack="jcr.stack" />
      <sync />
   </clustering>

   <loaders passivation="false" shared="true">
      <!-- All the data of the JCR locks needs to be loaded at startup -->
      <preload>
         <node fqn="/" />
      </preload>  
      <!--
      For another cache-loader class you should use another template with
      cache-loader specific parameters
      -->
      <loader class="org.jboss.cache.loader.JDBCCacheLoader" async="false" fetchPersistentState="false"
         ignoreModifications="false" purgeOnStartup="false">
         <!-- All the configurable parameters in this file are populated with templates which will be replaced with LockManager configuration parameters. -->
         <properties>         
            cache.jdbc.table.name=${jbosscache-cl-cache.jdbc.table.name}
            cache.jdbc.table.create=${jbosscache-cl-cache.jdbc.table.create}
            cache.jdbc.table.drop=${jbosscache-cl-cache.jdbc.table.drop}
            cache.jdbc.table.primarykey=${jbosscache-cl-cache.jdbc.table.primarykey}
            cache.jdbc.fqn.column=${jbosscache-cl-cache.jdbc.fqn.column}
            cache.jdbc.fqn.type=${jbosscache-cl-cache.jdbc.fqn.type}
            cache.jdbc.node.column=${jbosscache-cl-cache.jdbc.node.column}
            cache.jdbc.node.type=${jbosscache-cl-cache.jdbc.node.type}
            cache.jdbc.parent.column=${jbosscache-cl-cache.jdbc.parent.column}
            cache.jdbc.datasource=${jbosscache-cl-cache.jdbc.datasource}
         </properties>
      </loader>
   </loaders>
</jbosscache>
The parameters that will populate the above file are shown below:

Example 41.2. JBoss Cache Configuration Parameters

<lock-manager class="org.exoplatform.services.jcr.impl.core.lock.jbosscache.CacheableLockManagerImpl">
   <properties>
      <property name="time-out" value="15m" />
      <property name="jbosscache-configuration" value="test-jbosscache-lock.xml" />
      <!-- Comment #1 -->
      <property name="jgroups-configuration" value="udp-mux.xml" />
      <property name="jgroups-multiplexer-stack" value="true" />
      <property name="jbosscache-cluster-name" value="JCR-cluster-locks-ws" />
      <property name="jbosscache-cl-cache.jdbc.table.name" value="jcrlocks_ws" />
      <property name="jbosscache-cl-cache.jdbc.table.create" value="true" />
      <property name="jbosscache-cl-cache.jdbc.table.drop" value="false" />
      <property name="jbosscache-cl-cache.jdbc.table.primarykey" value="jcrlocks_ws_pk" />
      <!-- Comment #2 -->
      <property name="jbosscache-cl-cache.jdbc.fqn.column" value="fqn" />
      <property name="jbosscache-cl-cache.jdbc.fqn.type" value="AUTO"/>
      <property name="jbosscache-cl-cache.jdbc.node.column" value="node" />
      <!-- Comment #2 -->
      <property name="jbosscache-cl-cache.jdbc.node.type" value="AUTO"/>
      <property name="jbosscache-cl-cache.jdbc.parent.column" value="parent" />
      <property name="jbosscache-cl-cache.jdbc.datasource" value="jdbcjcr" />
   </properties>
</lock-manager>
Comment #1: The jgroups-configuration has been moved to a separate configuration file (udp-mux.xml, shown below). In this case the udp-mux.xml is a common configuration for all JGroup components (QueryHandler, cache, LockManager), but this is not a requirement of the configuration method.
Comment #2: The jbosscache-cl-cache.jdbc.fqn.column and jbosscache-cl-cache.jdbc.node.type parameters are not explicitly defined as cache.jdbc.fqn.type and cache.jdbc.node.type are defined in the JBoss Cache configuration.

Refer to Table 41.1, “Data Types in Different Databases” for information about setting these parameters or set them as AUTO and the data type will by detected automatically.
udp-mux.xml:
<protocol_stacks>
   <stack name="jcr.stack">
      <config>
         <UDP mcast_addr="228.10.10.10" mcast_port="45588" tos="8" ucast_recv_buf_size="20000000"
            ucast_send_buf_size="640000" mcast_recv_buf_size="25000000" mcast_send_buf_size="640000" loopback="false"
            discard_incompatible_packets="true" max_bundle_size="64000" max_bundle_timeout="30"
            use_incoming_packet_handler="true" ip_ttl="2" enable_bundling="true" enable_diagnostics="true"
            thread_naming_pattern="cl" use_concurrent_stack="true" thread_pool.enabled="true" thread_pool.min_threads="2"
            thread_pool.max_threads="8" thread_pool.keep_alive_time="5000" thread_pool.queue_enabled="true"
            thread_pool.queue_max_size="1000" thread_pool.rejection_policy="discard" oob_thread_pool.enabled="true"
            oob_thread_pool.min_threads="1" oob_thread_pool.max_threads="8" oob_thread_pool.keep_alive_time="5000"
            oob_thread_pool.queue_enabled="false" oob_thread_pool.queue_max_size="100" oob_thread_pool.rejection_policy="Run" />

         <PING timeout="2000" num_initial_members="3" />
         <MERGE2 max_interval="30000" min_interval="10000" />
         <FD_SOCK />
         <FD timeout="10000" max_tries="5" shun="true" />
         <VERIFY_SUSPECT timeout="1500" />
         <BARRIER />
         <pbcast.NAKACK use_stats_for_retransmission="false" exponential_backoff="150" use_mcast_xmit="true"
            gc_lag="0" retransmit_timeout="50,300,600,1200" discard_delivered_msgs="true" />
         <UNICAST timeout="300,600,1200" />
         <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000" max_bytes="1000000" />
         <VIEW_SYNC avg_send_interval="60000" />
         <pbcast.GMS print_local_addr="true" join_timeout="3000" shun="false" view_bundling="true" />
         <FC max_credits="500000" min_threshold="0.20" />
         <FRAG2 frag_size="60000" />
         <!--pbcast.STREAMING_STATE_TRANSFER /-->
         <pbcast.STATE_TRANSFER />
         <!-- pbcast.FLUSH  /-->
      </config>
   </stack>
</protocol_stacks>

41.1.3. Lock Migration

There are three options available:

Lock Migration Options

When new Shareable Cache feature is not going to be used and all locks should be kept after migration.

Procedure 41.1. 

  1. Ensure that the same lock tables are used in configuration
  2. Start the server
When new Shareable Cache feature is not going to be used and all locks should be removed after migration.

Procedure 41.2. 

  1. Ensure that the same lock tables used in configuration
  2. Start the sever WITH system property:
    -Dorg.exoplatform.jcr.locks.force.remove=true
    
  3. Stop the server
  4. Start the server WITHOUT system property:
    -Dorg.exoplatform.jcr.locks.force.remove
    
When new Shareable Cache feature will be used (in this case all locks are removed after migration).

Procedure 41.3. 

  1. Start the sever WITH system property:
    -Dorg.exoplatform.jcr.locks.force.remove=true
    
  2. Stop the server.
  3. Start the server WITHOUT system property:
    -Dorg.exoplatform.jcr.locks.force.remove
    
  4. Optional:

    Manually remove old tables for lock.

Chapter 42. Configuring QueryHandler

42.1. Indexing in clustered environment

JCR offers indexing strategies for clustered environments using the advantages of running in a single JVM or doing the best to use all resources available in cluster. JCR uses Lucene library as underlying search and indexing engine, but it has several limitations that greatly reduce possibilities and limits the usage of cluster advantages. That's why eXo JCR offers two strategies that are suitable for it's own usecases. They are clustered with shared index and clustered with local indexes. Each one has it's pros and cons.
Clustered implementation with local indexes combines in-memory buffer index directory with delayed file-system flushing. This index is called "Volatile" and it is invoked in searches also. Within some conditions volatile index is flushed to the persistent storage (file system) as new index directory. This allows to achieve great results for write operations.
Local Index Diagram

Figure 42.1. Local Index Diagram


As this implementation designed for clustered environment it has additional mechanisms for data delivery within cluster. Actual text extraction jobs done on the same node that does content operations (i.e. write operation). Prepared "documents" (Lucene term that means block of data ready for indexing) are replicated withing cluster nodes and processed by local indexes. So each cluster instance has the same index content. When new node joins the cluster it has no initial index, so it must be created. There are some supported ways of doing this operation. The simplest is to simply copy the index manually but this is not intended for use. If no initial index found JCR uses automated scenarios. They are controlled via configuration (see "index-recovery-mode" parameter) offering full re-indexing from database or copying from another cluster node.
For some reasons having a multiple index copies on each instance can be costly. So shared index can be used instead (see diagram below).
Shared Index Diagram

Figure 42.2. Shared Index Diagram


This indexing strategy combines advantages of in-memory index along with shared persistent index offering "near" real time search capabilities. This means that newly added content is accessible via search practically immediately. This strategy allows nodes to index data in their own volatile (in-memory) indexes, but persistent indexes are managed by single "coordinator" node only. Each cluster instance has a read access for shared index to perform queries combining search results found in own in-memory index also. Take in account that shared folder must be configured in your system environment (i.e. mounted NFS folder). But this strategy in some extremely rare cases can have a bit different volatile indexes within cluster instances for a while. In a few seconds they will be up2date.

42.2. Configuration

42.2.1. Query-handler configuration overview

Configuration example:
<workspace name="ws">
   <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
      <properties>
         <property name="index-dir" value="shareddir/index/db1/ws" />
         <property name="changesfilter-class"
            value="org.exoplatform.services.jcr.impl.core.query.jbosscache.JBossCacheIndexChangesFilter" />
         <property name="jbosscache-configuration" value="jbosscache-indexer.xml" />
         <property name="jgroups-configuration" value="udp-mux.xml" />
         <property name="jgroups-multiplexer-stack" value="true" />
         <property name="jbosscache-cluster-name" value="JCR-cluster-indexer-ws" />
         <property name="max-volatile-time" value="60" />
         <property name="rdbms-reindexing" value="true" />
         <property name="reindexing-page-size" value="1000" />
         <property name="index-recovery-mode" value="from-coordinator" />
         <property name="index-recovery-filter" value="org.exoplatform.services.jcr.impl.core.query.lucene.DocNumberRecoveryFilter" />
      </properties>
   </query-handler>
</workspace>

Table 42.1. Configuration properties

Property name Description
index-dir path to index
changesfilter-class template of JBoss-cache configuration for all query-handlers in repository
jbosscache-configuration template of JBoss-cache configuration for all query-handlers in repository
jgroups-configuration jgroups-configuration is template configuration for all components (search, cache, locks) [Add link to document describing template configurations]
jgroups-multiplexer-stack [TODO about jgroups-multiplexer-stack - add link to JBoss doc]
jbosscache-cluster-name cluster name (must be unique)
max-volatile-time max time to live for Volatile Index
rdbms-reindexing indicate that need to use rdbms reindexing mechanism if possible, the default value is true
reindexing-page-size maximum amount of nodes which can be retrieved from storage for re-indexing purpose, the default value is 100
index-recovery-mode If the parameter has been set to from-indexing, so a full indexing will be automatically launched (default behavior), if the parameter has been set to from-coordinator, the index will be retrieved from coordinator
index-recovery-filter Defines implementation class or classes of RecoveryFilters, the mechanism of index synchronization for Local Index strategy.
async-reindexing Controls the process of re-indexing on JCR's startup. If this flag is set, indexing will be launched asynchronously, without blocking the JCR. Default is "false".

Improving Query Performance With postgreSQL and rdbms-reindexing
If you use postgreSQL and rdbms-reindexing is set to true, the performance of the queries used while indexing can be improved by:

Procedure 42.1. 

  1. Set the parameter "enable_seqscan" to "off"
    OR
    Set "default_statistics_target" to at least "50".
  2. Restart DB server and make analyze of the JCR_SVALUE (or JCR_MVALUE) table.
Improving Query Performance With DB2 and rdbms-reindexing
If you use DB2 and rdbms-reindexing is set to true, the performance of the queries used while indexing can be improved by:

Procedure 42.2. 

  • Make statistics on tables by running the following for JCR_SITEM (or JCR_MITEM) and JCR_SVALUE (or JCR_MVALUE) tables:
    RUNSTATS ON TABLE <scheme>.<table> WITH DISTRIBUTION AND INDEXES ALL

42.2.2. Cluster-ready indexing

For both cluster-ready implementations JBoss Cache, JGroups and Changes Filter values must be defined. Shared index requires some kind of remote or shared file system to be attached in a system (i.e. NFS, SMB or etc). Indexing directory ("indexDir" value) must point to it. Setting "changesfilter-class" to "org.exoplatform.services.jcr.impl.core.query.jbosscache.JBossCacheIndexChangesFilter" will enable shared index implementation.
<workspace name="ws">
   <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
      <properties>
         <property name="index-dir" value="/mnt/nfs_drive/index/db1/ws" />
         <property name="changesfilter-class"
            value="org.exoplatform.services.jcr.impl.core.query.jbosscache.JBossCacheIndexChangesFilter" />
         <property name="jbosscache-configuration" value="jbosscache-indexer.xml" />
         <property name="jgroups-configuration" value="udp-mux.xml" />
         <property name="jgroups-multiplexer-stack" value="true" />
         <property name="jbosscache-cluster-name" value="JCR-cluster-indexer-ws" />
         <property name="max-volatile-time" value="60" />
         <property name="rdbms-reindexing" value="true" />
         <property name="reindexing-page-size" value="1000" />
         <property name="index-recovery-mode" value="from-coordinator" />
      </properties>
   </query-handler>
</workspace>
In order to use cluster-ready strategy based on local indexes, when each node has own copy of index on local file system, the following configuration must be applied. Indexing directory must point to any folder on local file system and "changesfilter-class" must be set to "org.exoplatform.services.jcr.impl.core.query.jbosscache.LocalIndexChangesFilter".
<workspace name="ws">
   <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
      <properties>
         <property name="index-dir" value="/mnt/nfs_drive/index/db1/ws" />
         <property name="changesfilter-class"
            value="org.exoplatform.services.jcr.impl.core.query.jbosscache.LocalIndexChangesFilter" />
         <property name="jbosscache-configuration" value="jbosscache-indexer.xml" />
         <property name="jgroups-configuration" value="udp-mux.xml" />
         <property name="jgroups-multiplexer-stack" value="true" />
         <property name="jbosscache-cluster-name" value="JCR-cluster-indexer-ws" />
         <property name="max-volatile-time" value="60" />
         <property name="rdbms-reindexing" value="true" />
         <property name="reindexing-page-size" value="1000" />
         <property name="index-recovery-mode" value="from-coordinator" />
      </properties>
   </query-handler>
</workspace>

42.2.3. Local Index Recovery Filters

A common usecase for all cluster-ready applications is a hot joining and leaving of processing units. All nodes that are joining a cluster for the first time or nodes joining after some downtime, must be in a synchronized state.
When using shared value storages, databases and indexes, cluster nodes are synchronized at any given time. But is not the case when a local index strategy is used.
If a new node joins a cluster, without an index it is retrieved or recreated. Nodes can be also be restarted and thus the index is not empty. By default, even though the existing index is thought to be up to date, it can be outdated.
The JBoss Portal Platform JCR offers a mechanism called RecoveryFilters that will automatically retrieve index for the joining node on start up. This feature is a set of filters that can be defined via QueryHandler configuration:
<property name="index-recovery-filter" value="org.exoplatform.services.jcr.impl.core.query.lucene.DocNumberRecoveryFilter" />
Filter numbers are not limited so they can be combined:
<property name="index-recovery-filter" value="org.exoplatform.services.jcr.impl.core.query.lucene.DocNumberRecoveryFilter" />
    <property name="index-recovery-filter" value="org.exoplatform.services.jcr.impl.core.query.lucene.SystemPropertyRecoveryFilter" />
If any one returns fires, the index is re-synchronized. This feature uses standard index recovery mode defined by previously described parameter (can be "from-indexing" (default) or "from-coordinator")
<property name="index-recovery-mode" value="from-coordinator" />
There are multiple filter implementations:

org.exoplatform.services.jcr.impl.core.query.lucene.DummyRecoveryFilter

Always returns true, for cases when index must be force resynchronized (recovered) each time.
org.exoplatform.services.jcr.impl.core.query.lucene.SystemPropertyRecoveryFilter
Returns value of system property "org.exoplatform.jcr.recoveryfilter.forcereindexing". So index recovery can be controlled from the top without changing documentation using system properties.
org.exoplatform.services.jcr.impl.core.query.lucene.ConfigurationPropertyRecoveryFilter
Returns value of QueryHandler configuration property "index-recovery-filter-forcereindexing". So index recovery can be controlled from configuration separately for each workspace. For example:
<property name="index-recovery-filter" value="org.exoplatform.services.jcr.impl.core.query.lucene.ConfigurationPropertyRecoveryFilter" />
    <property name="index-recovery-filter-forcereindexing" value="true" />
org.exoplatform.services.jcr.impl.core.query.lucene.DocNumberRecoveryFilter
Checks the number of documents in index on coordinator side and self-side. It returns true if the count differs.
The advantage of this filter compared to others, is that it will skip reindexing for workspaces where the index was not modified.
For example; if there is ten repositories with three workspaces in each and only one is heavily used in the cluster, this filter will only reindex those workspaces that have been changed, without affecting other indexes.
This greatly reduces start up time.

42.2.4. JBoss-Cache template configuration

JBoss-Cache template configuration for query handler is about the same for both clustered strategies.

Example 42.1. jbosscache-indexer.xml

<?xml version="1.0" encoding="UTF-8"?>
<jbosscache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:jboss:jbosscache-core:config:3.1">
   <locking useLockStriping="false" concurrencyLevel="50000" lockParentForChildInsertRemove="false"
      lockAcquisitionTimeout="20000" />
   <!-- Configure the TransactionManager -->
   <transaction transactionManagerLookupClass="org.jboss.cache.transaction.JBossStandalone
            JTAManagerLookup" />
   <clustering mode="replication" clusterName="${jbosscache-cluster-name}">
      <stateRetrieval timeout="20000" fetchInMemoryState="false" />
      <jgroupsConfig multiplexerStack="jcr.stack" />
      <sync />
   </clustering>
   <!-- Eviction configuration -->
   <eviction wakeUpInterval="5000">
      <default algorithmClass="org.jboss.cache.eviction.FIFOAlgorithm" eventQueueSize="1000000">
         <property name="maxNodes" value="10000" />
         <property name="minTimeToLive" value="60000" />
      </default>
   </eviction>
</jbosscache>

Read more about template configurations Chapter 40, Configuring JBoss Cache.

42.3. Asynchronous Re-indexing

Managing a large data set using a JCR in a production environment at times requires special operations with Indexes, stored on File System. One of those maintenance operations is a recreation of it. Also called "re-indexing". There are various usecases when it's important to do. They include hardware faults, hard restarts, data-corruption, migrations and JCR updates that brings new features related to index. Usually index re-creation requested on server's startup or in runtime.

42.3.1. On startup indexing

A common usecase for updating and re-creating the index is to stop the server and manually remove indexes for workspaces requiring it. When the server is re-started, the missing indexes are automatically recovered by re-indexing.
The eXo JCR Supports direct RDBMS re-indexing, which can be faster than ordinary and can be configured via QueryHandler parameter rdbms-reindexing set to true.
A new feature is asynchronous indexing on startup. Usually startup is blocked until the indexing process is finished. This block can take any period of time, depending on amount of data persisted in repositories. But this can be resolved by using an asynchronous approaches of startup indexation.
Essentially, all indexing operations are performed in the background without blocking the repository. This is controlled by the value of the async-reindexing parameter in QueryHandler configuration.
With asynchronous indexation active, the JCR starts with no active indexes present. Queries on JCR still can be executed without exceptions, but no results will be returned until index creation completed.
The index state check is accomplished via QueryManagerImpl:
The OFFLINE state means that the index is currently re-creating. When the state is changed, a corresponding log event is printed. When the background index task starts the index is switched to OFFLINE, with following log event :
[INFO] Setting index OFFLINE (repository/production[system]).
When the indexing process is finished, the following two events are logged :
[INFO] Created initial index for 143018 nodes (repository/production[system]).
[INFO] Setting index ONLINE (repository/production[system]).
Those two log lines indicates the end of process for workspace given in brackets. Calling isOnline() as mentioned above, will also return true.

42.3.2. Hot Asynchronous Workspace Re-indexing using JMX

Some hard system faults, errors during upgrades, migration issues and some other factors may corrupt the index. Current versions of JCR supports Hot Asynchronous Workspace Reindexing feature. It allows Service Administrators to launch the process in background without stopping or blocking the whole application by using any JMX-compatible console.
JMX Jconsole

Figure 42.3. JMX Jconsole


The server can continue working as expected while the index is recreated.
This depends on the flag allow queries being passed via JMX interface to the reindex operation invocation. If the flag is set, the application continues working.
However, there is one critical limitation users must be aware of; the index is frozen while the background task is running.
This means that queries are performed on a version of the index present at the moment the indexing task is started, and that data written into the repository after startup will not be available through the search until process completes.
Data added during re-indexation is also indexed, but will be available only when reindexing is complete. The JCR makes a snapshot of indexes at the invocation of the asynchronous indexing task and uses that snapshot for searches.
When the operation is finished, the stale index is replaced by the newly created index, which included any newly added data.
If the allow queries flag is set to false, then all queries will throw an exception while task is running. The current state can be acquired using the following JMX operation:
  • getHotReindexingState() - returns information about latest invocation: start time, if in progress or finish time if done.

42.3.3. Notices

Hot re-indexing via JMX cannot be launched if the index is already in offline mode. This means that the index is currently involved in some other operations, such as re-indexing at startup, copying in cluster to another node or whatever.
Also; Hot Asynchronous Reindexing via JMX and on startup reindexing are different features. So you can't get the state of startup reindexing using command getHotReindexingState in JMX interface, but there are some common JMX operations:
  • getIOMode - returns current index IO mode (READ_ONLY / READ_WRITE), belongs to clustered configuration states;
  • getState - returns current state: ONLINE / OFFLINE.

42.4. Advanced tuning

42.4.1. Lucene tuning

As mentioned, JCR Indexing is based on the Lucene indexing library as the underlying search engine. It uses Directories to store index and manages access to index by Lock Factories.
By default, the JCR implementation uses optimal combination of Directory implementation and Lock Factory implementation.
The SimpleFSDirectory is used in Windows environments and the NIOFSDirectory implementation is used in non-Windows systems.
NativeFSLockFactory is an optimal solution for a wide variety of cases including clustered environment with NFS shared resources.
But those defaults can be overridden in the system properties.
Two properties: org.exoplatform.jcr.lucene.store.FSDirectoryLockFactoryClass and org.exoplatform.jcr.lucene.FSDirectory.class control (and change) the default behavior.
The first defines the implementation of abstract Lucene LockFactory class and the second sets implementation class for FSDirectory instances.
For more information, refer to the Lucene documentation. But be careful, for while the JCR allows users to change implementation classes of Lucene internals, it does not guarantee the stability and functionality of those changes.

Chapter 43. JBossTransactionsService

43.1. Introduction

JBossTransactionsService implements eXo TransactionService and provides access to JBoss Transaction Service (JBossTS) JTA implementation via eXo container dependency.
TransactionService used in JCR cache org.exoplatform.services.jcr.impl.dataflow.persistent.jbosscache.JBossCacheWorkspaceStorageCache implementation.

43.2. Configuration

Example configuration:
  <component>
    <key>org.exoplatform.services.transaction.TransactionService</key>
    <type>org.exoplatform.services.transaction.jbosscache.JBossTransactionsService</type>
    <init-params>
      <value-param>
        <name>timeout</name>
        <value>3000</value>
      </value-param>
    </init-params>   
  </component>
timeout - XA transaction timeout in seconds

Chapter 44. JCR Query Use-cases

44.1. Introduction

The JCR supports two query languages; JCR and XPath. A query, whether XPath or SQL, specifies a subset of nodes within a workspace, called the result set. The result set constitutes all the nodes in the workspace that meet the constraints stated in the query.

44.2. Query Lifecycle

44.2.1. Query Creation and Execution

Example 44.1. SQL

// get QueryManager
QueryManager queryManager = workspace.getQueryManager(); 
// make SQL query
Query query = queryManager.createQuery("SELECT * FROM nt:base ", Query.SQL);
// execute query
QueryResult result = query.execute();

Example 44.2. XPath

// get QueryManager
QueryManager queryManager = workspace.getQueryManager(); 
// make XPath query
Query query = queryManager.createQuery("//element(*,nt:base)", Query.XPATH);
// execute query
QueryResult result = query.execute();

44.2.2. Query Result Processing

// fetch query result
QueryResult result = query.execute();
To fetch the nodes:
NodeIterator it = result.getNodes();
The results can be formatted in a table:
// get column names
String[] columnNames = result.getColumnNames();
// get column rows
RowIterator rowIterator = result.getRows();
while(rowIterator.hasNext()){
   // get next row
   Row row = rowIterator.nextRow();
   // get all values of row
   Value[] values = row.getValues();
}

44.2.3. Scoring

The result returns a score for each row in the result set. The score contains a value that indicates a rating of how well the result node matches the query. A high value means a better matching than a low value. This score can be used for ordering the result.
eXo JCR Scoring is a mapping of Lucene scoring. For a more in-depth understanding, please study Lucene documentation.
The jcr:score is calculated as; (lucene score)*1000f.

44.3. Tips and tricks

44.3.1. XPath queries containing node names starting with a number

If you execute an XPath request like this...
// get QueryManager
QueryManager queryManager = workspace.getQueryManager(); 
// make XPath query
Query query = queryManager.createQuery("/jcr:root/Documents/Publie/2010//element(*, exo:article)", Query.XPATH);
...you will receive an Invalid request error. This is because XML (and thus XPath) does not allow names starting with a number.
Therefore, XPath requests using a node name that starts with a number are invalid.
Some possible alternatives are:
  • Use an SQL request.
  • Use escaping:
    // get QueryManager
    QueryManager queryManager = workspace.getQueryManager(); 
    // make XPath query
    Query query = queryManager.createQuery("/jcr:root/Documents/Publie/_x0032_010//element(*, exo:article)", Query.XPATH);

Chapter 45. Searching Repository Content

45.1. Introduction

You can find the JCR configuration file here: JPP_DIST/gatein/gatein.ear/portal.war/portal/WEB-INF/conf/jcr/repository-configuration.xml.
Please refer to Chapter 35, Configuring Search for more information about index configuration.

45.2. Bi-directional RangeIterator

QueryResult.getNodes() will return bi-directional NodeIterator implementation.

Note

Bi-directional NodeIterator is not supported in two cases:
  1. SQL query: select * from nt:base
  2. XPath query: //* .
TwoWayRangeIterator interface:
/**
 * Skip a number of elements in the iterator.
 * 
 * @param skipNum the non-negative number of elements to skip
 * @throws java.util.NoSuchElementException if skipped past the first element
 *           in the iterator.
 */
public void skipBack(long skipNum);
Usage:
NodeIterator iter = queryResult.getNodes();
while (iter.hasNext()) {
  if (skipForward) {
    iter.skip(10); // Skip 10 nodes in forward direction
  } else if (skipBack) {
    TwoWayRangeIterator backIter = (TwoWayRangeIterator) iter; 
    backIter.skipBack(10); // Skip 10 nodes back 
  }
  .......
}

45.3. Fuzzy Searches

The JBoss Portal Platform JCR supports features such as Lucene Fuzzy Searches. To perform a fuzzy search, form your query like the one below:
QueryManager qman = session.getWorkspace().getQueryManager();
Query q = qman.createQuery("select * from nt:base where contains(field, 'ccccc~')", Query.SQL);
QueryResult res = q.execute();

45.4. SynonymSearch

Searching with synonyms is integrated in the jcr:contains() function and uses the same syntax as synonym searches in web search engines (Google, for example). If a search term is prefixed by a tilde symbol ( ~ ), synonyms of the search term are taken into consideration. For example:
SQL: select * from nt:resource where contains(., '~parameter')

XPath: //element(*, nt:resource)[jcr:contains(., '~parameter')
This feature is disabled by default and you need to add a configuration parameter to the query-handler element in your JCR configuration file to enable it.
<param  name="synonymprovider-config-path" value="..you path to configuration file....."/>
<param  name="synonymprovider-class" value="org.exoplatform.services.jcr.impl.core.query.lucene.PropertiesSynonymProvider"/>
/**
 * <code>SynonymProvider</code> defines an interface for a component that
 * returns synonyms for a given term.
 */
public interface SynonymProvider {

   /**
    * Initializes the synonym provider and passes the file system resource to
    * the synonym provider configuration defined by the configuration value of
    * the <code>synonymProviderConfigPath</code> parameter. The resource may be
    * <code>null</code> if the configuration parameter is not set.
    *
    * @param fsr the file system resource to the synonym provider
    *            configuration.
    * @throws IOException if an error occurs while initializing the synonym
    *                     provider.
    */
   public void initialize(InputStream fsr) throws IOException;

   /**
    * Returns an array of terms that are considered synonyms for the given
    * <code>term</code>.
    *
    * @param term a search term.
    * @return an array of synonyms for the given <code>term</code> or an empty
    *         array if no synonyms are known.
    */
   public String[] getSynonyms(String term);
}

45.5. Highlighting

An ExcerptProvider retrieves text excerpts for a node in the query result and marks up the words in the text that match the query terms.
By default, match highlighting is disabled because as it requires that additional information is written to the search index.
To enable this feature, you need to add a configuration parameter to the query-handler element in your JCR configuration file:
<param name="support-highlighting" value="true"/>
Additionally, there is a parameter that controls the format of the excerpt created. In JCR 1.9, the default is set to org.exoplatform.services.jcr.impl.core.query.lucene.DefaultHTMLExcerpt. The configuration parameter for this setting is:
<param name="excerptprovider-class" value="org.exoplatform.services.jcr.impl.core.query.lucene.DefaultXMLExcerpt"/>

45.5.1. DefaultXMLExcerpt

This excerpt provider creates an XML fragment of the following form:
<excerpt>
    <fragment>
        <highlight>exoplatform</highlight> implements both the mandatory
        XPath and optional SQL <highlight>query</highlight> syntax.
    </fragment>
    <fragment>
        Before parsing the XPath <highlight>query</highlight> in
        <highlight>exoplatform</highlight>, the statement is surrounded
    </fragment>
</excerpt>

45.5.2. DefaultHTMLExcerpt

This excerpt provider creates an HTML fragment of the following form:
<div>
    <span>
        <strong>exoplatform</strong> implements both the mandatory XPath
        and optional SQL <strong>query</strong> syntax.
    </span>
    <span>
        Before parsing the XPath <strong>query</strong> in
        <strong>exoplatform</strong>, the statement is surrounded
    </span>
</div>

45.5.3. Usage

If you are using XPath, you must use the rep:excerpt() function in the last location step, just like you would select properties:
QueryManager qm = session.getWorkspace().getQueryManager();
Query q = qm.createQuery("//*[jcr:contains(., 'exoplatform')]/(@Title|rep:excerpt(.))", Query.XPATH);
QueryResult result = q.execute();
for (RowIterator it = result.getRows(); it.hasNext(); ) {
   Row r = it.nextRow();
   Value title = r.getValue("Title");
   Value excerpt = r.getValue("rep:excerpt(.)");
}
The above code searches for nodes that contain the word exoplatform and then gets the value of the Title property and an excerpt for each resultant node.
It is also possible to use a relative path in the call Row.getValue() while the query statement still remains the same. Also, you may use a relative path to a string property. The returned value will then be an excerpt based on string value of the property.
Both available excerpt providers will create fragments of about 150 characters and up to three fragments.
In SQL, the function is called excerpt() without the rep prefix, but the column in the RowIterator will nonetheless be labelled rep:excerpt(.).
QueryManager qm = session.getWorkspace().getQueryManager();
Query q = qm.createQuery("select excerpt(.) from nt:resource where contains(., 'exoplatform')", Query.SQL);
QueryResult result = q.execute();
for (RowIterator it = result.getRows(); it.hasNext(); ) {
   Row r = it.nextRow();
   Value excerpt = r.getValue("rep:excerpt(.)");
}

45.6. SpellChecker

The lucene based query handler implementation supports a pluggable spell-checker mechanism. By default, spell checking is not available, it must be configured first.
Information about the spellCheckerClass parameter is available in Chapter 35, Configuring Search.
The JCR currently provides an implementation class which uses the lucene-spellchecker.
The dictionary is derived from the fulltext, indexed content of the workspace and updated periodically. You can configure the refresh interval by picking one of the available inner classes of org.exoplatform.services.jcr.impl.core.query.lucene.spell.LuceneSpellChecker:
  • OneMinuteRefreshInterval
  • FiveMinutesRefreshInterval
  • ThirtyMinutesRefreshInterval
  • OneHourRefreshInterval
  • SixHoursRefreshInterval
  • TwelveHoursRefreshInterval
  • OneDayRefreshInterval
For example, if you want a refresh interval of six hours, the class name would be; org.exoplatform.services.jcr.impl.core.query.lucene.spell.LuceneSpellChecker$SixHoursRefreshInterval.
If you use org.exoplatform.services.jcr.impl.core.query.lucene.spell.LuceneSpellChecker, the refresh interval will be one hour.
The spell checker dictionary is stored as a lucene index under <index-dir>/spellchecker. If this index does not exist, a background thread will create it on start up. Similarly, the dictionary refresh is also done in a background thread so as not to block regular queries.

45.6.1. Usage

You can spell check a fulltext statement either with an XPath or a SQL query:
// rep:spellcheck('explatform') will always evaluate to true
Query query = qm.createQuery("/jcr:root[rep:spellcheck('explatform')]/(rep:spellcheck())", Query.XPATH);
RowIterator rows = query.execute().getRows();
// the above query will always return the root node no matter what string we check
Row r = rows.nextRow();
// get the result of the spell checking
Value v = r.getValue("rep:spellcheck()");
if (v == null) {
   // no suggestion returned, the spelling is correct or the spell checker
   // does not know how to correct it.
} else {
   String suggestion = v.getString();
}
And the same using SQL:
// SPELLCHECK('exoplatform') will always evaluate to true
Query query = qm.createQuery("SELECT rep:spellcheck() FROM nt:base WHERE jcr:path = '/' AND SPELLCHECK('explatform')", Query.SQL);
RowIterator rows = query.execute().getRows();
// the above query will always return the root node no matter what string we check
Row r = rows.nextRow();
// get the result of the spell checking
Value v = r.getValue("rep:spellcheck()");
if (v == null) {
   // no suggestion returned, the spelling is correct or the spell checker
   // does not know how to correct it.
} else {
   String suggestion = v.getString();
}

45.7. Similarity

Starting with version, 1.12 JCR allows you to search for nodes that are similar to an existing node.
Similarity is determined by looking up terms that are common to nodes. There are some conditions that must be met for a term to be considered. This is required to limit the number possibly relevant terms.
To be considered, terms must:
  • Be at least four characters long.
  • Occur at least twice in the source node.
  • Occur in at least five other nodes.

Note

The similarity function requires that the support Hightlighting is enabled. Please make sure that you have the following parameter set for the query handler in your workspace.xml.
<param name="support-highlighting" value="true"/>
The functions (rep:similar() in XPath and similar() in SQL) have two arguments:

relativePath
A relative path to a descendant node or a period (.) for the current node.
absoluteStringPath
A string literal that contains the path to the node for which to find similar nodes.

Warning

Relative path is not supported yet.

Example 45.1. Example

//element(*, nt:resource)[rep:similar(., '/parentnode/node.txt/jcr:content')]
Finds nt:resource nodes, which are similar to node by path /parentnode/node.txt/jcr:content.

Chapter 46. Full Text Search And Affecting Settings

Property content indexing
Each property of a node (if it is indexable) is processed with the Lucene analyzer and stored in the Lucene index. This is called indexing of a property. It allows fulltext searching of these indexed properties.

46.1. Lucene Analyzers

The purpose of analyzers is to transform all strings stored in the index into a well-defined condition. The same analyzer(s) is/are used when searching in order to adapt the query string to the index reality.
Therefore, performing the same query using different analyzers can return different results.
The example below illustrates how the same string is transformed by different analyzers.

Table 46.1. "The quick brown fox jumped over the lazy dogs"

Analyzer Parsed
org.apache.lucene.analysis.WhitespaceAnalyzer [The] [quick] [brown] [fox] [jumped] [over] [the] [lazy] [dogs]
org.apache.lucene.analysis.SimpleAnalyzer [the] [quick] [brown] [fox] [jumped] [over] [the] [lazy] [dogs]
org.apache.lucene.analysis.StopAnalyzer [quick] [brown] [fox] [jumped] [over] [lazy] [dogs]
org.apache.lucene.analysis.standard.StandardAnalyzer [quick] [brown] [fox] [jumped] [over] [lazy] [dogs]
org.apache.lucene.analysis.snowball.SnowballAnalyzer [quick] [brown] [fox] [jump] [over] [lazi] [dog]
org.apache.lucene.analysis.standard.StandardAnalyzer (configured without stop word - jcr default analyzer) [the] [quick] [brown] [fox] [jumped] [over] [the] [lazy] [dogs]

Table 46.2. "XY&Z Corporation - xyz@example.com"

Analyzer Parsed
org.apache.lucene.analysis.WhitespaceAnalyzer [XY&Z] [Corporation] [-] [xyz@example.com]
org.apache.lucene.analysis.SimpleAnalyzer [xy] [z] [corporation] [xyz] [example] [com]
org.apache.lucene.analysis.StopAnalyzer [xy] [z] [corporation] [xyz] [example] [com]
org.apache.lucene.analysis.standard.StandardAnalyzer [xy&z] [corporation] [xyz@example] [com]
org.apache.lucene.analysis.snowball.SnowballAnalyzer [xy&z] [corpor] [xyz@exampl] [com]
org.apache.lucene.analysis.standard.StandardAnalyzer (configured without stop word - jcr default analyzer) [xy&z] [corporation] [xyz@example] [com]

Note

StandardAnalyzer is the default analyzer in the JBoss Portal Platform JCR search engine. But it does not use stop words.
You can assign your analyzer as described in Chapter 35, Configuring Search.

46.2. Property Indexing

Different properties are indexed in different ways and this affects whether it can be searched via fulltext by property or not.
Only two property types are indexed as fulltext searcheable: STRING and BINARY.

Table 46.3. Fulltext search by different properties

Property Type Fulltext search by all properties Fulltext search by exact property
STRING YES YES
BINARY YES NO

For example, the jcr:data property (which is BINARY) will not be found with a query structured as:
SELECT * FROM nt:resource WHERE CONTAINS(jcr:data, 'some string')
This is because, BINARY is not searchable by fulltext search by exact property.
However, the following query will return some results (provided, of course they node contains the targeted data):
SELECT * FROM nt:resource WHERE CONTAINS( * , 'some string')

46.3. Different Analyzers

First of all, we will fill repository by nodes with mixin type 'mix:title' and different values of 'jcr:description' property.
root
  ├── document1 (mix:title) jcr:description = "The quick brown fox jumped over the lazy dogs"
  ├── document2 (mix:title) jcr:description = "Brown fox live in forest."
  └── document3 (mix:title) jcr:description = "Fox is a nice animal."
The example below shows different Analyzers in action. The first instance uses base JCR settings, so the string; "The quick brown fox jumped over the lazy dogs" will be transformed to the set; {[the] [quick] [brown] [fox] [jumped] [over] [the] [lazy] [dogs] }.
// make SQL query
QueryManager queryManager = workspace.getQueryManager();
String sqlStatement = "SELECT * FROM mix:title WHERE CONTAINS(jcr:description, 'the')";
// create query
Query query = queryManager.createQuery(sqlStatement, Query.SQL);
// execute query and fetch result
QueryResult result = query.execute();
The NodeIterator will return document1.
However, if the default analyzer is changed to org.apache.lucene.analysis.StopAnalyzer, the repository populated again (the new Analyzer must process node properties) and the same query run, it will return nothing, because stop words like "the" will be excluded from parsed string set.

Chapter 47. WebDAV

47.1. Introduction

The WebDAV protocol enables you to use third party tools to communicate with hierarchical content servers via the HTTP protocol. It is possible to add and remove documents or a set of documents from a path on the server.
DeltaV is an extension of the WebDav protocol that allows managing document versioning. The Locking feature guarantees protection against multiple access when writing resources. The ordering support allows changing the position of the resource in the list and sort the directory to make the directory tree viewed conveniently. The full-text search makes it easy to find the necessary documents. You can search by using two languages: SQL and XPATH.
In the eXo JCR, the WebDAV layer (based on the code taken from the extension modules of the reference implementation) is plugged in on top of our JCR implementation. This makes it possible to browse a workspace using the third party tools regardless of operating system environments. You can use a Java WebDAV client, such as DAVExplorer or Internet Explorer using FileOpen as a Web Folder.
WebDav is an extension of the REST service. To get the WebDav server ready, you must deploy the REST application. Then, you can access any workspaces of your repository by using the following URL:
When accessing the WebDAV server via http://localhost:8080/rest/jcr/repository/production, you can substitute production with collaboration.
You will be asked to enter your login credentials. These will then be checked by using the organization service that can be implemented thanks to an InMemory (dummy) module or a DB module or an LDAP one and the JCR user session will be created with the correct JCR Credentials.

Note:

If you try the "in ECM" option, add "@ecm" to the user's password. Alternatively, you may modify jaas.conf by adding the domain=ecm option as follows:
exo-domain {
     org.exoplatform.services.security.jaas.BasicLoginModule required domain=ecm;
};

47.2. WebDAV Configuration

The WebDAV configuration file:
<component>
  <key>org.exoplatform.services.webdav.WebDavServiceImpl</key>
  <type>org.exoplatform.services.webdav.WebDavServiceImpl</type>
  <init-params>

    <!-- this parameter indicates the default login and password values
         used as credentials for accessing the repository -->
    <!-- value-param>
      <name>default-identity</name>
      <value>admin:admin</value>    
    </value-param -->

    <!-- this is the value of WWW-Authenticate header -->
    <value-param>
      <name>auth-header</name>
      <value>Basic realm="eXo-Platform Webdav Server 1.6.1"</value>
    </value-param>

    <!-- default node type which is used for the creation of collections -->
    <value-param>
      <name>def-folder-node-type</name>
      <value>nt:folder</value>
    </value-param>

    <!-- default node type which is used for the creation of files -->
    <value-param>
      <name>def-file-node-type</name>
      <value>nt:file</value>
    </value-param>

    <!-- if MimeTypeResolver can't find the required mime type, 
         which conforms with the file extension, and the mimeType header is absent
         in the HTTP request header, this parameter is used 
         as the default mime type-->
    <value-param>
      <name>def-file-mimetype</name>
      <value>application/octet-stream</value>
    </value-param>

    <!-- This parameter indicates one of the three cases when you update the content of the resource by PUT command.
         In case of "create-version", PUT command creates the new version of the resource if this resource exists.
         In case of "replace" - if the resource exists, PUT command updates the content of the resource and its last modification date.
         In case of "add", the PUT command tries to create the new resource with the same name (if the parent node allows same-name siblings).-->

    <value-param>
      <name>update-policy</name>
      <value>create-version</value>
      <!--value>replace</value -->
      <!-- value>add</value -->
    </value-param>

    <!--
        This parameter determines how service responds to a method that attempts to modify file content.
        In case of "checkout-checkin" value, when a modification request is applied to a checked-in version-controlled resource, the request is automatically preceded by a checkout and followed by a checkin operation.
        In case of "checkout" value, when a modification request is applied to a checked-in version-controlled resource, the request is automatically preceded by a checkout operation.
    -->         
    <value-param>
      <name>auto-version</name>
      <value>checkout-checkin</value>
      <!--value>checkout</value -->
    </value-param>

    <!--
        This parameter is responsible for managing Cache-Control header value which will be returned to the client.
        You can use patterns like "text/*", "image/*" or wildcard to define the type of content.
    -->  
    <value-param>
      <name>cache-control</name>
      <value>text/xml,text/html:max-age=3600;image/png,image/jpg:max-age=1800;*/*:no-cache;</value>
    </value-param>
    
    <!--
        This parameter determines the absolute path to the folder icon file, which is shown
        during WebDAV view of the contents
    -->
    <value-param>
      <name>folder-icon-path</name>
      <value>/absolute/path/to/file</value>
    </value-param>

  </init-params>
</component>

47.3. Corresponding WebDAV and JCR actions

Table 47.1. 

WebDav JCR
COPY Workspace.copy(...)
DELETE Node.remove()
GET Node.getProperty(...); Property.getValue()
HEAD Node.getProperty(...); Property.getLength()
MKCOL Node.addNode(...)
MOVE Session.move(...) or Workspace.move(...)
PROPFIND Session.getNode(...); Node.getNode(...); Node.getNodes(...); Node.getProperties()
PROPPATCH Node.setProperty(...); Node.getProperty(...).remove()
PUT Node.addNode("node","nt:file"); Node.setProperty("jcr:data", "data")
CHECKIN Node.checkin()
CHECKOUT Node.checkout()
REPORT Node.getVersionHistory(); VersionHistory.getAllVersions(); Version.getProperties()
RESTORE Node.restore(...)
UNCHECKOUT Node.restore(...)
VERSION-CONTROL Node.addMixin("mix:versionable")
LOCK Node.lock(...)
UNLOCK Node.unlock()
ORDERPATCH Node.orderBefore(...)
SEARCH Workspace.getQueryManager(); QueryManager.createQuery(); Query.execute()

47.4. WebDAV Considerations

There are some restrictions for WebDAV in different operating systems.
Windows 7
When attempting to set up a web folder through Add a Network Location or Map a Network Drive through My Computer, an error message stating The folder you entered does not appear to be valid. Please choose another or Windows cannot access … Check the spelling of the name. Otherwise, there might be … may be encountered. These errors may appear when you are using SSL or non-SSL.
To fix this, do as follows:
  1. Go to Windows Registry Editor.
  2. Find a key: \HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\services\WebClient\Parameters\BasicAuthLevel .
  3. Change the value to 2.
Microsoft Office 2010
If you have:
  • Microsoft Office 2007/2010 applications installed on a client computer AND...
  • The client computer is connected to a web server configured for Basic authentication VIA...
  • A connection that does not use Secure Sockets Layer (SSL) AND...
  • You try to access an Office file that is stored on the remote server...
  • You might experience the following symptoms when you try to open or to download the file:
    • The Office file does not open or download.
    • You do not receive a Basic authentication password prompt when you try to open or to download the file.
    • You do not receive an error message when you try to open the file. The associated Office application starts. However, the selected file does not open.
These outcomes can be circumvented by enabling Basic authentication on the client machine.
To enable Basic authentication on the client computer, follow these steps:
  1. Click Start, type regedit in the Start Search box, and then press Enter.
  2. Locate and then click the following registry subkey:
    HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\Common\Internet
  3. On the Edit menu, point to New, and then click DWORD Value.
  4. Type BasicAuthLevel, and then press Enter.
  5. Right-click BasicAuthLevel, and then click Modify.
  6. In the Value data box, type 2, and then click OK.

Chapter 48. FTP

48.1. Introduction

The JCR-FTP Server operates as an FTP server with access to a content stored in JCR repositories in the form of nt:file/nt:folder nodes or their successors. The client of an executed Server can be any FTP client. The FTP server is supported by a standard configuration which can be changed as required.

48.2. Configuration Parameters

Parameters

command-port:
<value-param>
   <name>command-port</name>
   <value>21</value>
</value-param>
The value of the command channel port. The value '21' is default.
If you have already other FTP server installed in your system, this parameter needs to be changed (to 2121, for example) to avoid conflicts or if the port is protected.
data-min-port and data-max-port
<value-param>
   <name>data-min-port</name>
   <value>52000</value>
</value-param>
<value-param>
   <name>data-max-port</name>
   <value>53000</value>
</value-param>
These two parameters indicate the minimum and maximum values of the range of ports, used by the server. The usage of the additional data channel is required by the FTP protocol, which is used to transfer the contents of files and the listing of catalogues. This range of ports should be free from listening by other server-programs.
system
<value-param>
   <name>system</name>

   <value>Windows_NT</value>
     or
   <value>UNIX Type: L8</value>
</value-param>
Types of formats of listing of catalogues which are supported.
client-side-encoding
<value-param>
   <name>client-side-encoding</name>
      
   <value>windows-1251</value>
     or
   <value>KOI8-R</value>
     
</value-param>
This parameter specifies the coding which is used for dialogue with the client.
def-folder-node-type
<value-param>
   <name>def-folder-node-type</name>
   <value>nt:folder</value>
</value-param>
This parameter specifies the type of a node, when an FTP-folder is created.
def-file-node-type
<value-param>
   <name>def-file-node-type</name>
   <value>nt:file</value>
</value-param>
This parameter specifies the type of a node, when an FTP-file is created.
def-file-mime-type
<value-param>
   <name>def-file-mime-type</name>                 
   <value>application/zip</value>
</value-param>
The mime type of a created file is chosen by using its file extension. In case, a server cannot find the corresponding mime type, this value is used.
cache-folder-name
<value-param>
   <name>cache-folder-name</name>
   <value>../temp/ftp_cache</value>
</value-param>
The Path of the cache folder.
upload-speed-limit
<value-param>
   <name>upload-speed-limit</name>           
   <value>20480</value>
</value-param>
Restriction of the upload speed. It is measured in bytes.
download-speed-limit
<value-param>
   <name>download-speed-limit</name>
   <value>20480</value>          
</value-param>
Restriction of the download speed. It is measured in bytes.
timeout
<value-param>
   <name>timeout</name>
   <value>60</value>
</value-param>
Defines the value of a timeout.

Chapter 49. Use External Backup Tool

49.1. Repository Suspending

To have the repository content consistent with the search index and value storage, the repository should be suspended. This means all working threads are suspended until a resume operation is performed. The index will be flushed.
JCR provides ability to suspend repository via JMX.
Repository Suspend Controller

Figure 49.1. Repository Suspend Controller


To suspend repository you need to invoke the suspend() operation. The returned result will be "suspended" if everything passed successfully.
Repository Suspend Controller Suspended

Figure 49.2. Repository Suspend Controller Suspended


An "undefined" result means not all components were successfully suspended. Check the console to review the stack traces.

49.2. Backup

You can backup your content manually or by using third part software. You should back up:
  • Database.
  • Lucene index.
  • Value storage (if configured).

49.3. Repository Resuming

Once a backup is done you need to invoke the resume() operation to switch the repository back to on-line. The returned result will be "on-line".
Repository Suspend Controller Online

Figure 49.3. Repository Suspend Controller Online


Chapter 50. eXo JCR statistics

50.1. Statistics on the Database Access Layer

In order to have a better idea of the time spent into the database access layer, it can be interesting to get some statistics on that part of the code, knowing that most of the time spent into eXo JCR is mainly the database access.
These statistics will then allow you to identify, without using any profiler, what is abnormally slow in this layer which could help diagnose, and fix, a problem.
If you use org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.CQJDBCWorkspaceDataContainer or org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer as WorkspaceDataContainer, you can get statistics on the time spent into the database access layer.
The database access layer (in eXo JCR) is represented by the methods of the interface org.exoplatform.services.jcr.storage.WorkspaceStorageConnection, so for all the methods defined in this interface, we can have the following figures:
  • The minimum time spent into the method.
  • The maximum time spent into the method.
  • The average time spent into the method.
  • The total amount of time spent into the method.
  • The total amount of time the method has been called.
Those figures are also available globally for all the methods which gives us the global behavior of this layer.
If you want to enable the statistics, you just need to set the JVM parameter called JDBCWorkspaceDataContainer.statistics.enabled to true. The corresponding CSV file is StatisticsJDBCStorageConnection-${creation-timestamp}.csv for more details about how the CSV files are managed, please refer to the section dedicated to the statistics manager.
The format of each column header is ${method-alias}-${metric-alias}. The metric alias are described in the statistics manager section.
The name of the category of statistics corresponding to these statistics is JDBCStorageConnection, this name is mostly needed to access to the statistics through JMX.

Table 50.1. Method Alias

global This is the alias for all the methods.
getItemDataById This is the alias for the method getItemData(String identifier).
getItemDataByNodeDataNQPathEntry This is the alias for the method getItemData(NodeData parentData, QPathEntry name).
getChildNodesData This is the alias for the method getChildNodesData(NodeData parent).
getChildNodesCount This is the alias for the method getChildNodesCount(NodeData parent).
getChildPropertiesData This is the alias for the method getChildPropertiesData(NodeData parent).
listChildPropertiesData This is the alias for the method listChildPropertiesData(NodeData parent).
getReferencesData This is the alias for the method getReferencesData(String nodeIdentifier).
commit This is the alias for the method commit().
addNodeData This is the alias for the method add(NodeData data).
addPropertyData This is the alias for the method add(PropertyData data).
updateNodeData This is the alias for the method update(NodeData data).
updatePropertyData This is the alias for the method update(PropertyData data).
deleteNodeData This is the alias for the method delete(NodeData data).
deletePropertyData This is the alias for the method delete(PropertyData data).
renameNodeData This is the alias for the method rename(NodeData data).
rollback This is the alias for the method rollback().
isOpened This is the alias for the method isOpened().
close This is the alias for the method close().

50.2. Statistics on the JCR API accesses

In order to know exactly how your application uses eXo JCR, it can be interesting to register all the JCR API accesses in order to easily create real life test scenario based on pure JCR calls and also to tune your JCR to better fit your requirements.
In order to allow you to specify the configuration which part of eXo JCR needs to be monitored without applying any changes in your code and/or building anything, we choose to rely on the Load-time Weaving proposed by AspectJ.
To enable this feature, you will have to add in your classpath the following jar files:
You will also need to get aspectjweaver-1.6.8.jar from the main maven repository http://repo2.maven.org/maven2/org/aspectj/aspectjweaver.
At this stage, to enable the statistics on the JCR API accesses, you will need to add the JVM parameter -javaagent:${pathto}/aspectjweaver-1.6.8.jar to your command line, for more details please refer to http://www.eclipse.org/aspectj/doc/released/devguide/ltw-configuration.html.
By default, the configuration will collect statistics on all the methods of the internal interfaces org.exoplatform.services.jcr.core.ExtendedSession and org.exoplatform.services.jcr.core.ExtendedNode, and the JCR API interface javax.jcr.Property.
To add and/or remove some interfaces to monitor, you have two configuration files to change that are bundled into the jar exo.jcr.component.statistics-X.Y.Z.jar, which are conf/configuration.xml and META-INF/aop.xml.
The file content below is the content of conf/configuration.xml that you will need to modify to add and/or remove the full qualified name of the interfaces to monitor, into the list of parameter values of the init param called targetInterfaces.
<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"
 xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd">

 <component>
   <type>org.exoplatform.services.jcr.statistics.JCRAPIAspectConfig</type>
   <init-params>
     <values-param>
       <name>targetInterfaces</name>
       <value>org.exoplatform.services.jcr.core.ExtendedSession</value>
       <value>org.exoplatform.services.jcr.core.ExtendedNode</value>
       <value>javax.jcr.Property</value>
     </values-param>
   </init-params>
  </component>
</configuration>
The file content below is the content of META-INF/aop.xml that you will to need to modify to add and/or remove the full qualified name of the interfaces to monitor, into the expression filter of the pointcut called JCRAPIPointcut.
By default only JCR API calls from the exoplatform packages are taken into account. This filter can be modified to add other package names.
<aspectj>
  <aspects>
    <concrete-aspect name="org.exoplatform.services.jcr.statistics.JCRAPIAspectImpl" extends="org.exoplatform.services.jcr.statistics.JCRAPIAspect">
      <pointcut name="JCRAPIPointcut"
        expression="(target(org.exoplatform.services.jcr.core.ExtendedSession) || target(org.exoplatform.services.jcr.core.ExtendedNode) || target(javax.jcr.Property)) && call(public * *(..))" />
    </concrete-aspect>
  </aspects>
  <weaver options="-XnoInline">
    <include within="org.exoplatform..*" />
  </weaver>
</aspectj>
The corresponding CSV files are of type Statistics${interface-name}-${creation-timestamp}.csv for more details about how the CSV files are managed, please refer to the section dedicated to the statistics manager.
The format of each column header is ${method-alias}-${metric-alias}. The method alias will be of type ${method-name}(semicolon-delimited-list-of-parameter-types-to-be-compatible-with-the-CSV-format).
The metric alias are described in the statistics manager section.
The name of the category of statistics corresponding to these statistics is the simple name of the monitored interface (e.g. ExtendedSession for org.exoplatform.services.jcr.core.ExtendedSession), this name is mostly needed to access to the statistics through JMX.

Performance Consideration

Please note that this feature will affect the performances of eXo JCR so it must be used with caution.

50.3. Statistics Manager

The statistics manager manages all the statistics provided by eXo JCR, it is responsible of printing the data into the CSV files and also exposing the statistics through JMX and/or Rest.
The statistics manager will create all the CSV files for each category of statistics that it manages, the format of those files is Statistics${category-name}-${creation-timestamp}.csv. Those files will be created into the user directory if it is possible otherwise it will create them into the temporary directory. The format of those files is CSV (i.e. Comma-Separated Values), one new line will be added regularly (every 5 seconds by default) and one last line will be added at JVM exit. Each line, will be composed of the 5 figures described below for each method and globally for all the methods.

Table 50.2. Metric Alias

Min The minimum time spent into the method expressed in milliseconds.
Max The maximum time spent into the method expressed in milliseconds.
Total The total amount of time spent into the method expressed in milliseconds.
Avg The average time spent into the method expressed in milliseconds.
Times The total amount of times the method has been called.

You can disable the persistence of the statistics by setting the JVM parameter called JCRStatisticsManager.persistence.enabled to false. It is set to true by default.
You can also define the period of time between each record (that is, line of data into the file) by setting the JVM parameter called JCRStatisticsManager.persistence.timeout to your expected value expressed in milliseconds. It is set to 5000 by default.
You can also access to the statistics via JMX. The available methods are:

Table 50.3. JMX Methods

getMin Give the minimum time spent into the method corresponding to the given category name and statistics name. The expected arguments are the name of the category of statistics (JDBCStorageConnection for example) and the name of the expected method or global for the global value.
getMax Give the maximum time spent into the method corresponding to the given category name and statistics name. The expected arguments are the name of the category of statistics and the name of the expected method or global for the global value.
getTotal Give the total amount of time spent into the method corresponding to the given category name and statistics name. The expected arguments are the name of the category of statistics and the name of the expected method or global for the global value.
getAvg Give the average time spent into the method corresponding to the given category name and statistics name. The expected arguments are the name of the category of statistics and the name of the expected method or global for the global value.
getTimes Give the total amount of times the method has been called corresponding to the given category name and statistics name. The expected arguments are the name of the category of statistics (e.g. JDBCStorageConnection) and the name of the expected method or global for the global value.
reset Reset the statistics for the given category name and statistics name. The expected arguments are the name of the category of statistics and the name of the expected method or global for the global value.
resetAll Reset all the statistics for the given category name. The expected argument is the name of the category of statistics (e.g. JDBCStorageConnection).

The full name of the related MBean is xo:service=statistic, view=jcr.

Chapter 51. Checking repository integrity and consistency

51.1. JMX-based consistency tool

It is important to check the integrity and consistency of system regularly, especially if there is no, or stale, backups. The JBoss Portal Platform JCR implementation offers an innovative JMX-based complex checking tool.
During an inspection, the tool checks every major JCR component, such as persistent data layer and the index. The persistent layer includes JDBC Data Container and Value-Storages if they are configured.
The database is verified using the set of complex specialized domain-specific queries. The Value Storage tool checks the existence of, and access to, each file.
Access to the check tool is exposed via the JMX interface, with the following operations available:

Table 51.1. Available methods

checkRepositoryDataConsistency() Inspect full repository data (db, value storage and search index)
checkRepositoryDataBaseConsistency() Inspect only DB
checkRepositoryValueStorageConsistency() Inspect only ValueStorage
checkRepositorySearchIndexConsistency() Inspect only SearchIndex

All inspection activities and corrupted data details are stored in a file in the app directory and named as per the following convention: report-<repository name>-dd-MMM-yy-HH-mm.txt .
The path to the file will be returned in result message also at the end of the inspection.

Note

There are three types of inconsistency (Warning, Error and Index) and two of them are critical (Errors and Index):
  • Index faults are marked as "Reindex" and can be fixed by re-indexing the workspace.
  • Errors can only be fixed manually.
  • Warnings can be a normal situation in some cases and usually production system will still remain fully functional.

Chapter 52. JCR Performance Tuning Guide

52.1. Introduction

This section will show you various ways of improving JCR performance.
It is intended for Administrators and others who want to use the JCR features more efficiently.

52.2. JCR Performance and Scalability

52.2.1. Cluster configuration

The table below contains details about the configuration of the cluster used in benchmark testing:

Table 52.1. EC2 network: 1Gbit

Servers hardware Specification
RAM 7.5 GB
Processors 4 EC2 Compute Units (2 virtual cores with 2 EC2 Compute Units each)
Storage 850 GB (2×420 GB plus 10 GB root partition)
Architecture 64-bit
I/O Performance High
API name m1.large
Note:
NFS and statistics (cacti snmp) server were located on one physical server.
JBoss Enterprise Application Platform 6 configuration:
JAVA_OPTS: -Dprogram.name=run.sh -server -Xms4g -Xmx4g -XX:MaxPermSize=512m -Dorg.jboss.resolver.warning=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -XX:+UseParallelGC -Djava.net.preferIPv4Stack=true

52.2.2. JCR Clustered Performance

Benchmark test using WebDAV (Complex read/write load test (benchmark)) with 20K same file. To obtain per-operation results we have used custom output from the test case threads to CSV file.
Read operation:
Warm-up iterations: 100
Run iterations: 2000
Background writing threads: 25
Reading threads: 225
EC2 Performance Results

Figure 52.1. EC2 Performance Results


Table 52.2. 

Nodes count tps Responses >2s Responses >4s
1 523 6.87% 1.27%
2 1754 0.64% 0.08%
3 2388 0.49% 0.09%
4 2706 0.46% 0.1%

Read operation with more threads:
Warm-up iterations: 100
Run iterations: 2000
Background writing threads: 50
Reading threads: 450
EC2 Performance Results 2

Figure 52.2. EC2 Performance Results 2


Table 52.3. 

Nodes count tps Responses >2s Responses >4s
1 116 ? ?
2 1558 6.1% 0.6%
3 2242 3.1% 0.38%
4 2756 2.2% 0.41%

52.3. Performance Tuning Guide

52.3.1. JBoss Enterprise Application Platform 6 Tuning

You can use maxThreads parameter to increase maximum amount of threads that can be launched in AS instance. This can improve performance if you need a high level of concurrency. also you can use -XX:+UseParallelGC java directory to use parallel garbage collector.

Note

Beware of setting maxThreads too big, this can cause OutOfMemoryError. We've got it with maxThreads=1250 on such machine:
7.5 GB memory
4 EC2 Compute Units (2 virtual cores with 2 EC2 Compute Units each)
850 GB instance storage (2×420 GB plus 10 GB root partition)
64-bit platform
I/O Performance: High
API name: m1.large
java -Xmx 4g

52.3.2. JCR Cache Tuning

Cache size
JCR-cluster implementation is built using JBoss Cache as distributed, replicated cache. But there is one particularity related to remove action in it. Speed of this operation depends on the actual size of cache. As many nodes are currently in cache as much time is needed to remove one particular node (subtree) from it.
Eviction
Manipulations with eviction wakeUpInterval value does not affect on performance. Performance results with values from 500 up to 3000 are approximately equal.
Transaction Timeout
Using short timeout for long transactions such as Export/Import, removing huge subtree defined timeout may cause TransactionTimeoutException.

52.3.3. Clustering

For performance it is better to have a load-balancer, DB server and shared NFS on different computers. If in some reasons you see that one node gets more load than others you can decrease this load using load value in load balancer.
JGroups configuration
It's recommended to use "multiplexer stack" feature present in JGroups. It is set by default in eXo JCR and offers higher performance in cluster, using less network connections also. If there are two or more clusters in your network, please check that they use different ports and different cluster names.
Write performance in cluster
Exo JCR implementation uses Lucene indexing engine to provide search capabilities. But Lucene brings some limitations for write operations: it can perform indexing only in one thread. That is why write performance in cluster is not higher than in singleton environment. Data is indexed on coordinator node, so increasing write-load on cluster may lead to ReplicationTimeout exception. It occurs because writing threads queue in the indexer and under high load timeout for replication to coordinator will be exceeded.
Taking in consideration this fact, it is recommended to exceed replTimeout value in cache configurations in case of high write-load.
Replication timeout
Some operations may take too much time. So if you get ReplicationTimeoutException try increasing replication timeout:
   <clustering mode="replication" clusterName="${jbosscache-cluster-name}">
      ...
      <sync replTimeout="60000" />
   </clustering>
value is set in milliseconds.

Chapter 53. eXo JCR with JBoss Portal Platform

53.1. How to use a Managed DataSource under JBoss Enterprise Application Platform 6

53.1.1. Configurations Steps

53.1.1.1. Declaring the Datasources in the AS

To declare the datasources using a JBoss application server, deploy a ds file (XXX-ds.xml) into the deploy directory of the appropriate server profile (/server/PROFILE/deploy, for example).
This file configures all datasources which JBoss Portal Platform will need (there should be four specifically named: jdbcjcr_portal, jdbcjcr_portal-sample, jdbcidm_portal and jdbcidm_sample-portal).
For example:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
   <no-tx-datasource>
      <jndi-name>jdbcjcr_portal</jndi-name>
      <connection-url>jdbc:hsqldb:${jboss.server.data.dir}/data/jdbcjcr_portal</connection-url>
      <driver-class>org.hsqldb.jdbcDriver</driver-class>
      <user-name>sa</user-name>
      <password></password>
   </no-tx-datasource>

   <no-tx-datasource>
      <jndi-name>jdbcjcr_sample-portal</jndi-name>
      <connection-url>jdbc:hsqldb:${jboss.server.data.dir}/data/jdbcjcr_sample-portal</connection-url>
      <driver-class>org.hsqldb.jdbcDriver</driver-class>
      <user-name>sa</user-name>
      <password></password>
   </no-tx-datasource>

   <no-tx-datasource>
      <jndi-name>jdbcidm_portal</jndi-name>
      <connection-url>jdbc:hsqldb:${jboss.server.data.dir}/data/jdbcidm_portal</connection-url>
      <driver-class>org.hsqldb.jdbcDriver</driver-class>
      <user-name>sa</user-name>
      <password></password>
   </no-tx-datasource>

   <no-tx-datasource>
      <jndi-name>jdbcidm_sample-portal</jndi-name>
      <connection-url>jdbc:hsqldb:${jboss.server.data.dir}/data/jdbcidm_sample-portal</connection-url>
      <driver-class>org.hsqldb.jdbcDriver</driver-class>
      <user-name>sa</user-name>
      <password></password>
   </no-tx-datasource>
</datasources>
The properties can be set for datasource can be found here: Configuring JDBC DataSources - The non transactional DataSource configuration schema

53.1.1.2. Do not bind datasources explicitly

Do not let the portal explicitly bind datasources.
Edit the JPP_HOME/standalone/configuration/gatein/configuration.properties and comment out the following rows in the JCR section:
#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=
Comment out the following lines in the IDM section:
#gatein.idm.datasource.driver=org.hsqldb.jdbcDriver
#gatein.idm.datasource.url=jdbc:hsqldb:file:${gatein.db.data.dir}/data/jdbcidm_${name}
#gatein.idm.datasource.username=sa
#gatein.idm.datasource.password=
Open the jcr-configuration.xml and idm-configuration.xml files and comment out references to the plug-in InitialContextInitializer.
<!-- Commented because, Datasources are declared and bound by AS, not in eXo -->
<!--
<external-component-plugins>
    [...]
</external-component-plugins>
-->

Revision History

Revision History
Revision 6.0.0-2.4002013-10-31Rüdiger Landmann
Rebuild with publican 4.0.0
Revision 6.0.0-2Fri Aug 9 2013Jared Morgan
Updated the Product Name to reflect the new name grouping for the product. No update was made to details in the guide.
Revision 6.0.0-1Tue Mar 05 2013Jared Morgan
Prepared for JBoss Portal Platform 6.0 GA.