Red Hat JBoss Portal 6.2

Development Guide

For use with Red Hat JBoss Portal 6.2

Jared Morgan

Red Hat, Ltd. Customer Content Services

Aakanksha Singh

Red Hat, Ltd. Customer Content Services

Legal Notice

Copyright © 2015 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

The Development Guide is intended for those developing portlet applications and gadgets for the product, and outlines options and strategies for successful deployment.
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. We Need Feedback
I. Architectural and Design Choices
1. Architectural Choices
1.1. Identity server
1.2. Storage
1.3. Cluster
1.4. SSO
1.4.1. Summary
2. Design Choices
2.1. Dashboards
2.2. JCR Index Replication in a Cluster Setup
2.2.1. JCR Index Replication in a Cluster Setup
2.2.2. Standalone Index
2.2.3. Local Index
2.2.4. Shared Index
II. Portal API
3. PortalRequest and Portal
4. Site
4.1. Retrieving Sites
4.2. Creating a Site
4.2.1. Setting Attributes for a Site
4.2.2. Setting permissions for a site
4.3. Deleting a Site
5. Navigation
5.1. Retrieving Navigation Nodes
5.1.1. NodeVisitor
5.1.2. Filtering Navigation Nodes
5.2. Creating a Navigation Node
5.2.1. Navigation Node Visibility
5.2.2. Localization
5.3. Deleting a Navigation Node
5.4. Moving a Navigation Node
6. Page
6.1. Retrieving Pages
6.2. Creating a Page
6.2.1. Setting Permissions for a Page
6.3. Page Composition
6.4. Deleting a Page
6.5. Application
7. OAuth Provider API
7.1. Retrieve an Instance of OAuthProvider
7.2. OAuthProvider Operations
7.3. Access to Provider-specific Operations
8. REST API
8.1. Overview
8.1.1. Base URL
8.1.2. Authentication
8.1.3. Content Type
8.1.4. Localization
8.2. Resources
8.2.1. Sites
8.2.2. Pages
8.2.3. Navigation
8.2.4. Working with Templates
9. Organization API
9.1. Organization API
10. Accessing User Profile
III. Portal Development
11. Portal Life-cycle
11.1. Application Server Start and Stop
11.1.1. Advanced WCI Registration
11.2. The Command Servlet
12. Portal Containers
13. Skinning the Portal
13.1. Skin Components
13.2. Skin Selection
13.2.1. Skin Selection Through the User Interface
13.2.2. Setting the Default Skin within the Configuration Files
13.3. Skins in Page Markups
13.4. The Skin Service
13.4.1. Skin configuration
13.4.2. Resource Request Filter
13.5. The Default Skin
13.6. Creating New Skins
13.6.1. Creating New Skins
13.6.2. Creating a New Portal Skin
13.6.3. Creating a New Window Style
13.6.4. How to Create New Portlet Skins
13.6.5. Create New Portlet Specification CSS Classes
13.7. Tips and Tricks
13.7.1. CSS Hosted on CDN
13.7.2. Easier CSS Debugging
13.7.3. Some CSS Techniques
14. Portal Extension
14.1. How the Shadowing Mechanism Works
14.2. Custom Groovy Template for a Portlet
14.3. Custom Skin for a Portlet
14.3.1. gatein-resources.xml
14.3.2. CSS and Images
14.4. Custom Navigation and Pages
14.4.1. portal-configuration.xml
14.5. Navigation Node Types
14.6. Internationalization of Navigation Nodes
14.7. Custom Internationalization Resource Bundles
14.8. Custom Sign-in Page
15. Visual Identity
16. Data Import Strategy
16.1. Import Strategy Overview
16.1.1. Portal Configuration
16.1.2. Page Data
16.1.3. Navigation Data
17. Right To Left (RTL) Framework
17.1. Groovy templates
17.2. Stylesheet
17.3. Images
17.4. Client Side JavaScript
18. XML Resources Bundles
18.1. XML format
18.2. Portal Support
19. Navigation Controller
19.1. Controller Configuration (controller.xml)
19.1.1. Rendering
IV. Gadget Development
20. Gadgets in Portal
20.1. Sources to Develop Gadgets
21. Portlet Development Resources
21.1. JSR-168 and JSR-286 overview
21.1.1. Portal Pages
21.1.2. Rendering Modes
21.1.3. Window States
21.2. Tutorials
21.2.1. Deploying your first portlet
21.2.2. JavaServer Pages Portlet Example
22. Portlet Development
22.1. Starting a Portlet Project
22.1.1. The Bill of Materials (BOM) Concept
22.1.2. Using the BOM
22.2. Building and Deploying Portlets
22.3. Standard Portlet Development (JSR-286)
22.3.1. Java Configuration
22.3.2. portlet.xml
22.3.3. web.xml
22.3.4. Building and Deploying Portlets
23. Basic JSF Portlet Development
23.1. Example Code
23.1.1. pom.xml
23.1.2. JSF Template Files
23.1.3. Java Beans
23.1.4. portlet.xml
23.1.5. web.xml
23.1.6. Custom CSS
23.1.7. Internationalization
23.2. Further Steps
23.3. See also
24. JSF Portlet Development with RichFaces
24.1. Example Code
24.1.1. pom.xml
24.1.2. JSF Template Files
24.1.3. Java Beans
24.1.4. portlet.xml
24.1.5. web.xml
24.1.6. Custom CSS
24.1.7. Internationalization
24.2. Further Steps
24.3. See also
25. Portlets with JSF and CDI
26. CDI Portlet Development
26.1. GenericPortlet and Portlet Filter Injection
26.2. Portlet CDI Scopes
26.2.1. @PortletLifecycleScoped
26.2.2. @PortletRedisplayScoped
27. Portlet Filter
27.1. Global Metadata Elements
27.1.1. Global Metadata Elements
27.1.2. Configuring a Portlet Filter
28. Portlet Bridge
28.1. JBoss Portlet Bridge
28.2. Portlet application
28.3. Extensions
28.4. Examples
28.5. Render Policy Parameters
28.6. Facelets Configuration
28.7. JSP-only Configuration
28.8. RichFaces Local and Remote Portlet Support
28.9. Sending and Receiving Events
28.10. Sending Events
28.11. Receiving Events
28.12. Public Render Parameters
28.13. Saving Bridge Request Scope after Render complete
28.14. PRP portlet configuration
28.15. Application configuration
28.16. Portlet Session
28.17. Resource serving
28.18. Serving JSF Resources in a Portlet
28.19. Expression Language Reference
28.20. Expression Language Configuration
28.21. Developing Portlets with the Bridge
28.21.1. Implementing Portlet Bridge
28.21.2. Declaring Artifact Dependencies
28.21.3. Declaring Depchain Dependencies
28.21.4. Deploying Portlet Bridge Portlets
28.21.5. Disable Automatic Portlet Bridge Injection
28.21.6. Supported Portlet Tags
28.21.7. Excluding Attributes from the Bridge Request Scope
28.21.8. Prevent Resources Being Added to Portal Page Head
28.21.9. JSF Facelet View
28.21.10. Error Handling
28.21.11. Switching Portlet Modes
28.21.12. Navigating to a mode's last viewId
28.21.13. Using Wildcards to Identify the Rule Target
28.21.14. Clearing the View History when Changing Portlet Modes
28.21.15. Communication Between Portlets
28.21.16. Storing Components in PortletSession.APPLICATION_SCOPE
28.21.17. Using the PortletSession
28.21.18. Linking to a Facelets page within the Same Portlet
28.21.19. Redirecting to an External Page or Resource
28.21.20. Using Provided EL Variables
29. Navigation Portlet Using the Public API
29.1. Example Code
29.1.1. Required Java Classes
29.1.2. Required JSP
29.1.3. Required JavaScript
29.1.4. Further Steps
29.2. See also
30. Using Data from Social Networks in Portlets
30.1. Social Portlets Example Code
30.1.1. Prerequisites
30.2. Retrieving the Access Token
30.3. Facebook
30.4. Google+
30.5. Twitter
30.6. Configuration and Deployment Descriptors
30.7. Further Steps
31. Spring Framework Portlet Development
V. JavaScript Development
32. JavaScript Modularity
32.1. JavaScript Modules
32.2. Introduction to Modules
32.3. Script Support
33. JavaScript in JBoss Portal
33.1. Declaring a Module
33.2. Translation to the AMD System
33.3. Producing Dependencies
34. Native AMD Modules
34.1. Difference in Native AMD and Portal Modules
34.2. Native AMD Modules hosted on CDN
34.3. Mapping path entries for AMD Modules
34.4. Fallback Paths
34.5. Example to Ensure Portal wide Consistency in mapping paths
35. Module Scopes
35.1. Shared Scope
35.2. Portal Scope
35.3. Portlet Scope
36. Portlet Dynamic Module
37. Aliases
38. Custom Adapters
39. Module Resources
40. Load Groups
41. Localization
42. Non-AMD Scripts
VI. JavaScript Code Examples
43. Module Code Examples
43.1. Declare a Module
43.2. Declare an AMD Module
43.3. Use jQuery Module
43.4. Use a Custom jQuery Version
43.5. jQuery Plug-ins
43.6. Override Native AMD Module Dependency
43.7. CommonJS modules
43.8. Mustache.js module
43.9. Resource loading with Text.js
44. Script Code Examples
44.1. Accessing a module from a script
44.2. Exposing jQuery Version Globally
44.3. Defining a custom global jQuery
44.4. Using a Global jQuery plugin
VII. Advanced Development Concepts
A. The eXo Kernel
A.1. The eXo Kernel
A.2. Kernel Configuration Namespace
A.3. Configuration Retrieval
A.4. PortalContainer Advanced Concepts
A.4.1. Add new configuration files from a WAR file
A.4.2. Creating PortalContainers from a WAR File
A.4.3. Defining a PortalContainer with its dependencies and its settings
A.4.4. PortalContainer Settings
A.4.5. Dynamically Changing a PortalContainerDefinition
A.4.6. Dynamically Disable a Portal Container
A.5. Runtime Configuration Profiles
A.6. Component request life cycle
A.6.1. Component request life cycle contract
A.6.2. Request life cycle
A.7. Configuring Services
A.7.1. Configuration syntax
A.8. Specific Services
A.8.1. ListenerService
A.8.2. Job Scheduler
A.8.3. The data source provider
A.9. Configuring a portal container
A.10. System property configuration
A.10.1. Properties <init-param>
A.10.2. Properties URL <init-param>
A.10.3. System Property configuration of the properties URL
A.10.4. Variable Syntaxes
A.11. The Extension Mechanism and Portal Extensions
A.11.1. Running Multiple Portals
A.12. Manageability
A.12.1. Introduction
A.12.2. Managed framework API
A.12.3. JMX Management View
A.12.4. Example
B. eXo JCR
B.1. Introduction and Support Scope
B.2. Concepts
B.2.1. Repository and Workspace
B.2.2. Items
B.2.3. The Data Model
B.2.4. Data Abstraction
B.3. eXo JCR Repository Service Components
B.3.1. Workspace Data Model
B.4. Template for JCR configuration file
B.4.1. Portal Configuration for JCR
B.5. Multi-language Support
B.5.1. Oracle
B.5.2. DB2
B.5.3. MySQL
B.5.4. PostgreSQL
B.6. Configuring Search
B.6.1. Global Search Index
B.6.2. Indexing Configuration
B.6.3. Advanced features
B.7. Configuring the JDBC Data Container
B.7.1. Introduction
B.7.2. Supported Databases
B.7.3. Configuring the database using SQL-script
B.7.4. Multilanguage support database configuration
B.7.5. Isolated-database Configuration
B.7.6. Multi-database Configuration
B.7.7. Single-database Configuration
B.7.8. Simple and Complex queries
B.7.9. Force Query Hints
B.7.10. Notes for Microsoft Windows Users
B.8. External Value Storages
B.8.1. Tree File Value Storage
B.8.2. Disabling Value Storage
B.9. Workspace Data Container
B.10. Configuring the Cluster
B.10.1. Launching Cluster
B.10.2. Requirements
B.11. Configuring JBoss Cache
B.11.1. Indexer lock manager and data container configuration
B.11.2. JGroups configuration
B.11.3. Sharing JBoss Cache instances
B.11.4. Shipped JBoss Cache configuration templates
B.12. LockManager
B.12.1. CacheableLockManagerImpl
B.12.2. JBoss Cache Configuration
B.12.3. Configuration of JBoss Cache for LockManager
B.12.4. LockManager Configuration Template
B.12.5. Creating udp-mux.xml
B.12.6. FQN type and node type in different Databases
B.12.7. Lock Migration
B.13. JCR Indexing
B.13.1. Standalone Index
B.13.2. Local Index
B.13.3. Shared Index
B.13.4. RSync-based Index
B.13.5. Query-handler configuration
B.13.6. Asynchronous Re-indexing
B.13.7. Lucene tuning
B.13.8. Searching Repository Content
B.13.9. Highlighting
B.13.10. SpellChecker
B.13.11. Similarity
B.14. Full Text Search And Affecting Settings
B.14.1. Lucene Analyzers
B.14.2. Property Indexing
B.14.3. Different Analyzers
B.15. WebDAV
B.15.1. WebDAV Configuration
B.15.2. WebDAV and JCR Actions
B.15.3. WebDAV Limitation on Windows
B.15.4. WebDAV Limitation for Microsoft Office 2010
B.16. FTP
B.17. Use External Backup Tool
B.17.1. Repository Suspending
B.17.2. Backup Considerations
B.17.3. Repository Resuming
B.18. eXo JCR statistics
B.18.1. Statistics on the Database Access Layer
B.18.2. Statistics on the JCR API Accesses
B.18.3. Statistics Manager
B.19. Checking Repository Integrity and Consistency
B.19.1. JMX-based consistency tool
B.20. JCR Performance Tuning Guide
B.20.1. Cluster configuration
B.20.2. JCR Clustered Performance
B.20.3. JBoss Enterprise Application Platform 6 Tuning
B.20.4. JCR Cache Tuning
B.20.5. Clustering Tuning
B.20.6. Declaring the Datasources in the Application Server
C. Quickstarts
C.1. Quickstarts
C.2. Quickstarts Downloads Page
C.3. JBoss Developer Studio or Eclipse with JBoss Tools
D. 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.

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 submenu 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:
static int kvm_vm_ioctl_deassign_device(struct kvm *kvm,
                 struct kvm_assigned_pci_dev *assigned_dev)
{
         int r = 0;
         struct kvm_assigned_dev_kernel *match;

         mutex_lock(&kvm->lock);

         match = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
                                       assigned_dev->assigned_dev_id);
         if (!match) {
                 printk(KERN_INFO "%s: device hasn't been assigned before, "
                   "so cannot be deassigned\n", __func__);
                 r = -EINVAL;
                 goto out;
         }

         kvm_deassign_device(kvm, match);

         kvm_free_assigned_device(kvm, match);

out:
         mutex_unlock(&kvm->lock);
         return r;
}

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. From the Customer Portal, you can:
  • Search or browse through a knowledge base 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 the name of any mailing list to subscribe to that list or to access the list archives.

2.2. We Need Feedback

If you find a typographical error in this manual, or if you have thought of a way to make this manual better, we would love to hear from you. Please submit a report in Bugzilla: http://bugzilla.redhat.com/ against the product Red Hat JBoss Portal.
When submitting a bug report, be sure to mention the manual's identifier: Development_Guide
If you have a suggestion for improving the documentation, try to be as specific as possible when describing it. If you have found an error, please include the section number and some of the surrounding text so we can find it easily.

Part I. Architectural and Design Choices

Chapter 1. Architectural Choices

Depending on your environment and goals, you have to decide which components make up the final portal. Some elements may already be in place, such as an identity server, and others may be a matter of choice. This section aims to guide you in making those decisions.

1.1. Identity server

Red Hat JBoss Portal (JBoss Portal) comes with a component named PicketLink IDM, which allows to store and retrieve users and groups from various identity sources. PicketLink IDM supports three general options:
Database
Users, groups and their relationships are stored in an RDBMS database. Table names and column names can be changed, but the overall relationship between tables remains the same. This solution suits identity servers that are build from scratch and will handle thousands of users.
LDAP
Users, groups and their relationships are stored in an LDAP (or ActiveDirectory) server. The directory's structure can be adapted by configuration to the most common scenarios. This solution is particularly suited to environments that already use an LDAP server, environments that will share the identity server amongst multiple services (the website being one of them) or for very large sets of users (millions). When using LDAP with large number of users, it is recommended to use LDAP tools to do the provisioning of users.
Custom
When retrieving users, groups and their relationship cannot be done by configuration, it is possible to use the Picketlink IDM Service Provider Interface (SPI) to implement custom methods which control retrieving and storing user information.
PicketLink IDM also supports mixed environments, which is very useful when an LDAP infrastructure is provided in read-only mode. Since a website may need to store additional information about users (such as their preferred language), LDAP and database services can be combined so that users' information is stored in an LDAP source and additional properties in a database. During calls to the identity API, the information from both sources is merged transparently.
For more information about PicketLink IDM, see the JBoss Portal Administration and Configuration Guide.

1.2. Storage

The portal framework stores page compositions, portlet preferences and gadget code in a database through a Java Content Repository (JCR) API. A set of database servers and JDBC connectors are tested by Red Hat and details of supported configurations can be found on the JBoss Enterprise Portal Platform Supported Configurations page here; http://www.jboss.com/products/platforms/portals/testedconfigurations/ page.
The database schema is automatically created during the initial start up of the portal, at which point it is required that the database users have sufficient rights to create tables. This privilege can be revoked after the initial start up. The database's content can be exported and imported without CREATE TABLE permission. The automatic creation of the database schema minimizes the manual effort involved in the installation of the product.
Since an RDBMS is not suited to the storage of large files, it is recommended to configure eXo JCR to store such files in the filesystem instead of a database, with metadata about the files stored in the database.

Note

If the website is running on a cluster, the filesystem must be accessible from all nodes and an NFS solution is required. For more details see "value storage" in the chapter eXo JCR.

1.3. Cluster

Clustering to meet failover or load-balancing goals requires a more complex configuration than a single node configuration. There is a cost associated with clustering since Red Hat JBoss Portal 6.1 has some optimizations when running on a single node, but the product is designed to scale linearly so that performance is improved with each new node.
In a cluster environment, all critical components are synchronized across all nodes, while less critical components are de-prioritized to achieve better performance. It is important that custom applications behave in the same manner when replicating data across a cluster of nodes.
The number of nodes required will vary according to factors such as: infrastructure, performance criteria, and applications developed. Performance analysis with tools such as JMeter and Grinder is recommended for measuring the impact of heavy loads prior to production implementation.

1.4. SSO

If a website is a part of a global infrastructure, a deployment of Single-Sign-On (SSO) may be beneficial to end users. Various SSO solutions are supported by Red Hat JBoss Portal, as documented on the "Supported Configurations" page available at https://access.redhat.com/site/articles/119833.
In some cases it can be better to have the token manager service on a specific server.

1.4.1. Summary

The infrastructure required is as follows:
  • A database.
  • LDAP (Optional).
  • NFS (Optional, unless in a cluster configuration with default settings).
  • An SSO token service (Optional)
  • A cluster of nodes (Optional).
An example of the simplest configuration:
Simple SSO Setup, featuring users connecting to a single JBoss Portal Platform node with a shared folder and a database server.

Figure 1.1. Simple SSO Example

An example of a more complex configuration:
Multi-node cluster consisting of three nodes, a primary database, LDAP server, SSO ticket server, load balancer, with NFS share connected to the three nodes.

Figure 1.2. Multi-node SSO Example

Chapter 2. Design Choices

Once the main components of the architecture have been decided, choices must be made on the overall design.

2.1. Dashboards

User dashboards may be very costly in a large-scale portal due to the information storage considerations for each user creating a customized page. While some measures have been taken to ameliorate this cost, it, by nature, cannot be avoided completely and should be taken into account when making design decisions in large organizations.
This overhead is difficult to estimate as it is dependent on how users navigate the portal. If only a minority use this functionality; the cost may be negligible. Alternately, the cost could be considerable if many users create custom spaces.
The impact of implementing this feature must be measured by:
  1. Estimating the number of dashboards and pages that will be created.
  2. Observing the impact on the database (through the JCR) in terms of size.

2.2. JCR Index Replication in a Cluster Setup

2.2.1. JCR Index Replication in a Cluster Setup

The JCR implementation uses Apache Lucene to index the data. The indexes are used to search for content (page nodes or WCM content for instance).
Lucene is not cluster-ready, but on a cluster, each node must be able to search for content and will need to have access to the Lucene indexes.
When optimizing searching, a balance must be struck between various goals, including:
  • Speed.
  • Fast indexing.
  • Consistent results on a node.
  • Data accuracy and longevity.
  • Impact on system performance.
  • Ease and impact of implementation.
However eXo JCR, the JCR implementation used by Red Hat JBoss Portal, makes it possible to configure the storage and retrieval of indexes according to the architect's decisions on constraint stringency.

2.2.2. Standalone Index

Standalone Index is only suitable for a non-cluster environment but is the least complicated configuration, with a combination of in-memory and file based indexes. There is no replication involved so any entry can be found by a search as soon as it is created.
Diagram explaining the basic premise of a Standalone Index.

Figure 2.1. Standalone Index

2.2.3. Local Index

In Local Index setup each node keeps a local copy of the full indexes so that when a search is requested on a node, there is no network communication required. However, the cost of this method is replication; when a node indexes an item, it is required to replicate that index on each node. If a node is unavailable at that time, it may miss an index update request and then the different nodes may be inconsistent.
Also when a node is added, it has to recreate its own full index.
An alternative to this configuration is to configure a node to retrieve the information from a coordinator on each search, which makes the startup of the new node faster, but impacts its performance during operation.
Diagram explaining JCR volatile indexes across multiple indexes.

Figure 2.2. Local Index

2.2.4. Shared Index

In this setup a unique index is created and shared among all the nodes. The Network File System (NFS) must be installed so that all nodes can read the index.
Advantages:
  • Consistency: all the nodes see the same data.
Disadvantages:
  • Requires a highly available NFS setup (NFS 3 is recommended).
  • Increased network communication.
Diagram explaining the shared index across all JCR instances.

Figure 2.3. Shared Index

Part II. Portal API

Chapter 3. PortalRequest and Portal

The PortalRequest is the entry point to the API. It provides access to the portal, and it also provides access to information associated with the current request (such as the authenticated user and the page being requested).

Example 3.1. Portal Request

This example shows how to use PortalRequest to obtain an instance of the portal.
Portal portal = PortalRequest.getInstance().getPortal();

Chapter 4. Site

A Site is comprised of a set of pages, navigation definitions, and can be assigned a unique skin. There are three different types of sites: standard sites, group spaces and user dashboards.

4.1. Retrieving Sites

A specific site can be retrieved using its SiteId . The SiteId is constructed by passing either the name for a standard site, the group for a group space or the user for a user dashboard. Alternatively, it can be constructed by passing the SiteType and name.

Example 4.1. Retrieving Sites

This example shows how to retrieve sites.

Site standardSite = portal.getSite(new SiteId("classic"));
Site groupSpace = portal.getSite(new SiteId(new Group("platform", "administrators")));
Site userDashboard = portal.getSite(new SiteId(new User("john")));
It is also possible to query for sites using the SiteQuery . The SiteQuery supports filtering, sorting and pagination when querying for sites.

Note

The default number of returned results is 15. If no pagination is set while building the query, the maximum number of results will be 15. Ensure the correct pagination is set while querying.

Example 4.2. Site Query

This example finds standard sites that a user has access permissions to, limited to the first ten results.

SiteQuery.Builder qb = new SiteQuery.Builder();

qb.withSiteTypes(SiteType.SITE).withPagination(0, 10).withFilter(new Filter<Site>() {
    public boolean accept(Site site) {
        return portal.hasPermission(user, site.getAccessPermission());
    }
});

List<Site> sites = portal.findSites(qb.build());

4.2. Creating a Site

The work flow to create a site is described below:
  1. Create a new site through the Portal.
  2. Set the configuration for the site (such as localized display names and skin).
  3. Save the configuration using Portal.

Example 4.3. Create a New Standard Site

This example creates a new standard site with the name mysite and a non-localized display name.
The site is not visible (or persisted) until saveSite is invoked.
Site site = portal.createSite(new SiteId("foo"));
site.setDisplayName("My Site");
portal.saveSite(site);

4.2.1. Setting Attributes for a Site

It is possible to change attributes for the site. The supported attributes are:
sessionAlive
Specifies whether a session is kept alive or not. Valid values are "onDemand", "always", and "never" with the default being "onDemand"
showPortletInfo
Specifies whether the info bar is shown by default when adding applications to pages. The default is 'false'

Example 4.4. Set Attributes Using the Key

Attributes are set by using the associated Key. This example changes the sessionAlive attribute.

site.getAttributes().put(Site.AttributeKeys.SESSION_BEHAVIOR, "always");

4.2.2. Setting permissions for a site

Associated with a site is an access permission and an edit permission, which controls which users and groups are allowed to access and edit the site respectively.

Example 4.5. Setting Access and Edit Permissions

This example demonstrates how to make a site accessible by everyone, but only editable by users in the /platform/administrators group.
The changed permissions do not take affect until saveSite is invoked.

site.setAccessPermission(Permission.everyone());
site.setEditPermission(Permission.any("platform", "administrators"));

portal.saveSite(site);

4.3. Deleting a Site

A site is deleted by invoking removeSite on the Portal.

Chapter 5. Navigation

A Navigation for a site is retrieved from the Portal and allows accessing the node tree that represents the navigation entries for the site. There is a special root node which is not directly part of the navigation, but it's the parent of the first level of navigation entries. As the root node is not meant to be displayed it doesn't have a display name.

Example 5.1. Retrieving Navigation

This example retrieves the Navigation for the classic site.

Navigation navigation = portal.getNavigation(new SiteId("classic"));

5.1. Retrieving Navigation Nodes

When retrieving navigation nodes it is possible to either retrieve the root node, or a specific node in the hierarchy. It is also possible to control which if any of the children are loaded.

Example 5.2. Basic Navigation Portlet

This example shows a very simple navigation portlet that displays the top level of entries in the navigation menu.

public void doView(RenderRequest request, RenderResponse response) throws IOException {
    PrintWriter pw = response.getWriter();
    Navigation navigation = PortalRequest.getInstance().getNavigation();

    pw.print("<ul>");
    for (Node n : navigation.getRootNode(Nodes.visitChildren())) {
        pw.printf("<li><a href='%s'>%s</a></li>", n.getURI(), n.getDisplayName());
    }
    pw.print("</ul>");
}

Example 5.3. Retrieve a Specific Node

This example demonstrates how to retrieve a specific node in the tree by specifying the NodePath to the node.

Node node = navigation.getNode(NodePath.path("home"));

Note

When a node is retrieved, it actually represents a tree of nodes and all operations (for example, save and refresh) act on that entire tree. For example, if a root node with two child nodes is retrieved, and changes are made to the both child nodes but only one node is saved, the other child will be saved automatically because the save operation acts on the entire node tree.

5.1.1. NodeVisitor

It is important to limit how many levels of navigation nodes are retrieved, especially where there is a large number of nodes. This is controlled by using either one of the built in NodeVisitor from the Nodes class, or by implementing your own NodeVisitor . The Nodes class contains the following visitors:
  • visitAll - loads all nodes
  • visitChildren - loads the immediate children
  • visitNone - loads only the node
  • visitNodes(int) - loads a specified depth of descendants

Example 5.4. Retrieve a Node Using visitNodes

This example retrieves the root node and two levels of nodes.

Node rootNode = navigation.getRootNode(Nodes.visitNodes(2));

Example 5.5. isChildrenLoaded versus visitChildren Method

To correctly confirm whether the child nodes are loaded, use the isChildrenLoaded method instead of visitChildren.
This would return true:
Node rootNode = navigation.getRootNode(Nodes.visitChildren()).isChildrenLoaded()
This would return false:
Node rootNode = navigation.getRootNode(Nodes.visitNone()).isChildrenLoaded()

5.1.2. Filtering Navigation Nodes

Nodes support a filtering mechanism which simplifies displaying nodes that have specific properties.

Example 5.6. Display User-visible Nodes When Creating a Navigation Portlet

This example displays visible nodes where the user has access to view the page.

Node filtered = node.filter().showDefault();
There are a number of methods available to control what nodes are displayed, and they all start with show . For example, showDefault is an abbreviation for showVisible().showHasAccess(PortalRequest.getInstance().getUser())

Example 5.7. Custom Filter

This example uses a custom filter to only display nodes with a display name that starts with "A".

Node filtered = root.filter().show(new Filter<Node>() {
    public boolean accept(Node node) {
        return node.getDisplayName().startsWith("A");
    }
});

5.2. Creating a Navigation Node

Creating a node uses the following workflow:
  1. Retrieve the parent node you want to add the node to.
  2. Invoke the addChild method on the parent node.
  3. Set the configuration for the node (such as the display name and the page it should link to).
  4. Save it using saveNode on Navigation.

Example 5.8. Creating a Navigation Node

This example creates a node as a child of the home node.
The node is not visible (or persisted) until saveNode is invoked.

Node home = navigation.getNode(NodePath.path("home"));
Node child = home.addChild("mynode");
child.setDisplayName("My Node");
child.setPageId(new PageId("classic", "mypage"));
navigation.saveNode(home);

5.2.1. Navigation Node Visibility

Nodes can be visible, hidden or only visible at a specified publication date. By default a new node is visible.
A node can be hidden with node.setVisibility(false), or only shown until a specific date with node.setVisibility(PublicationDate.endingOn(date)). It is also possible to set a starting date, or set both a starting date and a finishing date.
Changes to node visibility are not shown until saveNode is invoked on the Portal.

5.2.2. Localization

The display name for a node supports localization.

Example 5.9. Setting and English and French Node

This example sets the display name for a node in English and French.

LocalizedString localizedString = node.getDisplayNames();
localizedString.setLocalizedValue(Locale.ENGLISH, "My node");
localizedString.setLocalizedValue(Locale.FRENCH, "Mon noeud");
node.setDisplayNames(localizedString);

navigation.saveNode(node);

5.3. Deleting a Navigation Node

A node is deleted by removing it from the parent node.

Example 5.10. Deleting a Node

This example removes the child with the name mynode.
The node is not removed until saveNode is invoked.

node.removeChild("mynode");
navigation.saveNode(node);

5.4. Moving a Navigation Node

A node can be moved to a different parent, or it can be moved to a different index in the same parent. When moving to a different parent the new parent is required to be in the same tree.

Example 5.11. Move Child Nodes Between Parent and Index Nodes

Move a node from one parent to another.

root.getNode("parent1", "child").moveTo(root.getNode("parent2"));
navigation.saveNode(root);
Move a node to a different index in the same parent.

root.getNode("parent", "child").moveTo(0);
navigation.saveNode(root);
The changes are not visible (or persisted) until saveNode is invoked.
A more convenient way to sort children for a parent is to use the sort method on the parent.

Example 5.12. Sorting by Display Name

This example demonstrates how to sort the children of the root node by their display names.
The changes are not visible (or persisted) until saveNode is invoked.

root.sort(new Comparator<Node>() {
    public int compare(Node o1, Node o2) {
        return o1.getDisplayName().compareTo(o2.getDisplayName());
    }
});
navigation.saveNode(root);

Chapter 6. Page

6.1. Retrieving Pages

A specific page can be retrieved by its PageId . The PageId is constructed by passing either the name for a standard site, the group for a group space or the user for a user dashboard, and the name of the page. Alternatively it can be constructed by passing the SiteId and name of the page.

Example 6.1. Retrieving Specific Pages

This example shows how to retrieve specific pages of a portal.
Page standardSitePage = portal.getPage("classic", "home");
Page groupSpacePage = portal.getSite(new Group("platform", "administrators"), "grouppage");
Page userDashboardPage = portal.getSite(new User("john", "johnspage"));
It is also possible to query for pages using the PageQuery . The PageQuery supports filtering, sorting and pagination when querying for pages.

Example 6.2. Retrieve Pages Using Filtering, Sorting, and Pagination

This example finds pages with a display name that starts with "A".

PageQuery.Builder qb = new PageQuery.Builder();

qb.withSiteType(SiteType.SITE).withFilter(new Filter<Page>() {
    public boolean accept(Page page) {
        return page.getDisplayName().startsWith("A");
    }
});

List<Page> pages = portal.findPages(qb.build());

6.2. Creating a Page

Creating a page uses the following workflow:
  1. Create a new page through the Portal.
  2. Set the configuration for the page (such as localized display names).
  3. Save it using Portal.

Example 6.3. Create a Page

This example creates a new page in the classic site with the name mypage and a non-localized display name.
The page is not visible (or persisted) until portal.savePage is invoked.

Page page = portal.createPage(new PageId("classic", "mypage"));
page.setDisplayName("My Page");
portal.savePage(page);

6.2.1. Setting Permissions for a Page

Associated with a page is an access permission and an edit permission, which controls what users and groups are allowed to access and edit the page respectively.

Example 6.4. Set Permissions for a Page

This example makes a page accessible to everyone, but only editable by users in the /platform/administrators group.
The changed permissions do not take affect until savePage is invoked.

page.setAccessPermission(Permission.everyone());
page.setEditPermission(Permission.any("platform", "administrators"));

portal.savePage(page);

6.3. Page Composition

Red Hat JBoss Portal allows you to add applications and containers to a Page. Applications and containers represent blocks of content visible on a page.
There are three types of application:
  • Portlet
  • WSRP Portlet
  • Gadget
The template for containers renders children in the user interface. The Portal API provides a container with two basic templates for rendering children in rows and columns. A container can hold the following child elements: other containers, and applications.

Example 6.5. Page Containing a Single Application


PageBuilder pageBuilder = portal.newPageBuilder(); 
Application calculator = portal.getApplicationRegistry().getApplication("Gadgets/Calculator"); 
Page page = pageBuilder.child(calculator).siteName("classic").siteType("portal").name("awesome").build();

Example 6.6. Container Template Using Two Column Containers Nested in a Row Container


PageBuilder pageBuilder = portal.newPageBuilder();
ApplicationRegistry appRegistry = portal.getApplicationRegistry(); 
Page page = pageBuilder
         .newRowsBuilder() 
           .newColumnsBuilder()
             .child(appRegistry.getApplication("Gadgets/Calculator")) 
             .child(appRegistry.getApplication("Gadgets/rssAggregator"))
           .buildToParentBuilder()
           
           .newColumnsBuilder()
             .child(appRegistry.getApplication("Gadgets/Calendar"))
             .child(appRegistry.getApplication("Gadgets/Todo"))
           .buildToParentBuilder()
           
         .buildToTopBuilder()
         .siteName("classic")
         .siteType("portal")
         .name("awesome_" + UUID.randomUUID().toString())
         .displayName("Awesome page")
         .showMaxWindow(false)
         .accessPermission(Permission.everyone())
         .editPermission(Permission.any("platform", "administrators"))
         .moveAppsPermission(Permission.everyone())
         .moveContainersPermission(Permission.everyone())
         .build();
The PageBuilder.build() method returns a page object. The page object is an argument for Portal.savePage() method to persist the page.

Note

It is not mandatory to add children initially to a page. A page can be stored without content, and children added later through the JBoss Portal UI or API.

6.4. Deleting a Page

A page is deleted by invoking removePage on the Portal.

6.5. Application

An application is a read-only representation of a gadget, portlet or a WSRP portlet. Application is used to compose pages.
Application can be accessed through the user interface by clicking Application Registry.
To access the complete list of application use the following code:

ApplicationRegistry registry = portal.getApplicationRegistry();
List<Application> applications = registry.getApplications();
In case you know the ID of an application use the following code:

ApplicationRegistry registry = portal.getApplicationRegistry(); 
Application gadgetRss = registry.getApplication(applicationIdFromPreviousInteraction):

Chapter 7. OAuth Provider API

The interface OAuthProvider is a part of the public API. It is the entry point to perform operations on OAuth providers such as Facebook, Google, and Twitter.
Some additional portal configuration is required to use OAuth providers for authentication of users. See OAuth - Authentication with Social Network Accounts chapter in the Administration and Configuration Guide for more details on this topic.
Once a user is authenticated (or their account is linked with the OAuth provider), their access token is saved in the IDM database as a part of their User Profile. It is possible to retrieve the user's OAuth access token through the OAuthProvider interface and run its operations. It is also possible to revoke or validate existing access tokens or send a request to obtain new access tokens with more scopes (privileges).
The following sections present basic uses for the OAuthProvider API. There is an excellent Quickstart available called Portlets for Integration with Social Networks, shipped in the Quickstarts binary.

7.1. Retrieve an Instance of OAuthProvider

You need to retrieve the appropriate instance of OAuthProvider from Portal.
Currently the portal supports three OAuth providers:
  • OAuthProvider.FACEBOOK for Facebook.
  • OAuthProvider.GOOGLE for Google+.
  • OAuthProvider.TWITTER for Twitter.

Example 7.1. Retrieve Facebook OAuth Instance

This example shows the syntax to use when retrieving the Facebook instance of OAuthProvider. This syntax can be adapted for other instances.

Portal portal = PortalRequest.getInstance().getPortal();
OAuthProvider facebookProvider = portal.getOAuthProvider(OAuthProvider.FACEBOOK)

7.2. OAuthProvider Operations

Example 7.2. OAuthProvider Basic Use

This example provides a code snippet that describes some basic uses of the OAuthProvider API.

// Retrieve instance of Google OAuth provider
OAuthProvider googleProvider = PortalRequest.getInstance().getPortal().getOAuthProvider(OAuthProvider.GOOGLE);

// Check if Google was enabled in configuration.properties
if (googleProvider == null) {
    renderResp.getWriter().println("Authentication with Google not available. Ensure your administrator has enabled the service in the portal. See the Administration and Configuration Guide for instructions.");
    return;
}

// Retrieve the key and display name of the social network
String key = googleProvider.getKey();
String friendlyName = googleProvider.getFriendlyName();
renderResp.getWriter().println(friendlyName + " is enabled");

// Retrieve access token of the current user
AccessToken accessToken = googleProvider.loadAccessToken(renderReq.getRemoteUser());

// Check if access token is available. It's the case when this user was registered/authenticated into portal
// through Google+ or if he linked his account with Google+
if (accessToken == null) {
    renderResp.getWriter().println("Your account is not linked with Google+. You can link it in 'Social network' tab of " +
        "user settings or you can authenticate through Google into portal");
    return;
}

// Check if access token is valid and refresh it if necessary
try {
    accessToken = googleProvider.validateTokenAndUpdateScopes(accessToken);
} catch (OAuthApiException oauthException) {
    if (oauthException.getExceptionCode().equals(OAuthApiExceptionCode.ACCESS_TOKEN_ERROR)) {
        renderResp.getWriter().println("Your access token is invalid or has been revoked");
    } else if (oauthException.getExceptionCode().equals(OAuthApiExceptionCode.IO_ERROR)) {
        renderResp.getWriter().println("Network error during the communication with Google");
    }
}

// Check all available scopes
String availableScopes = accessToken.getAvailableScopes();

// Check if we have scope to call Google+ operations
if (!availableScopes.contains("https://www.googleapis.com/auth/plus.login")) {
    // Redirect to Google+ and ask for plus.login scope
    googleProvider.startOAuthWorkflow("https://www.googleapis.com/auth/plus.login");
    return;
}

// Obtain Google API object to call Google plus API operations
Plus service = googleProvider.getAuthorizedSocialApiObject(accessToken, Plus.class);

// Retrieve activities from Google+ wall of user
ActivityFeed activityFeed = service.activities().list("me", "public").execute();
for (Activity activity : activityFeed.getItems()) {
    renderResp.getWriter().println(activity.getTitle());
}

// Revoke the access token. It won't be possible to run any operations with it anymore.
// And your application will be cleared from Google applications of current user on page https://plus.google.com/apps
googleProvider.revokeToken(accessToken);

// Remove the token from the UserProfile of the current user
googleProvider.removeAccessToken(request.getRemoteUser());

7.3. Access to Provider-specific Operations

The oauthProvider.getAuthorizedSocialApiObject() method is used to obtain access to provider-specific operations. This method usually returns objects from a third-party library. Those objects are always initialized with the access token of the current user and can be used to retrieve data from the related social network.
Google
There are two supported types usable as arguments of this method:
com.google.api.services.plus.Plus
- Google Plus API class, which can be used to call operations on Google Plus - see GoogleActivitiesPortlet and GoogleFriendsPortlet in the Quickstart.
com.google.api.services.oauth2.Oauth2
OAuth2 class, which provides operations related to the user, such as obtaining their Google user profile details or obtaining information about his access token - see GoogleUserInfoPortlet in the Quickstart.
Twitter
There is only one supported type for Twitter: twitter4j.Twitter . An instance of this class can be used to retrieve user details, number of tweets, number of friends, last tweets. See TwitterPortlet in the Quickstart.
Facebook
There is no supported type for Facebook. In the Quickstart, the third-party library RestFB is used to perform operations against Facebook.

Chapter 8. REST API

8.1. Overview

The portal provides a REST API to portal entities such as Sites, Pages, and Navigation. All data is sent and received as JSON.

8.1.1. Base URL

There are two base URLs for the REST API. One that will be used if you need to authenticate and one that can be used to access resources anonymously.
Anonymous Base URL
http://<host>:<port>/rest/managed-components/api
Authenticated Base URL
http://<host>:<port>/rest/private/managed-components/api

8.1.2. Authentication

The REST API supports basic authentication. Below is an example of using basic authentication in curl command.
curl -u <username> http://<host>:<port>/rest/private/managed-components/api
This allows users to access resources they have access to depending on the portal entity permissions.

Note

Only members of the group /platform/administrators are allowed to add, update, or delete resources.

8.1.3. Content Type

Since all data is sent and received using JSON, appropriate HTTP headers need to be included in each HTTP request.
For clients receiving data the following HTTP header needs to be included in the request
Accept: application/json
For clients sending data the following HTTP header needs to be included in the request
Content-Type: application/json

8.1.4. Localization

Localization is supported for navigation nodes that have localized displayNames. To specify the language simply add the Accept-Language header to the HTTP request.

Example 8.1. French Header

This examples describes the Accept-Language header used for French localization.
Accept-Language: fr

8.2. Resources

The following sections describe the resources available in the REST API.

Important

All URLs in the Example responses featured in this section are relative to the Base URL.

8.2.1. Sites

8.2.1.1. List Sites

Sites are organized by site type, where site-type can be site , space , or dashboard .

Example 8.2. Listing Sites using GET

This example describes the syntax required to list site types.
GET /api/{site-type}s/

Note

The 's' at the end of the URL is important. Space resolves to /api/spaces/ and dashboard resolves to /api/dashboards/.

Table 8.1. Additional Parameters for site-type

Parameter
Type
Default
Description
emptySites
boolean
false
Indicates to include empty sites (sites w/out pages or navigation)
offset
int
0
The offset of the results in a paginated request
limit
int
15
The maximum number of results to return in a paginated request

Example 8.3. List Sites using GET Request and Response

This example lists the sites that are available and their corresponding resource location. This should be used by clients instead of hard coding every location or URL.
GET /api/sites
HTTP/1.1 200 OK
Content-Type: application/json
[
    {
        "name" : "classic",
        "type" : "site",
        "url" : "/api/sites/classic"
    },
    {
        "name" : "mobile",
        "type" : "site",
        "url" : "/api/sites/mobile"
    }
]

Note

The url element is the full URL but for sake of brevity it is relative for the examples in this document.

8.2.1.2. Retrieve a Site

To retrieve a site named site-name the following syntax applies.
GET /api/{site-type}s/{site-name}

Example 8.4. Retrieve a Site Request and Response

This example shows how to retrieve the Classic site.
GET /api/sites/classic
HTTP/1.1 200 OK
Content-Type: application/json
{
    "name" : "classic",
    "type" : "site",
    "displayName" : "Classic",
    "description" : "GateIn default portal",
    "skin" : "Default",
    "locale" : "en",
    "access-permissions" : ["Everyone"],
    "edit-permissions" : ["*:/platform/administrators"],
    "attributes" : [
        {
            "key" : "showPortletInfo",
            "value" : "false"
        },
        {
            "key" : "sessionAlive",
            "value" : "onDemand"
        }
    ],
    "pages" : {"url" : "/api/sites/classic/pages"},
    "navigation" : {"url" : "/api/sites/classic/navigation"}
}

Note

The url element is the full URL but for sake of brevity it is relative for the examples in this document.

8.2.1.3. Create a site

To create a site named site-name, the following syntax applies:
POST /api/{site-type}s/{site-name}

Example 8.5. Create a Site Request and Response

This example creates a site named 'foo'.
POST /api/sites/foo
HTTP/1.1 200 OK
Content-Type: application/json
{
    "name" : "foo",
    "type" : "site",
    "displayName" : "Basic Portal",
    "description" : "This is basic portal template",
    "skin" : "Default",
    "locale" : "en",
    "access-permissions" : ["Everyone"],
    "edit-permissions" : ["*:/platform/administrators"],
    "attributes" : [{
        "key" : "sessionAlive",
        "value" : "onDemand"
    }],
    "pages" : {"url" : "/api/sites/foo/pages"},
    "navigation" : {"url" : "/api/sites/foo/navigation"}
}

Note

The url element is the full URL but for sake of brevity it is relative for the examples in this document.

8.2.1.4. Delete a site

To delete a site named site-name, the following syntax applies:
DELETE /api/{site-type}s/{site-name}

Example 8.6. Delete a Site Request and Response

This example deletes a site named 'foo'
DELETE /api/sites/foo
HTTP/1.1 200 OK
Content-Type: application/json

8.2.1.5. Update a site

To update a site named site-name, the following syntax applies:
PUT /api/{site-type}s/{site-name}

Example 8.7. Update a Site Request and Response

This example updates the site 'classic' with a new description "The Classic site shipped with the product".
PUT /api/sites/classic

Content-Type: application/json
{
   "description" : "The Classic site shipped with the product"
}
HTTP/1.1 200 OK
Content-Type: application/json
{
    "name" : "classic",
    "type" : "site",
    "displayName" : "Classic",
    "description" : "The Classic site shipped with the product !",
    "skin" : "Default",
    "locale" : "en",
    "access-permissions" : ["Everyone"],
    "edit-permissions" : ["*:/platform/administrators"],
    "attributes" : [
        {
            "key" : "showPortletInfo",
            "value" : "false"
        },
        {
            "key" : "sessionAlive",
            "value" : "onDemand"
        }
    ],
    "pages" : {"url" : "/api/sites/classic/pages"},
    "navigation" : {"url" : "/api/sites/classic/navigation"}
}

Note

The url element is the full URL but for sake of brevity it is relative for the examples in this document.

8.2.2. Pages

8.2.2.1. List Pages

To list pages in a site named site-name, the following syntax applies:
GET /api/{site-type}s/{site-name}/pages

Table 8.2. list-pages Additional Attributes

Parameter
Type
Default
Description
offset
int
0
The offset of the results in a paginated request
limit
int
15
The maximum number of results to return in a paginated request

Example 8.8. List all Pages Request and Response

This example shows how to list all pages for the site 'classic'.
GET /api/sites/classic/pages
HTTP/1.1 200 OK
Content-Type: application/json
[
    {
        "name" : "homepage",
        "siteType" : "site",
        "siteName" : "classic",
        "url" : "/api/sites/classic/pages/homepage"
    },
    {
        "name" : "register",
        "siteType" : "site",
        "siteName" : "classic",
        "url" : "/api/sites/classic/pages/register"
    },
    {
        "name" : "sitemap",
        "siteType" : "site",
        "siteName" : "classic",
        "url" : "/api/sites/classic/pages/sitemap"
    }
]

Note

The url element is the full URL but for sake of brevity it is relative for the examples in this document.

8.2.2.2. Retrieve a page

To retrieve a page named page-name, the following syntax applies:
GET /api/{site-type}s/{site-name}/pages/{page-name}

Example 8.9. Retrieve a Page Request and Response

This example shows how to retrieve the page 'homepage'
GET /api/sites/classic/pages/homepage
HTTP/1.1 200 OK
Content-Type: application/json
{
    "name" : "homepage",
    "displayName" : "Home Page",
    "description" : null,
    "access-permissions" : ["Everyone"],
    "edit-permissions" : ["*:/platform/administrators"]
}

8.2.2.3. Create a page

To create a page named page-name, the following syntax applies:
POST /api/{site-type}s/{site-name}/pages/{page-name}

Example 8.10. Create a New Page Request and Response

The following example creates a page named 'newpage'.
POST /api/sites/classic/pages/newpage
HTTP/1.1 200 OK
Content-Type: application/json
{
    "name" : "newpage",
    "displayName" : "newpage",
    "description" : null,
    "access-permissions" : ["Everyone"],
    "edit-permissions" : ["*:/platform/administrators"]
}

8.2.2.4. Delete a page

To delete a page named page-name, the following syntax applies:
DELETE /api/{site-type}s/{site-name}/pages/{page-name}

Example 8.11. Delete a Page Request and Response

This example deletes a page named 'newpage'.
DELETE /api/sites/classic/pages/newpage
HTTP/1.1 200 OK
Content-Type: application/json

8.2.2.5. Update a page

To update a page named page-name, the following syntax applies:
PUT /api/{site-type}s/{site-name}/pages/{page-name}

Example 8.12. Update Page Description Request and Response

This example updates the page 'homepage' with a new description.
PUT /api/sites/classic/pages/homepage

Content-Type: application/json
{
   "description" : "The default homepage"
}
HTTP/1.1 200 OK
Content-Type: application/json
{
    "name" : "homepage",
    "displayName" : "Home Page",
    "description" : "The default homepage",
    "access-permissions" : ["Everyone"],
    "edit-permissions" : ["*:/platform/administrators"]
}

8.2.3. Navigation

8.2.3.1. Retrieve Navigation

To update a page named site-name, the following syntax applies:
GET /api/{site-type}s/{site-name}/navigation

Table 8.3. Retrieve Navigation Parameters

Parameter
Type
Default
Description
scope
int
n/a
Specifies how many nodes to load (-1 for all)
Below is an example of retrieving the navigation for the site 'classic'

Example 8.13. Request

GET /api/sites/classic/navigation

Example 8.14. Response

HTTP/1.1 200 OK
Content-Type: application/json
{
    "priority" : 1,
    "siteType" : "site",
    "siteName" : "classic",
    "nodes" : [
        {
            "name" : "home",
            "url" : "/api/sites/classic/navigation/home"
        },
        {
            "name" : "sitemap",
            "url" : "/api/sites/classic/navigation/sitemap"
        }
    ]
}

Note

The url element is the full URL but for sake of brevity it is relative for the examples in this document.

Example 8.15. Scope Parameter Request and Response

This example uses the scope parameter to load more nodes which will include the actual node representation in the response
GET /api/sites/classic/navigation?scope=1
HTTP/1.1 200 OK
Content-Type: application/json
{
    "priority" : 1,
    "siteType" : "site",
    "siteName" : "classic",
    "nodes" : [
        {
            "name" : "home",
            "uri" : "/portal/classic/home",
            "isVisible" : true,
            "visibility" : {"status" : "VISIBLE"},
            "iconName" : null,
            "displayName" : "Home",
            "displayNames" : [...]
            "children" : null,
            "page" : {
                "pageName" : "homepage",
                "siteName" : "classic",
                "siteType" : "site",
                "url" : "/api/sites/classic/pages/homepage"
            }
        },
        {
            "name" : "sitemap",
            "uri" : "/portal/classic/sitemap",
            "isVisible" : true,
            "visibility" : {"status" : "VISIBLE"},
            "iconName" : null,
            "displayName" : "SiteMap",
            "displayNames" : [...]
            "children" : null,
            "page" : {
                "pageName" : "sitemap",
                "siteName" : "classic",
                "siteType" : "site",
                "url" : "/api/sites/classic/pages/sitemap"
            }
        }
    ]
}

Note

The url element is the full URL but for sake of brevity it is relative for the examples in this document.

8.2.3.2. Retrieve a node

To retrieve a node with path node-path, the following syntax applies:
GET /api/{site-type}s/{site-name}/navigation/{node-path}

Table 8.4. Node Path Parameters

parameter
type
default
description
scope
int
n/a
Specifies how many nodes to load (-1 for all)

Example 8.16. Retrieve a Node Request and Response

This example retrieves the node 'home'.
GET /api/sites/classic/navigation/home
HTTP/1.1 200 OK
Content-Type: application/json
{
    "name" : "home",
    "uri" : "/portal/classic/home",
    "isVisible" : true,
    "visibility" : {"status" : "VISIBLE"},
    "iconName" : null,
    "displayName" : "Home",
    "displayNames" : [...]
    "children" : null,
    "page" : {
        "pageName" : "homepage",
        "siteName" : "classic",
        "siteType" : "site",
        "url" : "/api/sites/classic/pages/homepage"
    }
}
You can control the displayName value for localized nodes with the Accept-Language header.

Example 8.17. Accept-Language Request and Response

This example retrieves the node 'home' to display the displayName in French.
GET /api/sites/classic/navigation/home
Accept-Language: fr
HTTP/1.1 200 OK
Content-Type: application/json
{
    "name" : "home",
    "uri" : "/portal/classic/home",
    "isVisible" : true,
    "visibility" : {"status" : "VISIBLE"},
    "iconName" : null,
    "displayName" : "Accueil",
    "displayNames" : [...]
    "children" : null,
    "page" : {
        "pageName" : "homepage",
        "siteName" : "classic",
        "siteType" : "site",
        "url" : "/api/sites/classic/pages/homepage"
    }
}

Note

The url element is the full URL but for sake of brevity it is relative for the examples in this document.

8.2.3.3. Create a node

To create a node with path node-path, the following syntax applies:
POST /api/{site-type}s/{site-name}/navigation/{node-path}

Example 8.18. Create a Node Request and Response

This example creates a new node 'newnode' under the home navigation node.
POST /api/sites/classic/navigation/home/newnode
HTTP/1.1 200 OK
Content-Type: application/json
{
    "name" : "newnode",
    "uri" : "/portal/classic/home/newnode",
    "isVisible" : true,
    "visibility" : {"status" : "VISIBLE"},
    "iconName" : null,
    "displayName" : "newnode",
    "children" : null,
    "page" : null
}

8.2.3.4. Delete a node

To delete a node with path node-path, the following syntax applies:
DELETE /api/{site-type}s/{site-name}/navigation/{node-path}

Example 8.19. Delete Node Request and Response

This example deletes a node 'newnode'.
DELETE /api/sites/classic/navigation/home/newnode
HTTP/1.1 200 OK
Content-Type: application/json

8.2.3.5. Update a node

To update a node with path node-path, the following syntax applies:
PUT /api/{site-type}s/{site-name}/navigation/{node-path}

Example 8.20. Update Node Request and Response

Below is an example of updating the node 'home' to point to the sitemap page rather then the homepage.
PUT /api/sites/classic/navigation/home

Content-Type: application/json
{
   "page" : {
        "pageName" : "sitemap",
        "siteName" : "classic",
        "siteType" : "site"
   }
}
HTTP/1.1 200 OK
Content-Type: application/json
{
    "name" : "home",
    "uri" : "/portal/classic/home",
    "isVisible" : true,
    "visibility" : {"status" : "VISIBLE"},
    "iconName" : null,
    "displayName" : "Test",
    "displayNames" : [...]
    "children" : null,
    "page" : {
        "pageName" : "sitemap",
        "siteName" : "classic",
        "siteType" : "site",
        "url" : "/api/sites/classic/pages/sitemap"
    }
}

Note

The url element is the full URL but for sake of brevity it is relative for the examples in this document.

Example 8.21. Update displayName Request and Response

This example updates the English localized displayName to 'Home Page'.
PUT /api/sites/classic/navigation/home

Content-Type: application/json
{
    "displayNames" : [
        {
            "value" : "ホーム",
            "lang" : "ja"
        },
        {
            "value" : "Home Page",
            "lang" : "en"
        },
        {
            "value" : "首頁",
            "lang" : "zh-TW"
        },
        {
            "value" : "Domů",
            "lang" : "cs"
        },
        {
            "value" : "Accueil",
            "lang" : "fr"
        },
        {
            "value" : "主页",
            "lang" : "zh"
        },
        {
            "value" : "Главная",
            "lang" : "ru"
        },
        {
            "value" : "ترحيب",
            "lang" : "ar"
        },
        {
            "value" : "Trang chủ",
            "lang" : "vi"
        },
        {
            "value" : "Home",
            "lang" : "nl"
        },
        {
            "value" : "Inicio",
            "lang" : "es"
        },
        {
            "value" : "Principal",
            "lang" : "pt-BR"
        },
        {
            "value" : "Startseite",
            "lang" : "de"
        },
        {
            "value" : "홈",
            "lang" : "ko"
        },
        {
            "value" : "Home",
            "lang" : "it"
        },
        {
            "value" : "Додому",
            "lang" : "uk"
        },
        {
            "value" : "गृह पृष्‍ठ",
            "lang" : "ne"
        }
    ]
}
HTTP/1.1 200 OK
Content-Type: application/json
{
    "name" : "home",
    "uri" : "/portal/classic/home",
    "isVisible" : true,
    "visibility" : {"status" : "VISIBLE"},
    "iconName" : null,
    "displayName" : "Home Page",
    "displayNames" : [
        {
            "value" : "ホーム",
            "lang" : "ja"
        },
        {
            "value" : "Home Page",
            "lang" : "en"
        },
        {
            "value" : "首頁",
            "lang" : "zh-TW"
        },
        {
            "value" : "Domů",
            "lang" : "cs"
        },
        {
            "value" : "Accueil",
            "lang" : "fr"
        },
        {
            "value" : "主页",
            "lang" : "zh"
        },
        {
            "value" : "Главная",
            "lang" : "ru"
        },
        {
            "value" : "ترحيب",
            "lang" : "ar"
        },
        {
            "value" : "Trang chủ",
            "lang" : "vi"
        },
        {
            "value" : "Home",
            "lang" : "nl"
        },
        {
            "value" : "Inicio",
            "lang" : "es"
        },
        {
            "value" : "Principal",
            "lang" : "pt-BR"
        },
        {
            "value" : "Startseite",
            "lang" : "de"
        },
        {
            "value" : "홈",
            "lang" : "ko"
        },
        {
            "value" : "Home",
            "lang" : "it"
        },
        {
            "value" : "Додому",
            "lang" : "uk"
        },
        {
            "value" : "गृह पृष्‍ठ",
            "lang" : "ne"
        }
    ],
    "children" : null,
    "page" : {
        "pageName" : "homepage",
        "siteName" : "classic",
        "siteType" : "site",
        "url" : "/api/sites/classic/pages/homepage"
    }
}

Note

The url element is the full URL but for sake of brevity it is relative for the examples in this document.

Note

We have to include all the displayNames since the REST API will overwrite the values on the server with whatever we supply in the request.
Since the REST API overwrites the displayNames field we can delete existing displayNames by simply not including them.

Example 8.22. Deleting All Localized Display Names Except for English Request and Response

This example deletes all localized displayNames except for English.
PUT /api/sites/classic/navigation/home

Content-Type: application/json
{
    "displayNames" : [
        {
            "value" : "Home",
            "lang" : "en"
        }
    ]
}
HTTP/1.1 200 OK
Content-Type: application/json
{
    "name" : "home",
    "uri" : "/portal/classic/home",
    "isVisible" : true,
    "visibility" : {"status" : "VISIBLE"},
    "iconName" : null,
    "displayName" : "Home",
    "displayNames" : [
        {
            "value" : "Home Page",
            "lang" : "en"
        }
    ],
    "children" : null,
    "page" : {
        "pageName" : "homepage",
        "siteName" : "classic",
        "siteType" : "site",
        "url" : "/api/sites/classic/pages/homepage"
    }
}

Note

The url element is the full URL but for sake of brevity it is relative for the examples in this document.

Example 8.23. Removing Localized Display Names Request and Response

This example removes all localized displayNames from the home navigation node and replaces them with a non-localized (simple) displayName 'Home'.
PUT /api/sites/classic/navigation/home

Content-Type: application/json
{
    "displayName" : "Home"
}
HTTP/1.1 200 OK
Content-Type: application/json
{
    "name" : "home",
    "uri" : "/portal/classic/home",
    "isVisible" : true,
    "visibility" : {"status" : "VISIBLE"},
    "iconName" : null,
    "displayName" : "Home",
    "displayNames" : [{"value" : "Home"}]
    "children" : null,
    "page" : {
        "pageName" : "homepage",
        "siteName" : "classic",
        "siteType" : "site",
        "url" : "/api/sites/classic/pages/homepage"
    }
}

8.2.4. Working with Templates

Portal administrators use templates to update existing portal, group, and user sites. Templates are also used to create new sites.
The scenarios described here explain the objective of templates.

Example 8.24. Portal Site Template

Consider a portal with 100 portal sites created from template A and 50 portal sites created from template B. A portal administrator wants to add two new pages to sites created from template A and three new pages to sites created from template B.
Using templates the portal administrator can perform this operation by using a single template for sites with two new pages created using template A and a single template for sites with three new pages created using template B.

Example 8.25. Group Site Template

Consider a portal with 20 different navigation groups. A portal administrator wants to add three new pages to ten navigation groups.
Using templates, the portal administrator can perform this operation by applying a single template to ten navigation groups.

Example 8.26. User Site Template

Consider a portal with 1000 users with individual dashboards. A portal administrator wants to modify the layout of the dashboards, or add new pages and navigation nodes for all the users.
In the absence of templates, the portal administrator uses the REST management API to perform this operation. The administrator exports all dashboards, modifies each dashboard and imports the modified dashboards with a merge strategy.
Using templates the portal administrator can perform the same operation by using a single template for all the dashboards.

8.2.4.1. Portal Site Template

To create a Portal site template, download the template using PUT command.
PUT http://<host>:<port>/rest/private/managed-components/template/portal Content-Type: application/zip .
The template file is a .zip file with following structure:
/portal
/portal/template
/portal/template/<name_of_template1>
/portal/template/<name_of_template1>/navigation.xml
/portal/template/<name_of_template1>/pages.xml
/portal/template/<name_of_template1>/portal.xml
/portal/template/<name_of_template2>
/portal/template/<name_of_template2>/navigation.xml
/portal/template/<name_of_template2>/pages.xml
/portal/template/<name_of_template2>/portal.xml
...

Portal site template parameters

importMode
String with default setting as merge.
Possible values: conserve, insert, merge, and overwrite
targetSite
String with default value set to all sites.
If this targetSite attribute is not present template is applied in all portals created from templates defined in .zip. This attribute allows to filter and define in which sites this operation is performed. This attribute can have multiple instances of itself.

Example 8.27. Create a Portal Site Using Curl Tool

Run the curl tool command.
$ curl -i -H "Content-Type: application/zip" -u root:gtn -X PUT -T "template-templateA.zip" http://localhost:8080/rest/private/managed-components/template/portal?importMode=merge&targetSite=site1&targetSite=site2
The output of the command.
Where template-templateA.zip:

portal/template/templateA/navigation.xml
portal/template/templateA/pages.xml
portal/tempalte/templateA/portal.xml
Result:
Updates only site1 and site2 of sites created from templateA.

8.2.4.2. Group Sites Template

To create a Group site template, download the template using the PUT command.
PUT http://<host>:<port>/rest/private/managed-components/template/group Content-Type: application/zip
The template file is a .zip file with following structure:
//group
/group/template
/group/template/navigation.xml
/group/template/pages.xml
/group/template/group.xml
...

Group site template parameters

importMode
String with default setting as merge.
Data import strategy. Possible values: conserve, insert, merge, overwrite
targetSite
String with default value set to all sites.
If this targetGroup attribute is not present template is applied in all group sites. This attribute allows to filter and define the sites on which this operation is performed. This attribute can have multiple instances of itself.

Example 8.28. Create Group Site using Curl Tool

Run the curl tool command.
$ curl -i -H "Content-Type: application/zip" -u root:gtn -X PUT -T "template-group.zip" http://localhost:8080/rest/private/managed-components/template/group?importMode=merge&targetGroup=myorg/mygroup1&targetGroup=myorg/mygroup2
The output of the command
Where template-group.zip:

group/template/navigation.xml
group/template/pages.xml
group/tempalte/group.xml
Result:
"myorg/mygroup1" and "myorg/mygroup2" groups sites created are updated with MERGE strategy using template defined in template-group.zip.

8.2.4.3. User Sites Template

To create a User site template, download the template using the PUT command.
PUT http://<host>:<port>/rest/private/managed-components/template/user Content-Type: application/zip
The template file is a .zip file with following structure:
/user
/user/template
/user/template/navigation.xml
/user/template/pages.xml
/user/template/user.xml
...

Group site template parameters

importMode
String with default setting as merge.
Data import strategy. Possible values: conserve, insert, merge, and overwrite
targetExpr
String with default value set to all users.
This attribute defines a search expression for user sites on which the template operation is performed. This attribute has priority over targetUser attribute.
targetUser
Is a String with default value set to all users.
If targetExpr is not defined, targetUser can define user target for template operation. This attribute can have multiple instances.
dashboardMode
String with default value set to none.
Creates dashboard for user. If this attribute is not present template is applied on users with a dashboard. Possible values: create.

Example 8.29. User Site using Curl Tool

Run the curl tool command.
$ curl -i -H "Content-Type: application/zip" -u root:gtn -X PUT -T "template-user.zip" http://localhost:8080/rest/private/managed-components/template/user?importMode=merge&targetExpr=user*
The output of the command
Where template-user.zip:

user/template/navigation.xml
user/template/pages.xml
user/template/user.xml
Result:
All dashboards from users starting with "user" updated with MERGE strategy using template defined in template-user.zip

Chapter 9. Organization API

9.1. 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.
Diagram describing the handler objects accessible by the OrganizationService component, which are described in the following paragraph.

Figure 9.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 10. 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.

Part III. Portal Development

Table of Contents

11. Portal Life-cycle
11.1. Application Server Start and Stop
11.1.1. Advanced WCI Registration
11.2. The Command Servlet
12. Portal Containers
13. Skinning the Portal
13.1. Skin Components
13.2. Skin Selection
13.2.1. Skin Selection Through the User Interface
13.2.2. Setting the Default Skin within the Configuration Files
13.3. Skins in Page Markups
13.4. The Skin Service
13.4.1. Skin configuration
13.4.2. Resource Request Filter
13.5. The Default Skin
13.6. Creating New Skins
13.6.1. Creating New Skins
13.6.2. Creating a New Portal Skin
13.6.3. Creating a New Window Style
13.6.4. How to Create New Portlet Skins
13.6.5. Create New Portlet Specification CSS Classes
13.7. Tips and Tricks
13.7.1. CSS Hosted on CDN
13.7.2. Easier CSS Debugging
13.7.3. Some CSS Techniques
14. Portal Extension
14.1. How the Shadowing Mechanism Works
14.2. Custom Groovy Template for a Portlet
14.3. Custom Skin for a Portlet
14.3.1. gatein-resources.xml
14.3.2. CSS and Images
14.4. Custom Navigation and Pages
14.4.1. portal-configuration.xml
14.5. Navigation Node Types
14.6. Internationalization of Navigation Nodes
14.7. Custom Internationalization Resource Bundles
14.8. Custom Sign-in Page
15. Visual Identity
16. Data Import Strategy
16.1. Import Strategy Overview
16.1.1. Portal Configuration
16.1.2. Page Data
16.1.3. Navigation Data
17. Right To Left (RTL) Framework
17.1. Groovy templates
17.2. Stylesheet
17.3. Images
17.4. Client Side JavaScript
18. XML Resources Bundles
18.1. XML format
18.2. Portal Support
19. Navigation Controller
19.1. Controller Configuration (controller.xml)
19.1.1. Rendering

Chapter 11. Portal Life-cycle

11.1. Application Server Start and Stop

A portal instance is a set of web applications deployed as EAR and WAR archives. Portlets are also part of an enhanced WAR called a portlet application.
Red Hat JBoss Portal (JBoss Portal) does not require any particular setup for portlets in most common scenarios, and the web.xml file can remain without any JBoss Portal specific configuration.
During deployment, JBoss Portal will automatically 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.

11.1.1. Advanced WCI Registration

The portal 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, particularly Spring based portlets, may have requirements that specific servlets are started before any portlets are initialized. Although portlets and servlet initialization order are meant to be independent of each other, Red Hat JBoss Portal provides a method to circumvent limitations imposed by these specific third-party applications.
As a workaround to this issue, the following advanced features have been integrated into the WCI component;

Advanced Features

Disabling Automatic registration
WCI registers all web applications by default. The portlet container analyzes the registered applications and initializes any portlets contained within them.
To prevent web applications from being automatically registered by the WCI component, set the gatein.wci.native.DisableRegistration context-param to true in the web.xml file of the application.
<!-- 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>

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

Note

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.
This example 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 Red Hat JBoss Portal 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>

Note

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 12. Portal Containers

In a single instance (or cluster) of JBoss Portal, multiple portals can be running and share resources with other portals with two levels of granularity:

title

Portal Containers
A portal container can host multiple sites, and a JBoss Portal instance can host multiple portal containers
Site
A site can have a unique identity, with its own skin applied to a set of pages.
The highest-level component of Red Hat JBoss Portal (JBoss Portal) is the portal container. A portal container can host multiple Sites. Those two components have a unique identifier that can be found in the default URL mapping according to the following scheme: http://localhost:8080/<portalcontainer>/<site>
When creating a website, you can either create a portal container or extend an existing one. Extending an existing portal container, such as the default one provided with JBoss Portal, is the recommended option because you only need to customize it to suit your requirements. Another benefit of the extension method is that upgrades consist of copying the newer distribution archives in place of the originals. This upgrade method is not possible if the distribution archives have been modified.
While running multiple portal containers is possible, it is recommended to keep those on separate installations. Note that multiple websites can run in a single portal container and share some services.
The procedure for creating portal containers and extending existing portal containers is similar: create an enterprise archive (EAR) containing configuration details, runnable code and static resources.

Chapter 13. Skinning the Portal

A portal visual identity is made of HTML produced as a result of portal aggregation (the components that make a page like columns and rows combined with the content produced by the portlets) and associated CSS files.
Red Hat JBoss Portal (JBoss Portal) allows to deploy multiple skins consisting of CSS files, which makes it possible to apply styling to the page compositions and components of a page (portlets). Different skins can be applied to the different websites, also if made available to the users, they can choose their preferred skin.
JBoss Portal 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.

13.1. 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 must include all UI components, except for 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 ways they can be implemented:
Portlet Specification CSS Classes
The portlet specification defines a set of CSS classes available to portlets. The portal 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
The portal 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 in contrast to using the portlet specification CSS classes.

CSS Classes

The window decorators and the default portlet specification CSS classes are separate types of skinning components. They must 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 portlet's web application.

13.2. Skin Selection

13.2.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 User Guide.

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

Example 13.1. Changing a Skin Name

To change the skin to MySkin 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>
    ...
</portal-config>

13.3. Skins in Page Markups

A skin contains CSS styles for the portal's components, but also shares components that may be reused in portlets. When the portal 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>

Note

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

13.4. The Skin Service

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

13.4.1. Skin configuration

The portal 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 is available from http://www.gatein.org/xml/ns/gatein_resources_1_2.

Example 13.2. Sample Skin

This example shows where to define a skin (MySkin) with its CSS location, and how to 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>
    ...

13.4.2. Resource Request Filter

Because of the portal's Right-To-Left support, all CSS files need to be retrieved through a Servlet filter and the web application must 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 the web.xml file:
<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>

Note

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

13.5. The Default Skin

The default skin 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 does not directly define portlet skins. Portlet skins must 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 portal skinning.
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);

Note

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

13.6. Creating New Skins

13.6.1. Creating New Skins

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

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

13.6.2.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.
Screenshot of the Skin Setting window, with the JppSkin selected in the Skins List.

Figure 13.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 screenshot must 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 must have the following updated with the preview icon CSS class. For a skin named MySkin, the following must 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 ;
}

13.6.3. Creating a New Window Style

Window styles are the CSS applied to window decorations. An administrator can decide which style of decoration is applied to the window when they add a new application or gadget to a page.
The Decoration Themes tab selected, with the Simple Style collection highlighted.

Figure 13.2. Window Styles

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

13.6.3.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;
}

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

13.6.4. How to Create New Portlet Skins

Portlets often require additional styles that may not be defined by the portal skin. The portal 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 is required 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.

13.6.4.1. Define a Custom CSS File

The portal does not serve CSS files directly, but uses a filter as well as a skin service in order to:
  • Cache the CSS files.
  • Merge them into one file if possible.
  • Add support for Right-To-Left (RTL) languages.
This causes the portal to create a non-functioning CSS link in html-src-code.

Procedure 13.1. Resolving a non-functioning CSS link

  • 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>
    
    The value of the <application-name> element must match the value of the <display-name> element in web.xml.
    The value of the <portlet-name> element must 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>
    
    The value of the <display-name> element must match the value of the <application-name> element in gatein-resources.xml.
    The ResourceRequestFilter must 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>
    
    The value of the <portlet-name> element must 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
    

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

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

13.7. Tips and Tricks

13.7.1. CSS Hosted on CDN

Red Hat JBoss Portal permits Cascading Stylesheet (CSS) to be loaded from external sources using a Content Delivery Network (CDN).
Declaring external CSS sources is achieved by using @import statements in CSS files that reference the external source (CDN).

Important

Import statements can not be declared in the <css-path> element of the gatein-resources.xml, and must be declared using @import statements directly in CSS files.

Example 13.3. Example CSS Import Statements

In this example, the URLs map to the page protocol that embeds the Cascading Stylesheet

@import url('//my-fast-cdn.com/my-favorite-framework/1.2.3/cool-theme.css');
		

@import url('//another-cdn.com/another-dir/another-style.css');
		

13.7.2. Easier CSS Debugging

By default, CSS files are processed during deployment so that @import and url(...) statements are replaced by the content returned by the imported URLs. These imports are merged into a single CSS file that is stored in a cache and used by SkinService. This reduces the number of HTTP requests from the browser to the server.
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 the portal.

Warning

This option may cause display bugs in some browsers.
./bin/standalone.sh -Dexo.product.developing=true

13.7.3. Some CSS Techniques

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

13.7.3.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 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 desired decoration effect.
A diagram of the Decorator with the BODY cell in the center of a nine-grid pattern. Each cell surrounding the BODY cell describes a different position relative to the BODY cell.

Figure 13.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>

13.7.3.2. Left Margin Left Pattern

Diagram explaining how Left Margin Left Pattern works, which is described below.

Figure 13.4. 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.
<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 14. Portal Extension

When extending an existing portal container, the name of the portal in the extension configuration is the same as the name of the existing portal container. The configuration (and other aspects) of the existing portal container can therefore be shadowed by the extension. Using this approach, many aspects of an available portal container can be customized, such as,
  • Customize groovy template for a portlet.
  • Customize skin for a portlet.
  • Customize CSS and images.
  • Customize navigation and pages.
  • Internationalization of navigation nodes.
  • Customize sign-in page.

14.1. How the Shadowing Mechanism Works

The following diagram illustrates how the shadowing mechanism works.
Diagram explaining how shadowing works, linking portal.war to my-extension.war inside the customized extension, and linking the main gatein/configuration.xml file with the conf/configuration.xml file in the customized extension.

Figure 14.1. Shadowing Mechanism

The extension's EAR conf/configuration.xml file plays a crucial role in shadowing.

Example 14.1. 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">
 <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: note that a Portal Container called "portal" 
                          already exists in the default GateIn installation. Therefore, we actually redefine 
                          that existing Portal Container here. -->
                     <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>gatein-domain</string>
                     </field>
                     <field name="externalSettingsPath">
                         <string>configuration.properties</string>
                     </field>
                     <!--
                         The list of all the context names that are needed to initialize the Portal Container properly.
                         The order of the dependencies will define the initialization order and also the order for 
                         loading resources. When a resource with the same path, say /dir/subdir/resource, is available 
                         in more than one of the listed contexts, the one from the context closest to the end of this list 
                         will be chosen. Here we want the resources available in gatein-portal-extension to win over all 
                         other resources. Therefore we have added gatein-portal-extension as the last element of the list.
                     -->
                     <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>gwtGadgets</string>
                             </value>
                             <value>
                                 <string>eXoGadgetServer</string>
                             </value>
                             <value>
                                 <string>rest</string>
                             </value>
                             <value>
                                <string>web</string>
                             </value>
                             <value>
                                 <string>gatein-portal-extension</string>
                             </value>
                         </collection>
                     </field>
                 </object>
             </object-param>
         </init-params>
     </component-plugin>
 </external-component-plugins>
</configuration>
In this code extract, the name parameter of the PortalContainerDefinition is set to "portal" . Because a portal container called "portal" already exists in the default portal installation, the container name is redefined in the above file.
Other resources available in an existing portal container can be customized in a similar way. See the following sections for more details.

14.2. Custom Groovy Template for a Portlet

Shadowing of built-in Groovy templates is controlled by the order of <dependencies> in the conf/configuration.xml file.
If a template with the same path (for example, /dir/subdir/template.gtmpl) is available in more than one of the dependencies, the template from the context closest to the end of the list of dependencies is chosen. To ensure that the template created in Procedure 14.1, “Customizing HomePagePortlet” is chosen, gatein-portal-extension is added as the last element in the list.

Note

This section mentions code from the Portal Extension Examples, which is available for download as part of a valid Red Hat subscription.

Procedure 14.1. Customizing HomePagePortlet

Complete this procedure to change the text and layout used in the default HomePagePortlet shipped with the platform in a custom extension.
  1. Copy $JPP_HOME/gatein/gatein.ear/portal.war/templates/groovy/webui/component/UIHomePagePortlet.gtmpl
  2. Paste the file into the war/src/main/webapp/templates/groovy/webui/component/ folder of the extension EAR.

    Important

    The file location in the custom extension must mirror the original location of the file for shadowing to work correctly.
  3. Make the desired changes to the text and layout of the HomePagePortlet in the custom extension.
  4. Deploy the extension EAR to the portal platform. The HomePagePortlet mirrors the changes made in the custom extension.

14.3. Custom Skin for a Portlet

Note

This section mentions code from the Portal Extension Examples available for download as part of a valid Red Hat subscription.
The same guidelines that apply when creating a new portlet skin also apply when creating a custom skin for a built-in portlet distributed with the portal.
Declare the skin first in the gatein-resources.xml file, and then create the CSS and other supporting files such as backgrounds.
Shadowing of built-in Groovy templates is controlled by the order of <dependencies> in the configuration of the Portal Container (or Extension).
When the HomePagePortlet skin defined in web.war is available in more than one of the dependencies, the template from the context closest to the end of the list of dependencies is chosen. To ensure that the skin available in gatein-portal-extension is chosen, gatein-portal-extension is added as the last element in the list.

14.3.1. gatein-resources.xml

gatein-resources.xml is located in the war/src/main/webapp/WEB-INF sub-folder of the Portal Extension Quickstart project.

Example 14.2. gatein-resources.xml


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

<!-- The platform will load this CSS for HomePagePortlet instead of the default one. -->
<portlet-skin>
    <!-- 
        The name of the web application containing the portlet.xml file in which
        BannerPortlet is definded. The application name is usually the same as 
        the name of the WAR through which the application was deployed. 
    -->
    <application-name>web</application-name>
    <!-- <portlet-name> value from the portlet.xml referenced above -->
    <portlet-name>HomePagePortlet</portlet-name>
    <skin-name>Default</skin-name>
    <css-path>/templates/skin/webui/component/UIHomePagePortlet/DefaultStylesheet.css</css-path>
</portlet-skin>

14.3.2. CSS and Images

In the gatein-resources.xml file in Section 14.3.1, “gatein-resources.xml”, the custom skin is set to use the CSS file /templates/skin/webui/component/UIHomePagePortlet/DefaultStylesheet.css which translates to the Portal Extension Quickstart project war/src/main/webapp/templates/skin/webui/component/UIHomePagePortlet/DefaultStylesheet.css file. In the CSS file itself, image files use relative paths as demonstrated in Example 14.3, “DefaultStylesheet.css”:

Example 14.3. DefaultStylesheet.css


.UIHomePagePortlet .TRContainer .DotLine {
 background: url("DefaultSkin/background/Line.gif") no-repeat -2px top;
 height: 1px;
 width: 182px;
 margin: 7px auto;
}

14.4. Custom Navigation and Pages

Note

This section mentions code from the Portal Extension Examples available for download as part of a valid Red Hat subscription.

14.4.1. portal-configuration.xml

This configuration file is located in the war/src/main/webapp/WEB-INF/conf/sample-ext/portal directory of the Portal Extension quickstart. It is the starting point for navigation customization. There is normally no need to change any configuration in this file, although it is useful to know the location of this file.
For more information about portal-configuration.xml, see the "Portal Navigation Configuration" section in the Administration and Configuration Guide.

14.5. Navigation Node Types

There are three types of navigation nodes, as described in Figure 14.2, “Types of Navigation Nodes” :
The JBoss Portal Platform home screen, with the Site tab (1), Group menu (2), and Dashboard menu (3) called out.

Figure 14.2. Types of Navigation Nodes

1 (Site)
Navigation nodes of the current site.
2 (User Group)
Navigation nodes of user groups which the current user is a member.
3 (User)
Navigation nodes of the current user.

Example 14.4. Adding a Navigation Node to a Site Navigation

The file war/src/main/webapp/WEB-INF/conf/sample-ext/portal/portal/classic/navigation.xml contains the classic site navigation which can be modified.

<node-navigation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_3 http://www.gatein.org/xml/ns/gatein_objects_1_3" xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_3">
 <!-- Priority has no significance in site navigations. But it is required. See GTNPORTAL-2751 -->
 <priority>1</priority>
 
 <!-- 
     In this file, we are adding a navigation node to the navigation of the site called "classic". 
     The site-wide navigation is used e.g. in NavigationPortlet. 
     Visibility of navigation nodes depends on <access-permissions> set in the sibling pages.xml file.
     See also portal.configuration section of the file ../../portal-configuration.xml. 
 -->
 <page-nodes>
     <node>
         <name>page-just-added</name>
         <!-- 
             #{portal.extension.justAddedPage} is  place holder for an internationalized string.
             See WEB-INF/classes/locale/navigation/portal/classic_en.properties
         -->
         <label>#{portal.extension.justAddedPage}</label>
     </node>
</page-nodes>
</node-navigation>
By appending the page-just-added navigation node into the portal navigation, the new site navigation node is now visible in the navigation bar.
Navigation menu, with Page Just Added now visible as a site navigation option.
The page-just-added navigation node refers to portal::classic::justAddedPage which is defined in the pages.xml file located in the same directory as the navigation.xml file.
<page-set xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_3 http://www.gatein.org/xml/ns/gatein_objects_1_3"
 xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_3">
 <page>
     <name>justAddedPage</name>
     <title>Page Just Added</title>
     <!-- This page and any navigation nodes refering to it will be visible to all visitors (signed in or anonymous). -->
     <access-permissions>Everyone</access-permissions>
     <edit-permission>*:/platform/administrators</edit-permission>
     <portlet-application>
         <portlet>
             <application-ref>gatein-portal-extension</application-ref>
             <portlet-ref>JustAddedPortlet</portlet-ref>
         </portlet>
         <title>Just Added Portlet</title>
         <access-permissions>Everyone</access-permissions>
         <show-info-bar>false</show-info-bar>
         <show-application-state>false</show-application-state>
         <show-application-mode>false</show-application-mode>
     </portlet-application>
 </page>
</page-set>
Note that page-just-added navigation node and the related pages are visible to all portal visitors (signed in or anonymous) due to <access-permissions> set to Everyone in the above file.

Example 14.5. Adding a Navigation Node to a Group Navigation

To add a new navigation node visible to the members of /platform/administrators group, create a navigation.xml file located in war/src/main/webapp/WEB-INF/conf/sample-ext/portal/group/platform/administrators/navigation.xml. The name of the user group /platform/administrators forms part of the path.

<node-navigation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_3 http://www.gatein.org/xml/ns/gatein_objects_1_3" xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_3">
<!-- Priority is currently ignored for importMode merge. But it is required. See GTNPORTAL-2751 -->
<priority>1</priority>

<page-nodes>
    <node>
        <name>admins-sitemap</name>
        <!-- 
            #{platform.administrators.adminsSitemap} is  place holder for an internationalized string.
            See WEB-INF/classes/locale/navigation/group/platform/administrators_en.properties
        -->
        <label>#{platform.administrators.adminsSitemap}</label>
        <page-reference>group::/platform/administrators::adminsSitemap</page-reference>
    </node>
</page-nodes>
</node-navigation>
The navigation node admins-sitemap refers to adminsSitemap page defined in the sibling pages.xml file:
<page-set xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_3 http://www.gatein.org/xml/ns/gatein_objects_1_3"
 xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_3">
<!-- 
    adminsSitemap page contains just SiteMapPortlet. There is nothing special about it. 
    It is here just to demonstrate the adding of nodes to group navigation in the sibling 
    navigation.xml file.
-->
<page>
    <name>adminsSitemap</name>
    <title>Admins' Sitemap</title>
    <access-permissions>*:/platform/administrators</access-permissions>
    <edit-permission>*:/platform/administrators</edit-permission>
    <portlet-application>
        <portlet>
            <application-ref>web</application-ref>
            <portlet-ref>SiteMapPortlet</portlet-ref>
        </portlet>
        <title>Sitemap</title>
        <access-permissions>*:/platform/administrators</access-permissions>
        <show-info-bar>false</show-info-bar>
    </portlet-application>
</page>
</page-set>

Example 14.6. Adding a Navigation Node to a User Navigation

To add a new navigation node visible to a particular user, for example root create a navigation.xml file located in war/src/main/webapp/WEB-INF/conf/sample-ext/portal/user/root/navigation.xml . Note that the name of the user root is a part of the path.
<node-navigation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_3 http://www.gatein.org/xml/ns/gatein_objects_1_3" xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_3">
<!-- Priority has no significance in user navigations. But it is required. See GTNPORTAL-2751 -->
<priority>1</priority>

<page-nodes>
    <node>
        <name>roots-extra-dashboard</name>
        <label>#{user.root.RootsExtraDashboard}</label>
        <page-reference>user::root::roots-dashboard</page-reference>
    </node>
</page-nodes>
</node-navigation>
The navigation node roots-extra-dashboard refers to roots-dashboard page defined in the sibling pages.xml file:
<page-set xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_3 http://www.gatein.org/xml/ns/gatein_objects_1_3"
xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_3">
<!-- 
    roots-dashboard page contains just DashboardPortlet. There is nothing special about it. 
    It is here just to demonstrate the adding of nodes to user navigation in the sibling 
    navigation.xml file.
-->
<page>
    <name>roots-dashboard</name>
    <title>Root's Extra Dashboard</title>
    <access-permissions>*:/platform/users</access-permissions>
    <edit-permission>*:/platform/administrators</edit-permission>
    <portlet-application>
        <portlet>
            <application-ref>dashboard</application-ref>
            <portlet-ref>DashboardPortlet</portlet-ref>
        </portlet>
        <title>Root's Extra Dashboard</title>
        <access-permissions>*:/platform/users</access-permissions>
        <show-info-bar>false</show-info-bar>
    </portlet-application>
</page>
</page-set>

14.6. Internationalization of Navigation Nodes

As you can see in the above navigation.xml files the labels of navigation nodes can be internationalized. The files containing translation resources live under war/src/main/webapp/WEB-INF/classes/locale/navigation . The directory layout is similar to the one used for navigation.xml and pages.xml files:
Note that the navigation resource bundles also need to be named in init.resources parameter of BaseResourceBundlePlugin in war/src/main/webapp/WEB-INF/conf/sample-ext/common/common-configuration.xml :

Example 14.7. common-configuration.xml containing declarations of navigation resource bundles

<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 ResourceBundleService -->
    <target-component>org.exoplatform.services.resources.ResourceBundleService</target-component>
    <component-plugin>
        <!-- The name of the plugin -->
        <name>Sample ResourceBundle Plugin</name>
        <!-- The name of the method to call on the ResourceBundleService in order to register the ResourceBundles -->
        <set-method>addResourceBundle</set-method>
        <!-- The full qualified name of the BaseResourceBundlePlugin -->
        <type>org.exoplatform.services.resources.impl.BaseResourceBundlePlugin</type>
        <init-params>
            <values-param>
                <name>init.resources</name>
                <description>Initiate the following resources during the first launch.</description>
                <value>locale.portal.extension</value>
                <value>locale.navigation.user.root</value>
                <!-- 
                    Note that we actually do not need to name locale.navigation.portal.classic and 
                    locale.navigation.group.platform.administrators here as they are in init.resources 
                    of the default GateIn installation. But it makes no harm to include them once again here.
                -->
                <value>locale.navigation.portal.classic</value>
                <value>locale.navigation.group.platform.administrators</value>
           </values-param>
        </init-params>
    </component-plugin>

14.7. Custom Internationalization Resource Bundles

Note

This section mentions code from the Portal Extension Examples available for download as part of a valid Red Hat subscription.
There are two resource bundle customization scenarios possible with a Portal Extension:
  • to add new resource bundle items to the ones available in the default portal installation.
  • to assign new values to resource bundle items available in default portal installation.
Both scenarios can be demonstrated using the locale.portal.extension resource bundle. To use this bundle, include both init.resources and portal.resource.names parameters for the BaseResourceBundlePlugin<type> directive in war/src/main/webapp/WEB-INF/conf/sample-ext/common/common-configuration.xml.

Example 14.8. common-configuration.xml containing declarations of navigation resource bundles

<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd http://www.exoplatform.org/xml/ns/kernel_1_2.xsd" xmlns="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd">
<external-component-plugins>
    <!-- The full qualified name of the ResourceBundleService -->
    <target-component>org.exoplatform.services.resources.ResourceBundleService</target-component>
    <component-plugin>
        <!-- The name of the plugin -->
        <name>Sample ResourceBundle Plugin</name>
        <!-- The name of the method to call on the ResourceBundleService in order to register the ResourceBundles -->
        <set-method>addResourceBundle</set-method>
        <!-- The full qualified name of the BaseResourceBundlePlugin -->
        <type>org.exoplatform.services.resources.impl.BaseResourceBundlePlugin</type>
        <init-params>
            <values-param>
                <name>init.resources</name>
                <description>Initiate the following resources during the first launch.</description>
                <value>locale.portal.extension</value>
                <value>locale.navigation.user.root</value>
                <!-- 
                    Note that we actually do not need to name locale.navigation.portal.classic and 
                    locale.navigation.group.platform.administrators here as they are in init.resources 
                    of the default GateIn installation. But it makes no harm to include them once again here.
                -->
                <value>locale.navigation.portal.classic</value>
                <value>locale.navigation.group.platform.administrators</value>
            </values-param>
            <values-param>
                <name>portal.resource.names</name>
                <description>These resources are merged to a single resource bundle which is accessible from anywhere 
                    in GateIn. All these keys are located in the same bundle, which is separated from the navigation 
                    resource bundles.</description>
                <value>locale.portal.extension</value>
            </values-param>
        </init-params>
    </component-plugin>
</external-component-plugins>
</configuration>
The English version of locale.portal.extension is located in war/src/main/webapp/WEB-INF/classes/locale/portal/extension_en.properties.

Example 14.9. English version of locale.portal.extension

UIHomePagePortlet.Label.Slogan=Congratulations!
UIHomePagePortlet.Label.SubSlogan=You have just installed the GateIn Portal Extension
UIHomePagePortlet.Label.Title=Sign in as:
UIHomePagePortlet.Label.SubSlogan is a new key which is not normally available in the default portal installation. However, UIHomePagePortlet.Label.Slogan is redefined in extension_en.properties shown above.
In $JPP_HOME/gatein/gatein.ear/web.war/WEB-INF/classes/locale/portlet/web/GroovyPortlet_en.properties it is already defined as:
UIHomePagePortlet.Label.Slogan=The Best of eXo and JBoss Portal<div>GateIn #{gatein.version}</div>
Within the Portal Extension, the new value Congratulations! is appended to it.

14.8. Custom Sign-in Page

It is possible to customize the sign-in page displayed to a user by configuring an extension.

Procedure 14.2. Creating a Custom Sign-in Page

  1. Copy $JPP_HOME/gatein/gatein.ear/portal.war/login/jsp/login.jsp.
  2. Create a custom extension, and paste the file into Custom_Extension.ear/Custom-WAR.war/login/jsp/login.jsp.

    Important

    All logic relating to the sign-in page must be contained within the login page. Failing to do this may cause the portal to operate incorrectly.

    Note

    The file path must be the same in both the primary path and the extension path for mirroring to work correctly.
  3. Edit the login.jsp file with the customization required for the extension.

Procedure 14.3. Creating a Custom Modal Window

  1. Copy $JPP_HOME/gatein/portal.war/groovy/portal/webui/UILoginForm.gtmpl.
  2. In the custom extension created in Procedure 14.2, “Creating a Custom Sign-in Page”, paste the file into Custom_Extension.ear/Custom-WAR.war/groovy/portal/webui/UILoginForm.gtmpl.

    Note

    The file path must be the same in both the primary path and the extension path for mirroring to work correctly.
  3. Edit the UILoginForm.gtmpl file with the customization required for the extension.
Once these modifications are made, deploy the custom extension and test the functionality of the sign in page.

Chapter 15. Visual Identity

Visual Identity of GateIn Portal is given by applied skins. Skins are packages consisting of CSS files, background images, etc. Skins can be applied to whole Sites or Portlets. It is not only possible to deploy multiple skins and apply different skins to individual Sites and Portlets. The skin selection can also be made accessible to the users so that they can choose their preferred skin.

Note

See Chapter 13, Skinning the Portal for further details.

Chapter 16. Data Import Strategy

In the portal extension mechanism, developers can define an extension that portal data can be customized by.
There are several cases when it can be useful to define how to customize the Portal data; for example modifying, overwriting or inserting data into the data defined by the Portal. The portal also defines several modes for each case so that a developer only has to clarify the use-case and configure the extensions.

16.1. Import Strategy Overview

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.

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

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

16.1.3. Navigation Data

The navigation data import strategy is processed at the import mode level as follows:

title

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 portal 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, the portal provides a way to insert or merge new data to the initiated navigation tree or a subtree.
Diagram of the first data import strategy, with the foo, daa, and juu node labels branching off from the root node.

Figure 16.1. Data Import Strategy Navigation1

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>
<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>
Diagram of the second data import strategy, with the foo and daa node labels branching off from the root node.

Figure 16.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
Diagram of the first data import strategy, with the foo, daa, and juu node labels branching off from the root node.

Figure 16.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:
Diagram of the insert data import strategy navigation, with the foo, daa, and juu node labels branching off from the root node. The bar node is added in this instance.

Figure 16.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.
Diagram of the insert data import strategy navigation, with the foo, daa, and juu node labels branching off from the root node. The bar node is added in this instance.

Figure 16.5. Data Import Strategy Navigation_Merge

Case 4: OVERWRITE
Everything will be destroyed and replaced with new data if the OVERWRITE mode is used.
Diagram of the second data import strategy, with the foo and daa node labels branching off from the root node.

Figure 16.6. Data Import Strategy Navigation_2

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

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

17.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 This example 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 17.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 17.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 */

17.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 */

17.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 18. XML Resources Bundles

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.

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

18.2. 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 19. Navigation Controller

The navigation controller now provides non ambiguous URLs for portal managed resources as opposed to previous releases where different resources were available for a single URL,for example, navigation. These resources were dependent on private navigation such as groups and dashboard.
Navigation controller provides a flexible and configurable mapping by decoupling the http request from the portal request. Previously, both the requests were tightly coupled, for instance the URL for a site had to begin with /public/{sitename} or /private/{sitename}.
Navigation controller allows portal administrators to create user-friendly URL by configuring the http requests.
The WebAppController converts http request into a portal request by decoupling the http request to 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 http request data can be encoded in the request path or as a query parameter. The controller allows the portal to route a request based on a set of parameters instead of the servlet request.
The controller configuration is declared in an XML file to allow easy configuration of the routing table. It is processed into an internal data structure that is used to perform resolution (routing or rendering).
The routing rules for controller configuration are located in controller-configuration.xml The location of the configuration file is determined by the controller.config property.
The WebAppController loads and initializes the controller as shown:

<!-- conf/portal/controller.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>
Based on the extenstion mechanism the extension project can define its own routing table.
The controller.xml can be modified and reloaded at runtime to test different configurations and provides 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 the portalcontroller REST name.

Attributes and Operations

configurationPath
Attribute (read only), which specifies the configuration path of the controller XML file.
loadConfiguration
Operation, which loads a new configuration file from a specified XML path.
reloadConfiguration
Operation, which reloads the configuration file.
findRoutes
Operation, which routes the request argument through the controller and returns a list of 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.

19.1. 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.
Route parameters define a fixed value associated 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.

<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) and it renders the (gtn:handler=portal) map as the "/foo" URL. This example demonstrates two concepts, exact path matching ("/foo") and route parameters ("gtn:handler").
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 :.
<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.
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.

<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.
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.
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.
When an HTML error code is returned as a response to a URL request (for example, 404 Not Found), the portal 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, implement the configuration described in the procedure.

Procedure 19.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 requiring a 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 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 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.
The portal defines a set of parameters in the 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. It determines which handler will process the request immediately 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.
  • default
    http redirection to the default portal of the container.
  • staticResource
    Serve static resources such as image, CSS or javascriptfiles in portal.war.
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.

19.1.1. Rendering

The controller is designed to render a Map<QualifiedName, String> as a http URL according to the controller's routing table. Additional components are required to integrate the controller into the WebUI Framework of the portal.

19.1.1.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 a constant and a type-safe object that allows 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.

19.1.1.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();

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

19.1.1.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>");

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

19.1.1.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 IV. Gadget Development

Table of Contents

20. Gadgets in Portal
20.1. Sources to Develop Gadgets
21. Portlet Development Resources
21.1. JSR-168 and JSR-286 overview
21.1.1. Portal Pages
21.1.2. Rendering Modes
21.1.3. Window States
21.2. Tutorials
21.2.1. Deploying your first portlet
21.2.2. JavaServer Pages Portlet Example
22. Portlet Development
22.1. Starting a Portlet Project
22.1.1. The Bill of Materials (BOM) Concept
22.1.2. Using the BOM
22.2. Building and Deploying Portlets
22.3. Standard Portlet Development (JSR-286)
22.3.1. Java Configuration
22.3.2. portlet.xml
22.3.3. web.xml
22.3.4. Building and Deploying Portlets
23. Basic JSF Portlet Development
23.1. Example Code
23.1.1. pom.xml
23.1.2. JSF Template Files
23.1.3. Java Beans
23.1.4. portlet.xml
23.1.5. web.xml
23.1.6. Custom CSS
23.1.7. Internationalization
23.2. Further Steps
23.3. See also
24. JSF Portlet Development with RichFaces
24.1. Example Code
24.1.1. pom.xml
24.1.2. JSF Template Files
24.1.3. Java Beans
24.1.4. portlet.xml
24.1.5. web.xml
24.1.6. Custom CSS
24.1.7. Internationalization
24.2. Further Steps
24.3. See also
25. Portlets with JSF and CDI
26. CDI Portlet Development
26.1. GenericPortlet and Portlet Filter Injection
26.2. Portlet CDI Scopes
26.2.1. @PortletLifecycleScoped
26.2.2. @PortletRedisplayScoped
27. Portlet Filter
27.1. Global Metadata Elements
27.1.1. Global Metadata Elements
27.1.2. Configuring a Portlet Filter
28. Portlet Bridge
28.1. JBoss Portlet Bridge
28.2. Portlet application
28.3. Extensions
28.4. Examples
28.5. Render Policy Parameters
28.6. Facelets Configuration
28.7. JSP-only Configuration
28.8. RichFaces Local and Remote Portlet Support
28.9. Sending and Receiving Events
28.10. Sending Events
28.11. Receiving Events
28.12. Public Render Parameters
28.13. Saving Bridge Request Scope after Render complete
28.14. PRP portlet configuration
28.15. Application configuration
28.16. Portlet Session
28.17. Resource serving
28.18. Serving JSF Resources in a Portlet
28.19. Expression Language Reference
28.20. Expression Language Configuration
28.21. Developing Portlets with the Bridge
28.21.1. Implementing Portlet Bridge
28.21.2. Declaring Artifact Dependencies
28.21.3. Declaring Depchain Dependencies
28.21.4. Deploying Portlet Bridge Portlets
28.21.5. Disable Automatic Portlet Bridge Injection
28.21.6. Supported Portlet Tags
28.21.7. Excluding Attributes from the Bridge Request Scope
28.21.8. Prevent Resources Being Added to Portal Page Head
28.21.9. JSF Facelet View
28.21.10. Error Handling
28.21.11. Switching Portlet Modes
28.21.12. Navigating to a mode's last viewId
28.21.13. Using Wildcards to Identify the Rule Target
28.21.14. Clearing the View History when Changing Portlet Modes
28.21.15. Communication Between Portlets
28.21.16. Storing Components in PortletSession.APPLICATION_SCOPE
28.21.17. Using the PortletSession
28.21.18. Linking to a Facelets page within the Same Portlet
28.21.19. Redirecting to an External Page or Resource
28.21.20. Using Provided EL Variables
29. Navigation Portlet Using the Public API
29.1. Example Code
29.1.1. Required Java Classes
29.1.2. Required JSP
29.1.3. Required JavaScript
29.1.4. Further Steps
29.2. See also
30. Using Data from Social Networks in Portlets
30.1. Social Portlets Example Code
30.1.1. Prerequisites
30.2. Retrieving the Access Token
30.3. Facebook
30.4. Google+
30.5. Twitter
30.6. Configuration and Deployment Descriptors
30.7. Further Steps
31. Spring Framework Portlet Development

Chapter 20. Gadgets in Portal

A gadget is a mini web application, embedded in a web page and running on an application server platform. These small applications help users perform various tasks. Red Hat JBoss Portal Platform supports gadgets, such as Todo, Calendar, Calculator, Weather Forecasts, and RSS Reader.
In the context of the portal, gadgets are defined by the Google OpenSocial specifications. The portal framework includes Apache Shindig 2.0, which supports version 0.9 and 1.0 of OpenSocial.
Within a portal, it is possible to embed any OpenSocial gadget in a page, or in a user's dashboard.
Gadgets can be added to the application registry, and links can be added to the mini-composer (see the User Guide for more information about this feature).

20.1. Sources to Develop Gadgets

OpenSocial gadgets are built using standard HTML and JavaScript. The container offers an API. API documentation is available at http://opensocial-resources.googlecode.com/svn/spec/1.0/Core-Gadget.xml
A gadget has limited knowledge of its context (the portal) so the gadget integration within the portal is limited, for example, visual integration.
Google Web Toolkit (GWT) applications can be used as gadgets. GWT simplifies the process of creating user friendly applications.

Important

GWT applications usage is not recommended as they are not part of the standard Red Hat JBoss Portal support program.
For more information on gadget configuration and usage see Administration and Configuration guide.

Chapter 21. Portlet Development Resources

21.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.
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.
The Red Hat JBoss Portal includes a container that supports both versions.
This chapter gives a brief overview of the Portlet Specifications. Portlet developers are strongly encouraged to read the JSR-286 Portlet Specification, which is available at http://www.jcp.org/en/jsr/detail?id=286.
The portal offers complete JSR-286 compliance. Any compliant JSR-168 or JSR-286 portlet operates inside the portal as mandated by the respective specifications.

21.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:
Diagram describing the different components that comprise a Portal page. The diagram shows the difference between decorations and controls, portlet fragments, portlet windows, and the overall portlet page.

Figure 21.1. Portal Specification

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

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

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

21.2.1. Deploying your first portlet

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

21.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.1.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.

21.2.1.2. Package Structure

Like other Java EE applications, the Red Hat JBoss Portal 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 21.1. Portlet Directory Structure Explanation

|-- SimplestHelloWorld-0.0.1.war
|   `-- WEB-INF
|       |-- classes
|       |   `-- org
|       |       `-- jboss
|       |           `-- portal
|       |               `-- portlet
|       |                   `-- samples
|       |                       `-- SimplestHelloWorldPortlet.class
|       |-- portlet.xml
|       `-- web.xml

Directory Structure Elements

SimplestHelloWorldPortlet.class
The compiled Java class, which implements javax.portlet.Portlet through javax.portlet.GenericPortlet.
portlet.xml
The mandatory descriptor file for portlets, which is used during deployment.
web.xml
The mandatory descriptor for web applications.

21.2.1.3. Portlet Class

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

Note

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.

Example 21.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.

21.2.1.4. Application Descriptors

The portal 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 21.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>

Notes

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.

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

Note

The example used in this section is available in the directory of the portal sources package.

Procedure 21.1. Compile JavaServer Pages Portlet

  1. Obtain the Red Hat JBoss Portal 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.1.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.
The JSP Hello User portlet displayed on a new portlet page. The portlet has a single available field (Name) and a Submit button.

Figure 21.2. Create New Portal Page and Add Portlet

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

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

21.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 21.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 is required.
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.
Diagram showing how 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.

Figure 21.3. Render Phase Process

Chapter 22. Portlet Development

The Red Hat JBoss Portal (JBoss Portal) interface is fully customizable with applications called portlets. Application development can be done by using the plain Portlet specification JSR-286. It is also possible to use the JBoss Portlet Bridge to write applications with JavaServerFaces (JSF), RichFaces or Seam.
Learn how to set up your project in a robust and effective way by using the JBoss Portal Bill of Materials (BOM).

22.1. Starting a Portlet Project

The Red Hat JBoss Portal (JBoss Portal) interface is fully customizable with applications called portlets. Application development can be done by using the plain Portlet specification JSR-286. It is also possible to use the JBoss Portlet Bridge to write applications with JavaServerFaces (JSF), RichFaces or Seam.
Learn how to set up your project in a robust and effective way by using the JBoss Portal Bill of Materials (BOM).

22.1.1. The Bill of Materials (BOM) Concept

To make managing dependencies easier, the BOM needed for developing typical portlet applications is available. The BOM is a Maven pom.xml file which specifies the versions of dependencies compatible with or provided by the Red Hat JBoss Portal (JBoss Portal).

Note

The JBoss Portal BOM is closely tied to JBoss EAP for maximum compatibility with the Red Hat JBoss Middleware stack.

22.1.2. Using the BOM

Below is the pom.xml file from the "Simplest Hello World" example portlet. This file contains all the necessary details.
In the <dependencyManagement> section, it declares gatein-3.5-bom as a <dependency> with import as the defined <scope>.
It indicates that the dependency will de facto be replaced with the dependencies in its dependencyManagement section.
As a result it is possible to declare the javax.portlet:portlet-api dependency in the <dependencies> section of the "Simplest Hello World" pom.xml, without specifying its <version>, <type> or <scope>. All of those parameters are managed by gatein-3.5-bom.

Example 22.1. Simplest Hello World Portlet


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

<artifactId>simplest-hello-world-portlet</artifactId>
<groupId>org.gatein.portal.quickstarts</groupId>
<name>Simplest Hello World Portlet</name>
<version>3.5.0.Final</version>
<packaging>war</packaging>
<description>The very essence of every possible portlet.</description>
<url>http://www.gatein.org</url>
<licenses>
    <license>
        <name>Apache License, Version 2.0</name>
        <distribution>repo</distribution>
        <url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
    </license>
</licenses>

<properties>
    <!-- GateIn Bill of Materials (BOM) version -->
    <org.jboss.bom.gatein-bom.version>1.0.0.Final</org.jboss.bom.gatein-bom.version>
    
    <!-- Plugin versions and settings -->
    <jboss.as.plugin.version>7.1.1.Final</jboss.as.plugin.version>
    <!-- maven-compiler-plugin -->
    <maven.compiler.plugin.version>2.5.1</maven.compiler.plugin.version>
    <maven.compiler.target>1.6</maven.compiler.target>
    <maven.compiler.source>1.6</maven.compiler.source>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencyManagement>
    <dependencies>
        <!--
            Define the version of JBoss Portal we build for. In its dependencyManagement,
            JBoss Portal Bill of Materials (BOM) specifies the versions, types and scopes
            of dependencies which are granted to be compatible with (or indeed in many cases
            provided by) JBoss Portal.
        -->
        <dependency>
            <groupId>org.jboss.bom</groupId>
            <artifactId>gatein-3.5-bom</artifactId>
            <version>${org.jboss.bom.gatein-bom.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <!-- 
        The versions, scopes and types of these dependencies are managed in gatein-*-bom.
        You need to name only groupId and artifactId here.
        Name only those artifacts you refer to in your code.
        Look at gatein-*-bom POM file for the complete list of available artifacts.
    -->
    <dependency>
        <groupId>javax.portlet</groupId>
        <artifactId>portlet-api</artifactId>
    </dependency>
</dependencies>

<build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
        <plugin>
            <groupId>org.jboss.as.plugins</groupId>
            <artifactId>jboss-as-maven-plugin</artifactId>
            <version>${jboss.as.plugin.version}</version>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${maven.compiler.plugin.version}</version>
            <configuration>
                <source>${maven.compiler.source}</source>
                <target>${maven.compiler.target}</target>
            </configuration>
        </plugin>
    </plugins>
</build>

</project>
Further steps, after you have set up the pom.xml file for your project, depend on the technology you have chosen for writing portlets.

22.2. Building and Deploying Portlets

  1. Ensure that the instance of your JBoss Portal is running.
  2. Open a command line and navigate to the root directory of your portlet project.
  3. Type this command to build and deploy the archive: mvn clean package jboss-as:deploy
To deploy to other than default localhost:9999 JBoss instance, copy the following configuration just after <version>${jboss.as.plugin.version}</version> in the pom.xml file and adjust it to suit your needs. Note that <username> and <password> elements can be omitted sometimes, depending on your JBoss security settings.
<configuration>
    <hostname>127.0.0.1</hostname>
    <port>9999</port>
    <username>admin</username>
    <password>secret</password>
</configuration>
This will deploy target/simplest-hello-world-portlet.war to the running instance of the portal.

22.3. Standard Portlet Development (JSR-286)

Note

For an introduction to Portlet development, see Section 21.1, “JSR-168 and JSR-286 overview”
JSR-286 information is available from the Java Community Process Program located at: http://jcp.org/en/jsr/detail?id=286.

22.3.1. Java Configuration

After configuring the Maven pom.xml file, continue implementing a basic JSR-286 compatible portlet. An example of such a portlet is contained in the portal Quickstart "Simplest Hello World Portlet"

Example 22.2. SimplestHelloWorldPortlet.java


package org.jboss.portal.portlet.samples;

import java.io.IOException;
import java.io.PrintWriter;

import javax.portlet.GenericPortlet;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

/**
* The simplest posible Portlet.
* 
* @author Peter Palaga
*/
public class SimplestHelloWorldPortlet extends GenericPortlet {
/**
 * Serves the VIEW mode. Writes "Hello World !" to the response writer.
 * 
 * @see javax.portlet.GenericPortlet#doView(javax.portlet.RenderRequest, javax.portlet.RenderResponse)
 */
@Override
public void doView(RenderRequest request, RenderResponse response) throws IOException {
    PrintWriter writer = response.getWriter();
    writer.write("Hello World !");
    writer.close();
}
}
Two important things have occurred:
  • javax.portlet.GenericPortlet was extended from the javax.portlet:portlet-api artifact.
  • The doView() method has been overridden.
Edit and help portlet modes are not supported in this portlet. To add them, override the doEdit() and doHelp from javax.portlet.GenericPortlet and configure the portlet.xml file accordingly.

22.3.2. portlet.xml

The portlet.xml file for a plain JSR- 286 portlet is as follows:

Example 22.3. portlet.xml


<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>
    <description>Simplest Hello World Portlet is the very essence of every possible Portlet.</description>
    <portlet-name>SimplestHelloWorldPortlet</portlet-name>
    <display-name>Simplest Hello World Portlet</display-name>
    <portlet-class>org.jboss.portal.portlet.samples.SimplestHelloWorldPortlet</portlet-class>
    <supports>
        <mime-type>text/html</mime-type>
        <portlet-mode>view</portlet-mode>
        <!-- You can uncomment the other modes when your portlet-class supports them
        <portlet-mode>edit</portlet-mode>
        <portlet-mode>help</portlet-mode>
        -->
    </supports>
    <portlet-info>
        <title>Simplest Hello World Portlet</title>
    </portlet-info>
</portlet>
</portlet-app>

22.3.3. web.xml

There is no need to configure filters, servlets or mapping for a plain JSR- 286 portlet to work. However the maven-war-plugin artifact requires the web.xml by default. It can suffice to include a web.xml file which contains only the root element:

Example 22.4. web.xml


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">
    <!-- 
        There is no need to configure any filters, servlets, mapping & co. for this demo to work
        but maven-war-plugin by default requires this file.
    -->
</web-app>

22.3.4. Building and Deploying Portlets

Build and deploy the portlet by following the included procedure.
Import the portlet and add the portlet to a page to test its functionality. See the User Guide for instructions.

Procedure 22.1. Build and Deploy the Portlet

  1. Ensure the portal instance is running.
  2. Open a command line and navigate to the root directory of the portlet project.
  3. To deploy to portal instances not listening on localhost:9999 paste the following configuration immediately after <version>${jboss.as.plugin.version}</version> in the pom.xml file
    
    <configuration>
      <hostname>127.0.0.1</hostname>
      <port>9999</port>
      <username>admin</username>
      <password>secret</password>
    </configuration>
    Adjust the configuration to suit individual requirements. <username> and <password> may be omitted depending on the portal's security settings.
    This will deploy target/simplest-hello-world-portlet.war to the running instance of the portal.
  4. Run mvn clean package jboss-as:deploy

Chapter 23. Basic JSF Portlet Development

JSF stands for JavaServer Pages. The JSF version delivered by the built-in Portlet Bridge is 2.1.

23.1. Example Code

This section cites code from JSF2 Hello World Portlet from the Quickstarts Collection .

23.1.1. pom.xml

There is only one noticeable difference in pom.xml against what we have shown as a general case in the Starting a Portlet Project section: we only need to add JSF-specific dependencies:

Example 23.1. pom.xml


<dependencies>
    <!-- 
   The versions, scopes and types of these dependencies are managed in gatein-*-bom.
   You need to name only groupId and artifactId here.
   Name only those artifacts you refer to in your code.
   Look at gatein-*-bom POM file for the complete list of available artifacts.
    -->
    <dependency>
   <groupId>org.jboss.spec.javax.faces</groupId>
   <artifactId>jboss-jsf-api_2.1_spec</artifactId>
    </dependency>
    <dependency>
   <groupId>org.jboss.portletbridge</groupId>
   <artifactId>portletbridge-api</artifactId>
    </dependency>
</dependencies>

23.1.2. JSF Template Files

Example 23.2. Beginning of a JSF portlet template

This example shows the start of a typical JSF portlet template declaration. It is taken from main.xhtml file located in the src/main/webapp/pages directory.

<f:view xmlns="http://www.w3.org/1999/xhtml"  xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html">
	<!-- the f:view above prevents an erroneous <html> within <body> coming from portal. -->
	<!-- <h:head /> and <h:body> in the following are always needed otherwise JSF Facelets will not work -->
	<h:head />
	<h:body styleClass="jsf2HelloWorldPortlet">
	    <h:outputStylesheet library="css" name="jsf2-hello-world-portlet.css" />
	    <h2>JSF2 Hello World Portlet</h2>
Note that <f:view> as a root element of the portlet template above prevents an erroneous <html> within <body> coming from portal. Moreover, the <h:head> and <h:body> elements are always needed for JSF Facelets to work.

Example 23.3. A form in a JSF portlet template

This example demonstrates how to use several JSF elements, such as <h:outputLabel> , <h:inputText> and <h:commandButton> within <h:form>.

<h:form id="jsf2HelloWorldPortlet">
   <h:outputLabel value="#{msgs.Name}" for="nameInput"/>
   <h:inputText id="nameInput" value="#{helloBean.name}">
       <f:ajax render="output" event="keyup"/>
   </h:inputText>
   <br />
   
   <p>
       <h:panelGroup id="output">
           <strong>
               <h:outputFormat value="#{msgs.Hello_0}" rendered="#{not empty helloBean.name}">
                   <f:param value="#{helloBean.name}" />
              </h:outputFormat>
           </strong>
       </h:panelGroup>
   </p>
   
   <h:commandButton id="reset" value="#{msgs.Reset}" actionListener="#{helloBean.reset}">
       <f:ajax render="@form" />
   </h:commandButton> - #{msgs.ResetComment}
   <br />
   <h:commandButton id="reload" value="#{msgs.Reload}" /> - #{msgs.ReloadComment}
   <br />
   
   <h:messages />
</h:form>
The complete source code of the above template is located in src/main/webapp/pages/main.xhtml of JSF2 Hello World Portlet project.

23.1.3. Java Beans

In the JSF template file shown above, we reference the helloBean bean. This bean is implemented in Java as follows:

Example 23.4. helloBean Implementation


/**
* {@link HelloBean} is the JSF backing bean for the application, holding the input data to be re-displayed.
*/
@ManagedBean(name = "helloBean")
@SessionScoped
public class HelloBean implements Serializable {

   private static final long serialVersionUID = -6239437588285327644L;

   /**
    * Stores the name which will be used to greet the application user.
    */
   private String name;

   /**
    * Initializes {@link #name} with the value {@code "World"}.
    */
   @PostConstruct
   public void postContruct() {
       this.name = "World";
   }

   /**
    * Returns {@link #name}.
    * 
    * @return {@link #name}
    */
   public String getName() {
       return name;
   }

   /**
    * Set {@link #name}.
    * 
    * @param name
    */
   public void setName(String name) {
       this.name = name;
   }

   /**
    * Resets {@link #name} to the default value {@code "World"}.
    * 
    * @param ae ignored
    */
   public void reset(ActionEvent ae) {
       this.name = "World";
   }

}
The @ManagedBean and @SessionScoped annotations allow for omitting of equivalent declarations in the faces-config.xml file.

23.1.4. portlet.xml

portlet.xml is the place where we tie the JSF templates with the portlet.

Example 23.5. portlet.xml


<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>
        <description>A simple JSF2 portlet.</description>
        <portlet-name>jsf2HelloWorldPortlet</portlet-name>
        <display-name>JSF2 Hello World Portlet</display-name>
        <portlet-class>javax.portlet.faces.GenericFacesPortlet</portlet-class>
        <init-param>
            <name>javax.portlet.faces.defaultViewId.view</name>
            <value>/pages/main.xhtml</value>
        </init-param>
        <init-param>
            <name>javax.portlet.faces.defaultViewId.edit</name>
            <value>/pages/edit.xhtml</value>
        </init-param>
        <init-param>
            <name>javax.portlet.faces.defaultViewId.help</name>
            <value>/pages/help.xhtml</value>
        </init-param>
        <init-param>
            <name>javax.portlet.faces.preserveActionParams</name>
            <value>true</value>
        </init-param>
        <expiration-cache>0</expiration-cache>
        <supports>
            <mime-type>text/html</mime-type>
            <portlet-mode>VIEW</portlet-mode>
            <portlet-mode>EDIT</portlet-mode>
            <portlet-mode>HELP</portlet-mode>
        </supports>
        <portlet-info>
            <title>JSF2 Hello World Portlet</title>
        </portlet-info>
        <container-runtime-option>
            <name>org.gatein.pc.remotable</name>
            <value>true</value>
        </container-runtime-option>
    </portlet>
</portlet-app>


Note that the javax.portlet.faces.defaultViewId.*init-params are used to bind the JSF templates with the respective portlet view modes. javax.portlet.faces.GenericFacesPortlet as a <portlet-class> will serve the purpose for most JSF portlets.

23.1.5. web.xml

JSF portlets require a few tweaks in the web.xml file:

Example 23.6. web.xml


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
    <display-name>jsf2-hello-world-portlet</display-name>
    <context-param>
        <description>See https://docs.jboss.org/author/display/PBR/Render+Policy</description>
        <param-name>javax.portlet.faces.RENDER_POLICY</param-name>
        <param-value>ALWAYS_DELEGATE</param-value>
    </context-param>
    
    <!-- The following params are documented here: http://myfaces.apache.org/core21/myfaces-impl/webconfig.html -->
    <context-param>
        <param-name>javax.faces.FACELETS_VIEW_MAPPINGS</param-name>
        <param-value>*.xhtml</param-value>
    </context-param>
    <context-param>
        <param-name>facelets.DEVELOPMENT</param-name>
        <param-value>false</param-value>
    </context-param>
    <context-param>
        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
        <param-value>.xhtml</param-value>
    </context-param>
    
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.faces</url-pattern>
    </servlet-mapping>
</web-app>

23.1.6. Custom CSS

Portlet Bridge supports loading of CSS resources in the "JSF way". Just use the <h:outputStylesheet library="css" name="jsf2-hello-world-portlet.css" /> as we do in the main.xhtml file above. The jsf2-hello-world-portlet.css file needs to be placed in resources/css folder of the web application.
Note that relative paths like url('css/background/jsf-logo.png') do not work when used in CSS loaded via <h:outputStylesheet ...> . Rather a JSF Expression Language expression #{resource['/css/background/jsf-logo.png'] } needs to be used. This expression is dynamically evaluated to a proper public URL. The path '/css/background/jsf-logo.png' used in the expression is relative to resources folder of the web application. See also Resource Serving .

Example 23.7. jsf2-hello-world-portlet.css

17. div.jsf2HelloWorldPortlet {
18.     padding: 10px;
19.     /* In the following we use a JSF Expression Language expression rather then plain relative path. 
20.        Plain relative paths do not work in JSF portlets. The expression #{resource['...']} is 
21.        dynamically evaluated to a proper public URL. The path '/css/background/jsf-logo.png' 
22.        used in the expression is relative to  resources folder of this web application.
23.        See https://docs.jboss.org/author/display/PBR/Resource+Serving */
24.     background: url(#{resource['/css/background/jsf-logo.png']}) no-repeat;
25.     background-position-x: 753px;
26.     background-position-y: 10px;
27. }
28. div.jsf2HelloWorldPortlet p {
29.     width: 713px; 
30. }

23.1.7. Internationalization

Internationalization is supported via standard Java Resource bundles. In our example project the *.property files of org.jboss.as.quickstarts.jsf.messages resource bundle are stored under src/main/resources/org/jboss/as/quickstarts/jsf . The following settings in faces-config.xml are needed so that this bundle can be used in JSF templates.

Example 23.8. faces-config.xml

<faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd" version="2.0">
    <application>
        <!-- Declare the internationalization resources -->
        <resource-bundle>
            <!-- The resource bundle property files are in src/main/resources/org/jboss/as/quickstarts/jsf -->
            <base-name>org.jboss.as.quickstarts.jsf.messages</base-name>
            <var>msgs</var>
        </resource-bundle>
        <locale-config>
            <default-locale>en</default-locale>
            <supported-locale>de</supported-locale>
        </locale-config>
    </application>
</faces-config>
Note that in the above faces-config.xml, we have made our bundle visible in JSF templates under the variable msgs . Its individual entries can then be accessed using the usual Expression Language dot notation: e.g. #{msgs.Name }.

23.2. Further Steps

After having done all the above, it is time to build and deploy the portlet , import it and add it to a page so that you can test its functionality.

Chapter 24. JSF Portlet Development with RichFaces

As we have already noted, RichFaces (RF) is just a component library for JavaServer Faces (JSF). Therefore, everything said in Section 23.1, “Example Code” applies here too.

24.1. Example Code

This section cites code from JSF2+RF4 Hello World Portlet from the Quickstarts Collection .

24.1.1. pom.xml

We need to add several RF-specific dependencies to the general JSF ones:

Example 24.1. pom.xml

<dependencies>
  <!-- 
   The versions, scopes and types of these dependencies are managed in gatein-*-bom.
   You need to name only groupId and artifactId here.
   Name only those artifacts you refer to in your code.
   Look at gatein-*-bom POM file for the complete list of available artifacts.
  -->
  <!-- General JSF dependencies -->
  <dependency>
    <groupId>org.jboss.spec.javax.faces</groupId>
    <artifactId>jboss-jsf-api_2.1_spec</artifactId>
  </dependency>
  <dependency>
    <groupId>org.jboss.portletbridge</groupId>
    <artifactId>portletbridge-api</artifactId>
  </dependency>
 
  <!-- RF-sprecific dependencies -->
  <dependency>
    <groupId>org.jboss.portletbridge</groupId>
    <artifactId>portletbridge-extension-richfaces</artifactId>
  </dependency>
  <dependency>
    <groupId>org.richfaces.ui</groupId>
    <artifactId>richfaces-components-api</artifactId>
  </dependency>
  <dependency>
    <groupId>org.richfaces.ui</groupId>
  <artifactId>richfaces-components-ui</artifactId>
  </dependency>
  <dependency>
    <groupId>org.richfaces.core</groupId>
    <artifactId>richfaces-core-impl</artifactId>
  </dependency>
</dependencies>

24.1.2. JSF Template Files

We use <rich:*> components in the templates:

Example 24.2. Form with rich: components in main.xhtml

</p>
<h:form id="jsf2HelloWorldPortlet">
    <h:panelGrid columns="2">
        <h:outputLabel value="#{msgs.Greeting}" for="greeting"/>
        <rich:select id="greeting" value="#{helloBean.greeting}">
   <f:selectItems value="#{helloBean.greetings}" />
   <f:ajax render="output" event="selectitem"/>
        </rich:select>
        
        <h:outputLabel value="#{msgs.Name}" for="nameInput"/>
        <h:inputText id="nameInput" value="#{helloBean.name}">
   <f:validateLength minimum="1" maximum="50" />
   <f:ajax render="output" event="keyup"/>
        </h:inputText>
    </h:panelGrid>
    <p>
        <h:panelGroup id="output">
   <strong><h:outputText value="#{helloBean.greeting} #{helloBean.name}!" rendered="#{not empty helloBean.name}"/></strong>
        </h:panelGroup>
    </p>
    <p>
        <h:commandButton id="reset" value="#{msgs.Reset}" actionListener="#{helloBean.reset}">
   <f:ajax render="@form" />
        </h:commandButton> - #{msgs.ResetComment}
    </p>
    <p>
        <h:commandButton id="reload" value="#{msgs.Reload}" /> - #{msgs.ReloadComment}
    </p>

The complete source code of the above template can be found in src/main/webapp/pages/main.xhtml of JSF2+RF4 Hello World Portlet quickstart.

24.1.3. Java Beans

The HelloBean presented in the Section 23.1, “Example Code” chapter was extended firstly to provide a list of greeting phrases selectable in the drop-down box on the main.xhtml page and secondly to be able to store the greeting phrase selected in the drop-down box.

Example 24.3. HelloBean.java


/**
* Static list of greetings. Contains {@code "Hello"} and {@code "Hi"}.
*/
private static final List&lt;SelectItem&gt; GREETINGS;
 
static {
    List&lt;SelectItem&gt; l = new ArrayList&lt;SelectItem&gt;(2);
    l.add(new SelectItem("Hello"));
    l.add(new SelectItem("Hi"));
    GREETINGS = Collections.unmodifiableList(l);
}
 
/**
 * Stores the greeting phrase which will be used to greet the application user.
 */
private String greeting;

Example 24.4. HelloBean.java

/**
 * Returns {@link #greeting}.
 * 
 * @return {@link #greeting}
 */
public String getGreeting() {
    return greeting;
}
 
/**
 * Set {@link #greeting}.
 * 
 * @param greeting
 */
public void setGreeting(String greeting) {
    this.greeting = greeting;
}
 
/**
 * Returns {@link #GREETINGS}.
 * 
 * @return {@link #GREETINGS}
 */
public List&lt;SelectItem&gt; getGreetings() {
    return GREETINGS;
}
 
 
/**
 * Resets {@link #name} to the default value {@code "World"} and {@link #greeting} with the default value {@code "Hello"}.
 * 
 * @param ae ignored
 */
public void reset(ActionEvent ae) {
    this.name = "World";
    this.greeting = "Hello";
}
 
 }

24.1.4. portlet.xml

There is no substantial change in portlet.xml against Section 23.1, “Example Code”. Only <description> , <portlet-name> , <display-name> and <title> have been changed.

Example 24.5. portlet.xml

<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>
        <description>A simple portlet usinf JSF2 and RF4.</description>
        <portlet-name>jsf2Rf4HelloWorldPortlet</portlet-name>
        <display-name>JSF2+RF4 Hello World Portlet</display-name>
        <portlet-class>javax.portlet.faces.GenericFacesPortlet</portlet-class>
        <init-param>
            <name>javax.portlet.faces.defaultViewId.view</name>
            <value>/pages/main.xhtml</value>
        </init-param>
        <init-param>
            <name>javax.portlet.faces.defaultViewId.edit</name>
            <value>/pages/edit.xhtml</value>
        </init-param>
        <init-param>
            <name>javax.portlet.faces.defaultViewId.help</name>
            <value>/pages/help.xhtml</value>
        </init-param>
        <init-param>
            <name>javax.portlet.faces.preserveActionParams</name>
            <value>true</value>
        </init-param>
        <expiration-cache>0</expiration-cache>
        <supports>
            <mime-type>text/html</mime-type>
            <portlet-mode>VIEW</portlet-mode>
            <portlet-mode>EDIT</portlet-mode>
            <portlet-mode>HELP</portlet-mode>
        </supports>
        <portlet-info>
            <title>JSF2+RF4 Hello World Portlet</title>
        </portlet-info>
        <container-runtime-option>
            <name>org.gatein.pc.remotable</name>
            <value>true</value>
        </container-runtime-option>
    </portlet>
</portlet-app>

24.1.5. web.xml

We set a few more init-params in web.xml for RichFaces components to work:

Example 24.6. web.xml


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
    <display-name>jsf2-rf4-hello-world-portlet</display-name>
    <context-param>
        <description>See https://docs.jboss.org/author/display/PBR/Installing+Portlet+Bridge#InstallingPortletBridge-DisableautomaticinclusionofPortletBridge</description>
        <param-name>org.gatein.portletbridge.WAR_BUNDLES_PORTLETBRIDGE</param-name>
        <param-value>true</param-value>
    </context-param>
    <context-param>
        <description>See https://docs.jboss.org/author/display/PBR/Render+Policy</description>
        <param-name>javax.portlet.faces.RENDER_POLICY</param-name>
        <param-value>ALWAYS_DELEGATE</param-value>
    </context-param>
    
    <!-- The following params are documented here: http://myfaces.apache.org/core21/myfaces-impl/webconfig.html -->
    <context-param>
        <param-name>javax.faces.FACELETS_VIEW_MAPPINGS</param-name>
        <param-value>*.xhtml</param-value>
    </context-param>
    <context-param>
        <param-name>facelets.DEVELOPMENT</param-name>
        <param-value>false</param-value>
    </context-param>
    <context-param>
        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
        <param-value>.xhtml</param-value>
    </context-param>
    <context-param>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>server</param-value>
    </context-param>
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <!-- Change to Production to compress js files, etc. -->
        <param-value>Development</param-value>
    </context-param>

    <context-param>
        <description>http://docs.jboss.org/richfaces/latest_4_X/Developer_Guide/en-US/html/chap-Developer_Guide-Skinning_and_theming.html</description>
        <param-name>org.richfaces.skin</param-name>
        <param-value>#{skinBean.skin}</param-value>
    </context-param>
    <context-param>
        <description>See http://docs.jboss.org/richfaces/latest_4_X/Developer_Guide/en-US/html/chap-Developer_Guide-Advanced_features.html</description>
        <param-name>org.richfaces.resourceOptimization.enabled</param-name>
        <param-value>true</param-value>
    </context-param>

    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.faces</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>

    <mime-mapping>
        <extension>xcss</extension>
        <mime-type>text/css</mime-type>
    </mime-mapping>

</web-app>

24.1.6. Custom CSS

Fully analogous with basic JSF portlets. See Chapter 23, Basic JSF Portlet Development for more information about basic JSF Portlet Development.

24.1.7. Internationalization

Fully analogous with basic JSF portlets. See Chapter 23, Basic JSF Portlet Development for more information about JSF portlet development.

24.2. Further Steps

After having done all the above, it is time to build and deploy the portlet , import it and add it to a page so that you can test its functionality.

Chapter 25. Portlets with JSF and CDI

See the CDI Portlet with JSF Example in the Quickstarts Collection. Review the comments in the Quickstart files for necessary details.

Chapter 26. CDI Portlet Development

Red Hat JBoss Portal supports portlet development using Contexts and Dependency Injection (CDI) as specified by JSR 299. You can perform CDI injection directly into GenericPortlet instances and Portlet Filters. There are two new CDI scopes to specifically support the portlet lifecycle.

26.1. GenericPortlet and Portlet Filter Injection

To activate CDI in portlet applications, the WEB-INF/beans.xml file must be present inside the WAR, as defined by the JSR 299 specification. There is no additional configuration required to use CDI within a portlet or portlet filter.
Even if a portlet class is not managed by the CDI container, it can have CDI beans injected into it. These classes include any portlet class extending GenericPortlet or implementing the Portlet interface. Additionally, filters including PortletFilter , RenderFilter , ActionFilter , EventFilter, or ResourceFilter can use CDI injection. As with any kind of injection, use @Inject and specify the type of the bean to inject.
The point at which the injection is performed on the portlet class or filter, some contexts such as session will not be active. This will cause ContextNotActiveException to appear. Such a situation could arise if the injected bean is produced by a bean that is in session scope.

Note

The examples for CDI Generic Portlet are available in the Quickstarts Collection. See Section C.1, “Quickstarts”. The quickstarts show a basic portlet, and portlet filter using CDI.
The CDI Portlet with JSF quickstart shows how to use CDI in combination with JSF.

26.2. Portlet CDI Scopes

Note

The example titled Portlet Using CDI Scopes project is available in the Quickstarts Collection, see Section C.1, “Quickstarts”. The quickstart demonstrates the usage of CDI Scopes @PortletLifecycleScoped and @PortletRedisplayScoped .
To support the complex Portlet Lifecycle within CDI, @PortletLifecycleScoped and @PortletRedisplayScoped are available for use with either JSF portlets or portlets extending GenericPortlet . These scopes are present within the portal artifact, and are available to your portlet project by adding the Maven dependency described in the example API Maven Dependency.

Example 26.1. API Maven Dependency

There is no <version> necessary in <dependency> if the Bill of Materials (BOM) is imported as described in Section 22.1, “Starting a Portlet Project”.
<dependency>
    <groupId>org.gatein.api</groupId>
    <artifactId>gatein-api</artifactId>
    <scope>provided</scope>
</dependency>

26.2.1. @PortletLifecycleScoped

A bean annotated with @PortletLifecycleScoped is active for the full Portlet Lifecycle, from Action to Render.
If you set a value from within an ActionRequest or EventRequest, then retrieve that value from the bean within a RenderRequest, the value that was previously set is returned.
However, any further RenderRequest events that occur after the initial Portlet Lifecycle has completed will not contain any values that may have been set as part of an ActionRequest or EventRequest.
The @PortletLifecycleScoped is best suited to situations where it does not matter if the value is only present for the first render, such as wanting to display a message on the portlet to the user as a result of some action that was performed.

Note

A ResourceRequest is considered separate from the regular Portlet Lifecycle, therefore any changes to a CDI bean within a ResourceRequest will not be reflected in the instance of the bean used during an ActionRequest , EventRequest or RenderRequest, just as any changes to bean state within these requests will not be reflected in the instance that a ResourceRequest sees.

26.2.2. @PortletRedisplayScoped

A bean annotated with @PortletRedisplayScoped is active until a subsequent Portlet Lifecycle triggered by an ActionRequest or EventRequest is initiated.
This takes @PortletLifecycleScoped a step further by supporting the use case of needing to display the same data each time the portlet is refreshed without any new actions or events being triggered.
It does not matter how many RenderRequest are made, the data will be retained for redisplay until a new ActionRequest or EventRequest is triggered, causing the beans to be re-initialized to their default state.

Important

Because the @PortletRedisplayScoped bean is passivation capable, it must implement Serializable .
A ResourceRequest has any @PortletRedisplayScoped annotated bean initialized to its default state, however any bean that has its methods called will overwrite any existing instances of that bean upon completion of the ResourceRequest . This enables a subsequent RenderRequest to reflect the values that were set as part of a ResourceRequest. The typical use case for this scenario is Ajax calls from the browser.

Important

As @PortletRedisplayScoped beans have a CDI client proxy injected into the beans instead of the actual instance, it's important to remember that calling a get method on a bean during a ResourceRequest will result in its initialized state overwriting whatever is present in the instance that will be used for a RenderRequest .
Therefore it is recommended to only call beans methods during RenderRequest to avoid unexpected data changes on a portlet refresh.

Chapter 27. Portlet Filter

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.

Example 27.1. Contents of portlet.xml

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

27.1. Global Metadata Elements

27.1.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 Filter
  2. Portlet Mode
  3. Window State

27.1.2. Configuring a Portlet Filter

Portlet filter mappings are declared in the global portlet.xml file and applied across portlet applications.

Example 27.2. Configuring a filter

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:
  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 28. Portlet Bridge

28.1. JBoss Portlet Bridge
28.2. Portlet application
28.3. Extensions
28.4. Examples
28.5. Render Policy Parameters
28.6. Facelets Configuration
28.7. JSP-only Configuration
28.8. RichFaces Local and Remote Portlet Support
28.9. Sending and Receiving Events
28.10. Sending Events
28.11. Receiving Events
28.12. Public Render Parameters
28.13. Saving Bridge Request Scope after Render complete
28.14. PRP portlet configuration
28.15. Application configuration
28.16. Portlet Session
28.17. Resource serving
28.18. Serving JSF Resources in a Portlet
28.19. Expression Language Reference
28.20. Expression Language Configuration
28.21. Developing Portlets with the Bridge
28.21.1. Implementing Portlet Bridge
28.21.2. Declaring Artifact Dependencies
28.21.3. Declaring Depchain Dependencies
28.21.4. Deploying Portlet Bridge Portlets
28.21.5. Disable Automatic Portlet Bridge Injection
28.21.6. Supported Portlet Tags
28.21.7. Excluding Attributes from the Bridge Request Scope
28.21.8. Prevent Resources Being Added to Portal Page Head
28.21.9. JSF Facelet View
28.21.10. Error Handling
28.21.11. Switching Portlet Modes
28.21.12. Navigating to a mode's last viewId
28.21.13. Using Wildcards to Identify the Rule Target
28.21.14. Clearing the View History when Changing Portlet Modes
28.21.15. Communication Between Portlets
28.21.16. Storing Components in PortletSession.APPLICATION_SCOPE
28.21.17. Using the PortletSession
28.21.18. Linking to a Facelets page within the Same Portlet
28.21.19. Redirecting to an External Page or Resource
28.21.20. Using Provided EL Variables
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.

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

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

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

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

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

28.6. Facelets Configuration

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

Example 28.1. Facelets web.xml Configuration

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

28.7. JSP-only Configuration

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

Example 28.2. web.xml

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

28.8. RichFaces Local and Remote Portlet Support

Table 28.1. RichFaces Feature Support Exceptions

RichFaces Component Local Portlet Remote Portlet using WSRP Additional Comments
rich:editor Yes Yes Some issues with editor icons over WSRP may be encountered when the Consumer and Producer are on different servers because the URLs are relative and are generated through JavaScript
rich:focus Yes Untested Available since RichFaces 4.3.0.M3
rich:placeholder Yes Untested Available since RichFaces 4.3.0.M3
a4j:push Yes No Not supported for WSRP in Red Hat JBoss Portal Platform 6.2

Table 28.2. RichFaces Feature Status

RichFaces Component Supported as Local Portlet Supported as Remote Portlet using WSRP
a4j:actionListener Yes Yes
a4j:ajax Yes Yes
a4j:attachQueue Yes Yes
a4j:commandButton Yes Yes
a4j:commandLink Yes Yes
a4j:jsFunction Yes Yes
a4j:log Yes Yes
a4j:mediaOutput Yes Yes
a4j:outputPanel Yes Yes
a4j:param Yes Yes
a4j:poll Yes Yes
a4j:queue Yes Yes
a4j:region Yes Yes
a4j:repeat Yes Yes
a4j:status Yes Yes
rich:accordion Yes Yes
rich:accordionItem Yes Yes
rich:autocomplete Yes Yes
rich:calendar Yes Yes
rich:collapsiblePanel Yes Yes
rich:collapsibleSubTable Yes Yes
rich:collapsibleSubTableToggler Yes Yes
rich:column Yes Yes
rich:columnGroup Yes Yes
rich:componentControl Yes Yes
rich:contextMenu Yes Yes
rich:dataGrid Yes Yes
rich:dataTable Yes Yes
rich:dragIndicator Yes Yes
rich:dragSource Yes Yes
rich:dropDownMenu Yes Yes
rich:dataScroller Yes Yes
rich:dropTarget Yes Yes
rich:editor Yes Yes
rich:extendedDataTable Yes Yes
rich:fileUpload Yes Yes
rich:focus Yes Untested
rich:graphValidator Yes Yes
rich:hashParam Yes Yes
rich:hotKey Yes Yes
rich:inplaceInput Yes Yes
rich:inplaceSelect Yes Yes
rich:inputNumberSlider Yes Yes
rich:inputNumberSpinner Yes Yes
rich:jQuery Yes Yes
rich:list Yes Yes
rich:menuGroup Yes Yes
rich:menuItem Yes Yes
rich:menuSeparator Yes Yes
rich:message Yes Yes
rich:messages Yes Yes
rich:notify Yes Yes
rich:notifyMessage Yes No
rich:notifyMessages Yes Yes
rich:notifyStack Yes Yes
rich:orderingList Yes Yes
rich:panel Yes Yes
rich:panelMenu Yes Yes
rich:panelMenuGroup Yes Yes
rich:panelMenuItem Yes Yes
rich:inputNumberSlider Yes Yes
rich:pickList Yes Yes
rich:placeholder Yes Untested
rich:popupPanel Yes Yes
rich:progressBar Yes Yes
rich:select Yes Yes
rich:tab Yes Yes
rich:tabPanel Yes Yes
rich:toggleControl Yes Yes
rich:togglePanel Yes Yes
rich:togglePanelItem Yes Yes
rich:toolbar Yes Yes
rich:toolbarGroup Yes Yes
rich:tooltip Yes Yes
rich:tree Yes Yes
rich:treeModelAdaptor Yes Yes
rich:treeModelRecursiveAdaptor Yes Yes
rich:treeNode Yes Yes
rich:validator Yes Yes

28.9. Sending and Receiving Events

The bridge considers a portlet event a model event, which means the event is targeted to the application's 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.

28.10. 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 28.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 28.3. 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 28.4. 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));
  }

28.11. 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 28.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>
  3. Define the <event-definition> directive in the portlet that will be receiving the event.
    <event-definition>
      <qname xmlns:jbp="urn:jboss:portal:samples:event">jbp:BookingEvent</qname>
      <value-type>org.jboss.example.booking.BookingEvent</value-type>
    </event-definition>
    			
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
  }
}

28.12. 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).

28.13. Saving Bridge Request Scope after Render complete

By default, the Bridge Request Scope is not retained after completion of a Render Request. To save Bridge Request Scope after Render complete will enable developers to focus on Ajax requests and interactions as opposed to full page refreshes.
To return to the behavior of previous Portlet Bridge versions, you need to set the following initialization parameter in portlet.xml for a specific portlet:

  <init-param>
  <name>org.jboss.portletbridge.BRIDGE_SCOPE_PRESERVED_POST_RENDER</name>
  <value>true</value>
</init-param>



You can also set it for all portlets present within an archive by setting the following context parameter in web.xml:

  <context-param>
  <param-name>org.jboss.portletbridge.BRIDGE_SCOPE_PRESERVED_POST_RENDER</param-name>
  <param-value>true</param-value>
</context-param>



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

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

Example 28.5. 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(&quot;hotelName&quot;, &quot;Name of Hotel&quot;);
}

Example 28.6. 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, &quot;bookingPRP&quot;);
 
    if(null != bean) {
      System.out.println(&quot;******processUpdates from BookingPRPHandler: &quot; + bean.getHotelName());
    }
  }
}

28.16. 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']}

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

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

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

}

28.20. Expression Language 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 28.19, “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>

28.21. Developing Portlets with the Bridge

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

28.21.2. Declaring 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 28.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>

28.21.3. Declaring Depchain Dependencies

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

Procedure 28.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>

28.21.4. Deploying Portlet Bridge Portlets

Red Hat JBoss Portal 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 28.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 [VERSION] is the portletbridge-extension-richfaces version in $JPP_HOME/modules/org/jboss/portletbridge/api/main

28.21.5. Disable Automatic Portlet Bridge Injection

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

Procedure 28.6. Disabling automatic Portlet Bridge injection

  • Add the following web.xml dependency to prevent 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 web.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>

28.21.6. Supported 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 must be added to the facelet page of the application.
xmlns:pbr="http://jboss.org/portletbridge"

Example 28.7. 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 28.8. 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>

28.21.7. Excluding Attributes from the Bridge Request Scope

When your application uses request attributes on a per request basis and you do not need 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>

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

28.21.9. 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 markup 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.

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

28.21.11. 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 28.9. /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>

28.21.12. 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 must 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 28.10. /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>

28.21.13. 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 28.21.12, “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 this wild card method to ensure they execute properly in the broadest set of bridge implementations.

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

28.21.15. Communication Between 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 28.12, “Public Render Parameters” and Section 28.9, “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.

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

28.21.17. 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']}

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

28.21.19. 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");
  }
}

28.21.20. Using Provided EL Variables

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

Table 28.3. 

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

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();

Chapter 29. Navigation Portlet Using the Public API

Portal API can be used to implement a Navigation Portlet.

29.1. Example Code

The only difference in the pom.xml standard portlet project code is the gatein-api dependency.

Example 29.1. pom.xml

<dependency>
  <groupId>org.gatein.api</groupId>
  <artifactId>gatein-api</artifactId>
</dependency>
This section cites code from Navigation API Portlet from the Quickstarts Collection ( https://docs.jboss.org/author/pages/viewpage.action?pageId=62587128) .

29.1.1. Required Java Classes

Two classes are required to implement the Navigation Portlet: NavigationPortlet and NavigationNodeBean .
The NavigationPortlet.doView(RenderRequest, RenderResponse) method is responsible for rendering the main persistent menu.

Example 29.2. The doView method

protected void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {

PortalRequest portalRequest = PortalRequest.getInstance();

Navigation navigation = PortalRequest.getInstance().getNavigation();
        
// Diving two levels so the information about children count of children nodes is available
Node rootNode = navigation.getRootNode(Nodes.visitNodes(2));
navigationRootNodeBean = new NavigationNodeBean(rootNode);

List<NavigationNodeBean> rootNodeChildrenList = navigationRootNodeBean.getChildren();
 
/* Setting the 1st node to be active when accesing the root node "/" */
if (!rootNodeChildrenList.isEmpty() && portalRequest.getNodePath().equals(NodePath.root())) {
    navigationRootNodeBean.setFirstActive();
}
 
request.setAttribute("navigationRootNode", navigationRootNodeBean);
 
PortletRequestDispatcher prd = getPortletContext().getRequestDispatcher("/jsp/navigation.jsp");
prd.include(request, response);
     }
The NavigationPortlet.serveResource(ResourceRequest, ResourceResponse) method serves the AJAX requests to render sub-menus. For a given sub-menu URI, it returns a HTML fragment containing the sub-menu.

Example 29.3. The serveResource method

public void serveResource(ResourceRequest request, ResourceResponse response) throws PortletException, IOException {
 
Navigation navigation = PortalRequest.getInstance().getNavigation();

String chosenNodeURI = request.getParameter("uri");
 
Node chosenNode = navigation.getNode(NodePath.fromString(chosenNodeURI), Nodes.visitNodes(2));

request.setAttribute("parentNode", new NavigationNodeBean(chosenNode));
 
PortletRequestDispatcher prd = getPortletContext().getRequestDispatcher("/jsp/node.jsp");
prd.include(request, response);
     }
The NavigationNodeBean serves as a model and contains all the information being rendered on the page. In the NavigationPortlet.doView() method, the navigationRootNodeBean field is representing the root node of the navigation and contains main menu (top-menu) elements (Home and Sitemap by default) as children nodes.

29.1.2. Required JSP

JSP (JavaServer Pages) is used for rendering.
The navigation.jsp file is encapsulates the node.jsp output into an element with unique id identifier.

Example 29.4. Top Portlet JSP Source Code

<div id="id<portlet:namespace/>_gtnQuickstartNavigationPortlet" class="gtnQuickstartNavigationPortlet ">    
    <%-- Render the main menu, if nodes are available --%>
    <c:if test="${fn:length(navigationRootNode.children) > 0}">
        <c:set var="parentNode" value="${navigationRootNode}" scope="request" />
        <c:set var="menuType" value="topmenu" scope="request" />        
        <jsp:include page="node.jsp" />
    </c:if>
</div>
The node.jsp file is responsible for rendering the menus.

Note

The HTML comment tag does not disable the JSP code. It is used to remove the whitespace for more precise CSS styling.

Example 29.5. Menu JSP Source Code

<ol class="${menuType} collapsibleContent"><!--
     <c:forEach var="child" items="${parentNode.children}">
<c:if test="${!child.system}">
     --><li class="menuitem <c:if test='${child.active}'>active</c:if> <c:if test='${(child.parent) &amp;&amp; (child.page)}'> multilevel</c:if>">        
     <portlet:resourceURL var="ajaxResourceUrl">
<portlet:param name="uri" value="${child.path}" />
     </portlet:resourceURL>
     <c:choose>
<%-- Node is a clickable page and contains children nodes --%>
<c:when test="${child.page &amp;&amp; child.parent}">
    <a href="${child.URI}"><span>${child.name}</span></a><!--
    --><a href="#${ajaxResourceUrl}" class="caret menuhandler"><i>${resourceBundle.getString("label.children")}</i></a>
</c:when>
<%-- Node is a clickable page but doesn't contain any children nodes --%>
<c:when test="${child.page &amp;&amp; !child.parent}">
    <a href="${child.URI}"><span>${child.name}</span></a>
</c:when>
<%-- Node is not a clickable page but contains children nodes, it's a "category" node --%>
<c:when test="${!child.page &amp;&amp; child.parent}">
    <a href="#${ajaxResourceUrl}" class="menuhandler"><span>${child.name}</span><i class="caret">${resourceBundle.getString("label.children")}</i></a>
</c:when>
<%-- Do nothing with non-clickable "category" nodes without children nodes --%>
<c:otherwise>
    <span>${child.name}</span>
</c:otherwise>
    </c:choose>
</li>
<!--
</c:if>
</c:forEach>
-->
</ol>

29.1.3. Required JavaScript

The JavaScript part was implemented as a JQuery plug-in. It has several roles:
  • AJAX support
  • Basic on-click drop-down support
  • Dynamically respond to window changes, position menus within the browser window
The asynchronous requests use the JQuery ajax() method:

Example 29.6. The JQuery plugin for drop-down menu


$.ajax({
    type: "POST",
    url: $(this).attr('href').substring(1),
    cache: false,
    dataType: "text",
    success: function(data)
    {
        menuItem.append(data);
        openSubmenu(menuItem);
    },
    error: function(XMLHttpRequest, textStatus, errorThrown)
    {
        console && console.log("Ajax error");
    }
});
For more details about the implementation, see the comments in the dropdown.jquery.js file.

29.1.4. Further Steps

Read and complete the following sections to test the functionality:

Chapter 30. Using Data from Social Networks in Portlets

This chapter describes how to consume data from social networks such as Google+, Facebook and Twitter in portlets. While the kind of data accessible through public APIs varies among the providers, it is generally possible to read data from social networks, and in some cases, write data (in the case of Facebook).

30.1. Social Portlets Example Code

This section cites code from Social Portlets example project from the Quickstarts Collection . There is a portlet that displays the most recent Facebook wall posts, a portlet that displays a list of Google+ circles (and who is in the circles), and a portlet that shows user account details from Twitter.

30.1.1. Prerequisites

Configure the portal with a specific OAuth provider.
Finally, the given portal user must have the OAuth access token saved as part of their User Profile in the portal. To retrieve the access token the user must either:
  • Authenticate or register on the portal with the specific OAuth provider (social network),
  • Link their account with the OAuth Provider - this can be done through the dedicated tab of User Info screen in the portal UI or programmatically from portlets.
Once the user has an OAuth access token saved in their profile, it can be used to call various operations in the particular social network.

30.2. Retrieving the Access Token

The OAuth Provider API is used to retrieve the current user's access token. In the Social Portlets Quickstart, there is an OAuthProviderFilter portlet filter, which retrieves the social network access token of the current user and saves the token into the RequestContext bean. Alternative flows result in one of the following messages:
  • OAuth provider is not enabled in portal configuration.
  • User is not logged in.
  • User's access token does not exist.
  • User's access token is invalid or expired.
These messages are presented by error.jsp or token.jsp . In case the access token is unavailable or invalid, the token.jsp will show a link, which the user can follow to link their GateIn Portal account with the given social network. Once linked, the user will obtain the access token.
Image showing the Facebook User Info Portlet warning, which features a link that allows the user to re-link their Facebook account with the Portal.

Figure 30.1. Token Unavailable Message

Example 30.1. Declaration of OAuthProviderFilter in portlet.xml


<filter>
  <filter-name>FacebookFilter</filter-name>
  <filter-class>org.gatein.security.oauth.portlet.OAuthPortletFilter</filter-class>
  <lifecycle>ACTION_PHASE</lifecycle>
  <lifecycle>RENDER_PHASE</lifecycle>
  <init-param>
    <name>accessTokenValidation</name>
    <value>SKIP</value>
  </init-param>
  <init-param>
    <name>oauthProviderKey</name>
    <value>FACEBOOK</value>
  </init-param>
</filter>

Example 30.2. Mapping of OAuthProviderFilter in portlet.xml

<filter-mapping>
  <filter-name>FacebookFilter</filter-name>
  <portlet-name>Facebook*</portlet-name>
</filter-mapping>
Mapping of OAuthProviderFilter in portlet.xml, specifies that all portlets with the name starting with "Facebook" are handled by this filter. This same mapping configuration can be applied to Google+ and Twitter.
All portlets in the Social Portlets Quickstart extend the AbstractSocialPortlet . It is a subclass of the standard portlet API's GenericPortlet . It contains some basic functionality and helper methods useful for all social portlets, one of which is handling the action to redirect to the Social login.

Example 30.3. actionRedirectToOAuthFlow in AbstractSocialPortlet

@ProcessAction(name = ACTION_OAUTH_REDIRECT)
public void actionRedirectToOAuthFlow(ActionRequest aReq, ActionResponse aResp) throws IOException {
  String customScope = aReq.getParameter(PARAM_CUSTOM_SCOPE);
  getOAuthProvider().startOAuthWorkflow(customScope);
}
If customScope is null, the portal will request scopes only configured in configuration.properties . Otherwise, the portal will request this scope in addition to scopes from configuration.properties . This allows portlets to ask for more scopes than scopes from configuration.properties.

Example 30.4. Widening the Scope On-demand for OAuth Token Permissions

Portlets can request wider scope on demand in cases when wider scope is required to complete a task.
In configuration.properties , Facebook is enabled only with the default scope email.
In FacebookStatusUpdatePortlet , a user wants to publish messages on their Facebook Wall. For this purpose, an access token with scope publish_message is required.
Before the user is able to publish a message on their Facebook wall, the portlet displays an error message that the present scope is insufficient, and offers the user an opportunity to correct the issue.
Screen shot displaying the "insufficient privileges" error a user encounters if they try to publish a status update on their wall without the publish_stream scope.

Figure 30.2. Insufficient Privileges Error

Clicking the link starts an OAuth2 flow. The user is redirected to Facebook and is asked to permit the publish_message scope. After permitting the scope change, the user is able to publish messages.
In this scenario, CDI is used to store the access token. The RequestContext class is annotated as @RequestScoped . Each portlet contains only logic specific to its purpose, and does not need to specifically handle retrieving the access token.

30.3. Facebook

There are three portlets that demonstrate a basic integration with Facebook.

title

FacebookUserInfoPortlet
A simple portlet showing some details of the current user's profile on Facebook, such as first name, last name, username, e-mail and profile picture.
FacebookFriendsPortlet
An advanced portlet showing friends of the current user and their pictures. It also supports pagination (only 10 friends are on screen, but you can switch to different page size to see more friends). When clicking a friend, the portlet displays the last few status messages from the given friend's Facebook Wall.
FacebookStatusUpdatePortlet
A portlet with the capability to publish messages to the Wall of the current user.
All three portlets leverage the third-party library Rest FB( http://restfb.com), which provides the capability to send requests to the Facebook Graph API and convert JSON responses to plain Java objects (POJOs).
To decouple logic from the UI and handle error situations, the helper class FacebookClientWrapper offers multiple methods to retrieve objects from the Facebook Graph API and convert them to POJOs or beans relevant to the portal. These converted methods can be dispatched by PortletRequestDispatcher to JSP pages, which handle the HTML markup rendering.
The example portlets are using a small subset of the Facebook Graph API. It is possible use the whole spectrum of the Facebook Graph API operations in your own portlets. The complete documentation of Graph API is available on the Facebook Developers site, which is located at http://developers.facebook.com/docs/reference/api/ .

30.4. Google+

There are three example portlets for showing integration with Google+. All portlets leverage Google SDK libraries.
GoogleUserInfoPortlet leverages Google OAuth2 API library and it uses an Oauth2 object to call the operation.
GoogleFriendsPortlet and GoogleActivitiesPortlet leverage Google+ API library and they use Plus object to call the operations. Portlets have available access token and they obtain the required object through a call to oauthProvider.getAuthorizedAPIObject as described in Chapter 7, OAuth Provider API.

30.5. Twitter

There is one simple portlet for Twitter: TwitterPortlet . It shows details about the current user, such as their picture, last tweet, number of followers, and other user data commonly available on a twitter user's profile.
TwitterPortlet leverages Twitter4J library to interact with Twitter. Given that the access token is available, the portlet demonstrates how to obtain the Twitter client object from Twitter4J library through calling oauthProvider.getAuthorizedAPIObject() as described in Section 7.1, “Retrieve an Instance of OAuthProvider”.

30.6. Configuration and Deployment Descriptors

pom.xml
In pom.xml file of our Social Portlets Quickstart, we need to declare dependencies on JSR 286 portlet API, GateIn Portal API and CDI API:

Example 30.5. Declaration of basic dependencies in pom.xml


<!--
    The versions of these dependencies are managed in gatein-*-bom.
    Note that artifacts managed in gatein-*-bom are <scope>provided</scope> in most cases.
119.    Name only those artifacts you refer to in your code.
    Look at gatein-*-bom POM file for the complete list of available artifacts.
-->
<dependency>
    <groupId>javax.portlet</groupId>
    <artifactId>portlet-api</artifactId>
</dependency>
<dependency>
    <groupId>org.gatein.api</groupId>
    <artifactId>gatein-api</artifactId>
</dependency>
<dependency>
    <groupId>javax.enterprise</groupId>
    <artifactId>cdi-api</artifactId>
    <scope>provided</scope>
</dependency>
We also need to declare dependencies on 3rd party libraries to be able to access the public APIs of Facebook, Twitter and Google+:

Example 30.6. Declaration of 3rd party dependencies in pom.xml


<!-- Google -->
<dependency>
    <groupId>com.google.apis</groupId>
    <artifactId>google-api-services-plus</artifactId>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.google.apis</groupId>
    <artifactId>google-api-services-oauth2</artifactId>
    <scope>provided</scope>
</dependency>
<!-- Twitter -->
<dependency>
    <groupId>org.twitter4j</groupId>
    <artifactId>twitter4j-core</artifactId>
    <scope>provided</scope>
</dependency>
<!-- Facebook  (not provided for now as it's not needed in Portal and so it's bundled in this portlet WAR) -->
<dependency>
    <groupId>com.restfb</groupId>
    <artifactId>restfb</artifactId>
</dependency>
You may have noticed that com.restfb dependency is not declared as provided . This is because this library is not used by GateIn Portal itself, and therefore it is not bundled therein as well. We need to provide it in our WAR ourselves. Dependencies on Twitter4J and Google API libraries are provided by GateIn Portal and there is no need to package them inside our portlet WAR.
jboss-deployment-structure.xml
This file is specific for JBoss AS7 and JBoss EAP. In this file, dependencies on JBoss AS7/EAP modules are declared which are required by our portlet application.

Example 30.7. Declaration of dependencies in jboss-deployment-structure.xml

<deployment>
  <dependencies>
    <module name="com.google.apis" />
    <module name="org.twitter4j" />
  </dependencies>
</deployment>
Modules org.twitter4j and com.google.apis are part of GateIn Portal JBoss AS7 packaging and they contain the 3rd party libraries from Google+ and Twitter.
beans.xml
The presence of beans.xml file activates CDI for the application. There is nothing useful in it.

Example 30.8. beans.xml

<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://jboss.org/schema/cdi/beans_1_0.xsd">
<!--
portlet.xml
It contains declaration of all social portlets. Very important is also declaration of OAuthPortletFilter and it's filter mapping.
gatein-resources.xml
Declaration of CSS files used by our portlets.

30.7. Further Steps

Import Portlets and Gadgets and Manage Pages in the User Guide for further guidance about deploying and testing the portlets.

Chapter 31. Spring Framework Portlet Development

Support for Spring Portlet Model-View-Controller (MVC) is limited in the Red Hat JBoss Portal. It is recommended that other fully supported options such as JSP, JSF or JSF with RichFaces are considered as replacements to Spring Portlet MVC.

Important

Using other Spring components must be carefully evaluated as their support in the Portal environment may be limited.
See the Portlet MVC Framework section ( http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/portlet.html) of the Spring Framework Reference Documentation for detailed Spring-based portlet project configuration instructions.

Important

When generating an Action URL using the portlet tag library, ensure that escapeXml="false" is included in the definition of portlet:actionURL, as described in Example 31.1, “Action URL”. A page not found issue due to the Action URL not being able to map the URL to a Spring controller is likely if this advice is not followed.

Example 31.1. Action URL

<portlet:actionURL var="myAction" escapeXml="false" />
Deploying a Spring-based portlet, importing it, and adding it to a page uses the same process as any other portlet.
See Import Portlets and Gadgets and Manage Pages in the User Guide for further guidance about using the Web interface to import and manage portlets.

Limitation for Spring portlet on version 2.5

During development or migration of a Spring Portlet on version 2.5 for the platform, the configuration <context:component-scan base-package="org.classname.etc"/> fails to locate the beans for scanning. Spring 2.5 cannot interpret the VFS file system used by Red Hat JBoss Enterprise Application Platform therefore it cannot scan the JAR files to find the beans. Spring 3.x versions do not have this limitation. To resolve the problem, replace the <component-scan> tag with the bean tags for each required bean.
Spring 2.5 only supports JSR 168 (Portlet Spec 1.0), it does not include support for ResourceRequest which was introduced by JSR 286 (Portlet Spec 2.0). Without the ability to use a ResourceRequest, the means by which Ajax calls are handled in a portlet, it is not possible to use Ajax calls with a Spring 2.5 portlet. If your portlet requires Ajax calls, it must be upgraded to Spring 3.x.

Part V. JavaScript Development

Chapter 32. JavaScript Modularity

32.1. JavaScript Modules

The Red Hat JBoss Portal JavaScript handling improvements are built upon the concept of the JavaScript module. JavaScript does not provide native support for namespacing and so the notion of modules was designed to solve this problem. These modules provide namespacing and more; the module pattern allows developers to create dependencies between modules and resolve them at runtime, enabling on demand and parallel loading of JavaScript resources.
For more information see the RequireJS documentation at http://requirejs.org/. An in-depth article about JavaScript module patterns is available at http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth.

32.2. Introduction to Modules

A module can be thought of as:
  • An identifier or name;
  • A list of the module's dependencies;
  • Packaged code, usually expressed as a self-executing function.
  • The product, an object produced by the module that is usually consumed by other modules.
At runtime the dependency system defines a graph of functions to execute, the product of each module being injected in the other modules. It can be seen as a simple dependency injection system able to load modules in an asynchronous and parallel fashion, providing parallel loading, namespacing, and dependency management.
The portal provides support for different module formats:
GateIn Module Definition (GMD)
This format is the most appropriate way to define a JavaScript module in the portal. The module is expressed as a simple function, the list of dependencies being expressed in the gatein-resources.xml file. This format clearly decouples the module's code in a JavaScript file from the module's dependencies, expressed in a single XML file, and is the best compromise for integrating JavaScript in the portal.
Asynchronous Module Definition (AMD)
This is the native module format supported by RequireJS.
Adapted Module
This format is the most flexible as it adapts to other module formats such as CommonJS modules.
To explore the benefits of modules (beyond namespacing) the following example uses a module consuming the jQuery library:
(function($) {
  $("body").on("click", "mybutton", function(event) {
  alert("Button clicked");
  };
})($)
The JavaScript component of our module is a function that takes the jQuery $ object as an argument:
  • The jQuery object is provided as a module dependency expressed as a function argument $. The module code can use any name for jQuery; in our example we use the $ name.
    $ jQuery can be used without needing to use the $.ready function because jQuery will be already loaded and initialized when the function is executed.
  • The module is scoped to the entire page and this code will react to any click on the .mybutton selector. Since a single event listener handles as many portlets as are on the page, this benefits performance and simplicity.
To integrate this module, it is declared in the gatein-resources.xml file of the WAR archive:

<gatein-resources
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_resources_1_3 http://www.gatein.org/xml/ns/gatein_resources_1_3"
xmlns="http://www.gatein.org/xml/ns/gatein_resources_1_3">
  <portlet>
    <name>MyPortlet</name>
    <module>
      <script>
        <path>/mymodule.js</path>
      </script>
      <depends>
        <module>jquery</module>
        <as>$</as>
      </depends>
    </module>
  </portlet>
</<gatein-resources>
The module tag declares the module and its dependencies. In this case it is declared that jQuery is the only dependency and that it is named $ when the module is executed.
A dependency relationship between the module and the jQuery module has been created such that:
  • The module will be loaded only when the portlet is displayed;
  • The portal loads the jQuery module before loading our module;
  • Several different versions of jQuery can coexist in the browser without conflicting.
This introductory example outlines the important features provided by modules: performance, isolation, dependency management and ease of use.

32.3. Script Support

While modules are preferred whenever possible, they cannot be used in every instance. For example, when an existing portlet uses an inline script it will access the global namespace to find functions or objects. To accommodate these instances, the traditional method of using JavaScript with a simple script declaration in the head section of a web page continues to be supported.
Dependencies between scripts can be created to control the order of the scripts in the head section and resolve dependency problems as much as possible.

Chapter 33. JavaScript in JBoss Portal

33.1. Declaring a Module

A JavaScript module is the declaration of a JavaScript self-executing function and its declaration in the gatein-resources.xml descriptor of the web application.
The descriptor defines the module and its dependencies. The portal builds a graph of resources and their dependencies at runtime. When a client requires a particular module, it invokes a JavaScript function that calls RequireJS to resolve dependencies.

Example 33.1. foo.js


(function ($) {
// Do something with jQuery
})(jQuery)
This module is declared as a self-executing function in the foo.js file.
The foo.js file is then declared in the gatein-resources.xml file:

Example 33.2. gatein-resources.xml


...
<module>
  <name>foo</name>
  <script>
    <path>/foo.js</path>
  </script>
  <depends>
    <module>jquery</module>
    <as>jQuery</as>
  </depends>
</module>
...
The self-executing function declares:
  • The parameter for the function: $.
  • The arguments of the function's invocation: jQuery.
The self-executing function argument must match the dependencies.
  • Functions do not need to match the XML dependencies order.
  • Functions can use a parameter subset; a dependency can be declared in the XML but not consumed as an argument.
  • Parameters must be aliased in dependencies with the <as> XML tag.

Note

The self-executing function argument is a JavaScript construct. The purpose of this construct is to pass arguments to the function, let the function name the arguments and override the current scope of execution.
For example, jQuery uses the following:
(function(window, undefined) { .... })(window);
Resources are related by dependency relationships, which specify how scripts are related to each other and how the modular system should be honored.
In our example, the foo module uses jQuery. They are in relationship: foo depends on jQuery. When the foo module is loaded, the jQuery module must be available to it.

33.2. Translation to the AMD System

The portal will translate logical identifiers into an AMD module:

define("SHARED/foo", ["SHARED/jquery"], function(jQuery) {
  return (function($) {
  // Do something with jQuery
  })(jQuery);
});
  • Logical identifiers are translated to AMD identifiers:
    • The foo module is translated to the SHARED/foo AMD module identifier; the SHARED prefix is added by the portal for scoping the name.
      Other existing scopes include PORTLET and PORTAL.
    • The dependency on the jquery module is translated to a ["SHARED/jquery"] AMD module dependency.
  • The module function is wrapped twice:
    • Once by the portal module wrapper which delegates to the module's function. The goal of this function is to provide a ? evaluation of the module's self-executing function.
    • Then by the define AMD function that manages loading the module properly.
  • The self-executing function module's arguments must match the module dependencies expressed in the XML declaration.
    • The jquery dependency is aliased to the jQuery name in the XML as tag. As a consequence the portal function wrapper parameter is called jQuery.
    • The module's self-executing function argument is named jQuery to match the declared alias.
    • The module's self-executing function parameter is named $ and thus redefines the jquery dependency to $ locally.
At runtime, the process happens in the following order of events:
  • The defined function is invoked and declares its dependencies.
  • Once the dependencies are resolved, the module wrapper is invoked with the jQuery argument containing the jquery dependency.
  • The module wrapper evaluates the self-executing function that resolves the jQuery argument to the jquery dependency.
  • The self-executing function is invoked and the jquery dependency is available under the name $.

33.3. Producing Dependencies

The previous example illustrated that a module is able to consume dependencies as arguments. This section explains how a module can produce a dependency.
When a module self-executing function is evaluated it can return an object. You can modify the previous example to return a value.

Example 33.3. foo.js


(function ($) {
  // Do something with jQuery
  return {hello:"world"}
})($)
In this second example, we return a simple JavaScript object. This object will be stored by the dependency system of AMD and provided as arguments of modules that consume the foo module.

Chapter 34. Native AMD Modules

The support for native AMD modules is added to RedHat JBoss Portal Platform. Native AMD modules exhibit three main differences againstJBoss Portal Modules:
  • Simplified declaration in gatein-resources.xml.
  • No server-side wrapping of JavaScript code.
  • Shared scope only.

34.1. Difference in Native AMD and Portal Modules

Despite the great flexibility of Red Hat JBoss Portal Platform Modules, there are situations where they cannot provide an appropriate solution. For example, to use a large 3rd party JavaScript framework such as which complies with the AMD specification and it should be possible to declare Dojo modules as JBoss Portal Modules, but for certain reasons is not possible:
  1. Large Dojo 1.9.2 contains as many as 4899 JavaScript files. You declare them in gatein-resources.xml using a tool. But such a tool will have to provide the dependency tree which is not trivial.
  2. Extreme AMD.although AMD compliant consist of constructs, that JBoss Portal cannot handle such as:
    1. Relative module names. If you have a dependency graph as: dojo/sniff depends on dojo/has. Given that both dojo/sniff and dojo/has live in the same directory named dojo, the dojo/sniff.js file can start with define(["./has"], function(has){, but relative module path names such as "./has" cannot be handled by JBoss Portal modules.
    2. Cyclic dependencies occur in AMD Modules but JBoss Portal capitulates with an error when seeing them.
    3. Inline calls such as, require([id == 'platform' ? platformId : defId], function(provider){...}); are allowed in AMD but cannot be declared in JBoss Portal modules. There are two problems with such calls, you do not know at development time what dependency to declare in gatein-resources.xml and the JBoss Portal module implementation does server-side wrapping of all the modules which causes that these inline require() calls result in a request for a nonexistent resource.
    4. Loader Plugins. Specific loader plug-ins are supported in JBoss Portal. In AMD, the string following the ! separator, which is the resource ID is interpreted by the loader plug-in,so it can be interpreted incorrectly as a URL, name of another module,and so on. But the JBoss Portal module implementation is able to interpret them as resource paths.

34.2. Native AMD Modules hosted on CDN

Red Hat JBoss Portal supports AMD modules hosted outside of the portal, for example modules hosted on a Content Delivery Network (CDN).

Important

The code snippets used in the section are under development at ArcGis Gears Portlet hosted on GitHub: https://github.com/ppalaga/arcgis-gears-portlet/commits/master
This feature is implemented using require.js path mappings listed at, http://requirejs.org/docs/api.html#config-paths The implementation allows to map a module ID prefix to an URL, instead of the prefix.

34.3. Mapping path entries for AMD Modules

The mappings entries are declared in the <amd> section of the gatein-resources.xml file:

Example 34.1. AMD path entries in gatein-resources.xml

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

    <amd>
        <path-entry>
            <prefix>dojo</prefix><target-path>//js.arcgis.com/3.9/js/dojo/dojo</target-path>
        </path-entry>
        <path-entry>
            <prefix>dijit</prefix><target-path>//js.arcgis.com/3.9/js/dojo/dijit</target-path>
        </path-entry>
        <path-entry>
            <prefix>dojox</prefix><target-path>//js.arcgis.com/3.9/js/dojo/dojox</target-path>
        </path-entry>
        <path-entry>
            <prefix>esri</prefix><target-path>//js.arcgis.com/3.9/js/esri</target-path>
        </path-entry>
    </amd>

34.4. Fallback Paths

You can assign multiple target paths to a given prefix. These multiple paths are interpreted as fallback paths.
For example, you can insert a third-party CDN URL as the first choice and add a URL on your own server as a fallback URL.

Example 34.2. Adding Fallback paths to gatein-resources.xml

...
    <amd>
        <path-entry>
            <prefix>dojo</prefix>
            <target-path>//js.arcgis.com/3.9/js/dojo/dojo</target-path>
            <target-path>//mycompany.com/frameworks/dojo/1.9.1</target-path>
        </path-entry>
        ...
    </amd>
...
For more information on fallback URL, see http://requirejs.org/docs/api.html#pathsfallbacks

34.5. Example to Ensure Portal wide Consistency in mapping paths

There is one global path mappings collection for each Portal instance. When a portlet application is deployed on JBoss Portal, the Portal can only accept path mappings which are consistent with the paths declared earlier by other applications.
For example, application app2 wants to register the AMD module ID prefix /dojo to be mapped to target path http://fastcdn.com/dojo/1.7.0
The prefix /dojo was previously registered by another application app1 to point to a different target path http://othercdn.com/dojo/1.9.1 .
Adding the prefix /dojo to the available prefixes could break app1 and no JavaScript or CSS resources from app2 will be accepted by the portal.
But to resolve this scenario, you can add the same mapping multiple times as shown below:
The path mapping from app2.war is accepted by the portal because it does not introduce any inconsistency.

Chapter 35. Module Scopes

The name of a logical portal module translates into an AMD name with a prefix of SHARED, PORTLET or PORTAL. This prefix fully identifies a module by its logical name and its scope. Scopes ensure that a module (and therefore the underlying web resource) is loaded at the right time when it is effectively required.

35.1. Shared Scope

The shared scope does not related to a specific portal entity, instead a shared module should be consumed by other modules. It is declared in gatein-resources.xml with the top level module tag:

<module>
  <name>core</name>
  <script>
    <path>/core.js</path>
  </script>
  <depends>
    <module>base</module>
  </depends>
</module>

35.2. Portal Scope

The module is related to a portal instance and is loaded when the related portal is accessed:

<portal>
  <name>classic</name>
  <module>
    <script>
      <path>/classic.js</path>
    </script>
    <depends>
      <module>core</module>
    </depends>
  </module>
</portal>

35.3. Portlet Scope

The module is loaded when the corresponding portlet is accessed:

<portlet>
  <name>sitemap</name>
  <module>
    <script>
      <path>/sitemap.js</path>
    </script>
    <depends>
      <module>core</module>
    </depends>
  </module>
</portlet>

Note

A module can only depend on a shared module, therefore any depends tags implicitly reference a shared module.

Chapter 36. Portlet Dynamic Module

As explained previously, portlet dependencies (including JavaScript files) can be declared in the gatein-resources.xml file, which forces declaration of portlet dependencies at packaging time when the corresponding war file is created.
The JSR286 specification provides a mechanism for modifying portal headers, which can also be used to load dependency JavaScript files. Although this mechanism is portable, it has the following drawbacks:
  • A script can be loaded multiple times, specially if two portlets in two different war files load the same scripts since the only way to identify a script is by its URL.
  • Scripts have to be loaded by the head section of the portal, impacting front-end performance.
The portal combines both approaches by allowing to create dynamic dependencies of a portlet at runtime, during the render phase of the portlet:

public void render(RenderRequest req, RenderResponse resp) throws PortletException, IOException {
  resp.setProperty("org.gatein.javascript.dependency", "base");
  resp.addProperty("org.gatein.javascript.dependency", "common");
}
The code above is equivalent to the following declaration in the gatein-resources.xml file:

<portlet>
  <name>MyPortlet</name>
  <module>
  <depends>
    <module>base</module>
  </depends>
  <depends>
    <module>common</module>
  </depends>
  </module>
</portlet>

Chapter 37. Aliases

It is possible to define an alias for each module declared in the gatein-resources.xml file. The alias is added to the module declaration using the <as> element:

<module>
  <name>foo</name>
  <as>Foo</as>
  ...
</module>
<module>
  <name>bar</name>
  ...
  <depends>
    <module>foo</module>
  </depends>
</module>
The alias can then be used instead of the module's name in the argument of the respective self-executing JavaScript function:

(function(Foo) {
})(Foo)
Similarly, the <as> element can also be used inside the <depends> element to define a local alias for the dependency:

<module>
  <name>foo</name>
  ...
</module>
<module>
  <name>bar</name>  
  ...
  <depends>
    <module>foo</module>
    <as>Foo</as>
  </depends>
</module>

Chapter 38. Custom Adapters

Until ECMAScript 6, there had been no standardized format of a JavaScript module declaration. However, several proposals of such a format have emerged, all revolving around the same module pattern. The AMD (Asynchronous Module Definition) format was chosen because it is suitable for the Web, and its asynchronous nature.
As a consequence, you sometimes have to deal with included scripts that do not match the self-executing function declaration format. To adapt such scripts to the expected format without editing the actual code, it is possible to declare an adapter that will wrap the original scripts and make them reusable.
The jQuery library is a good example of how a custom adapter is useful. Thanks to the adapter, it is possible to reuse the jQuery library without any modifications, which facilitates the integration and maintenance of the library in the portal. jQuery uses the following construct to define itself:

(function(window, undefined) {
})(window)
The issue with this construct is that it will bind jQuery to the window, and that it will not return any value as expected by the dependency system. Thanks to the custom adapter, we can integrate JQuery the following way:

<module>
  <name>jquery</name>
  <script>
    <adapter>
      (function() {
        <include>/jquery.js</include>
        return jQuery.noConflict(true);
      })();
    </adapter>
  </script>
</module>
The <adapter> element can encapsulate any code, and the <include> element will perform a simple string inclusion of the original jQuery script:

define("SHARED/jquery", [], function() {
  return (function() {
    (function(window, undefined) {
    })(window);
    return jQuery.noConflict(true);
  })();
});

Chapter 39. Module Resources

The AMD format allows to define dependencies on a resource loaded by a module based on the loader plug-in.
A module that uses a loader plug-in uses the exclamation mark character (!) to delineate between the loader plug-in module to use and the target resource to load. Loader plug-ins are useful because they can load resources by means of the AMD loading mechanism and benefit from its performance and parallel loading.
There are some useful loader plug-ins:

Procedure 39.1. Configuring a Module Resource

To demonstrate the configuration of an AMD loader plug-in's target resource, we need some HTML code generated by JavaScript. The text AMD loader plug-in can be used for this purpose. The plug-in will load a template as its resource and pass it as a parameter into a module definition callback.
  1. Declare text as a module. It is written as a native module that depends on the predefined RequireJS module called module. The native support mechanism allows this configuration works transparently without modifying the third-party library.
    
    <module>
      <name>text</name>
      <script>
        <path>/requirejs/js/plugins/text.js</path>
      </script>
      <depends>
        <!-- module means RequireJS itself -->
        <module>module</module>
      </depends>
    </module>
    
  2. Define the foo module that requires the text plug-in to load the template by means of the <resource> XML element.
    
    <module>
      <name>foo</name>
      ...
      <depends>
        <module>text</module>
        <as>myTemplate</as>
        <resource>/path/to/template.tmpl</resource>
      </depends>
    </module>
    
  3. The foo module template is loaded by the text plug-in.
    
    (function(myTemplate) {
    //parse the 'myTemplate' then append to DOM
    })(myTemplate);
    

Chapter 40. Load Groups

When self-executing functions and XML descriptors are loaded by a browser, they are served as web resources by the portal. By default, a module is served as a single resource, which can cause performance issues in production systems. The load group feature solves these issues by decoupling the logical modules and the JavaScript resource serving.
The <load-group> element can be used to group modules into a single web resource. When a module contained in a load group is requested, the web resource containing all the grouped modules is loaded. For instance, the code below shows configuration of the foo and bar modules grouped into the foobar load group:

Example 40.1. load-group Example


<module>
  <name>foo</name>
  <load-group>foobar</load-group>
  ...
</module>
<module>
​</