+ All Categories
Home > Documents > GateIn Cookbook

GateIn Cookbook

Date post: 27-Dec-2016
Category:
Upload: piergiorgio-lucidi
View: 226 times
Download: 8 times
Share this document with a friend
392
Transcript

GateIn Cookbook

Over 60 recipes for building portals with GateIn including user security, gadgets, and applications with frameworks

Ken Finnigan

Luca Stancapiano

Piergiorgio Lucidi

BIRMINGHAM - MUMBAI

GateIn Cookbook

Copyright © 2012 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the authors, nor Packt Publishing, and its dealers and distributors will be held liable for any damages caused or alleged to be caused directly or indirectly by this book.

Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

First published: November 2012

Production Reference: 1311012

Published by Packt Publishing Ltd.Livery Place35 Livery StreetBirmingham B3 2PB, UK.

ISBN 978-1-84951-862-8

www.packtpub.com

Cover Image by Asher Wishkerman ([email protected])

Credits

AuthorsKen Finnigan

Luca Stancapiano

Piergiorgio Lucidi

ReviewersAntoine Herzog

Gurkan Erdogdu

Rafael Liu

Acquisition EditorJoanna Finchen

Lead Technical EditorAzharuddin Sheikh

Technical EditorKirti Pujari

Project CoordinatorJoel Goveya

ProofreaderMartin Diver

IndexerMonica Ajmera

Production Coordinator Arvindkumar Gupta

Cover WorkArvindkumar Gupta

About the Authors

Ken Finnigan is a Senior Software Engineer at Red Hat and Technical Lead of the JBoss Portlet Bridge project and a member of the GateIn Development team. As a Consultant and Engineer he has over 15 years of development experience with enterprises throughout the world using technologies that include Java EE frameworks (JSF, CDI, EJB3, Hibernate, Seam), Java testing frameworks (Arquillian, JUnit, TestNG), Maven, Ant, Arquillian, and a variety of others. In his spare time he is a Committer for Apache DeltaSpike, JBoss Seam 3, ShrinkWrap, and Arquillian.

I would like to thank my wife, Erin, and my family for all their support and understanding through the entire book development process.

Luca Stancapiano is a Consultant Expert in Java EE technologies since 2000. He started contributing to the JBoss Community at an early stage in his career. He contributed initially to Hibernate, JBoss AS, JBoss Portal, and JBoss Cache, and more recently to projects such as Seam, GateIn, ExoJCR, ModeShape, and Infinispan.

In 2005 he became a JBoss Advanced Consultant, and in 2006 he became the Project Leader of JBoss Forums.

In the Apache Community, he has contributed to Lucene and ManifoldCF, improving his knowledge on the search engines as a result.

He has also contributed for the OSGi Alliance, making products compliant with OSGi. He collaborates with Sourcesense as an open source ECM consultant and trainer.

I would like to thank the GateIn community team for their participation and Packt's team for the opportunity to write this book, and Monica for moral support.

Piergiorgio Lucidi is an open source ECM Specialist at Sourcesense. Sourcesense is a European open source systems integrator providing consultancy, support, and services around key open source technologies.

He works as Software Engineer, and he has 8 years of experience in the areas of Enterprise Content Management (ECM), system integrations, web, and mobile applications. He is an expert in integrating ECM solutions in web and portal applications.

He contributes as PMC member, Project Leader, and Committer at the Apache Software Foundation for the project Apache ManifoldCF; he also contributes on ECM connectors such as CMIS, Alfresco, and ElasticSearch. He is a Project Leader and Committer of the JBoss Community, and he contributes to some of the projects of the JBoss Portal platform.

He is a Speaker at conferences dedicated to ECM, Java, Spring Framework, and open source products and technologies.

He is an Author, Technical Reviewer, and Affiliate Partner at Packt Publishing, for whom he wrote the technical book Alfresco 3 Web Services. As Technical Reviewer, he contributed to both Alfresco 3 Cookbook and Alfresco Share. As Affiliate Partner, he writes and publishes book reviews on his website Open4Dev (http://www.open4dev.com/).

I would like to thank Packt Publishing for giving me this second opportunity to write a book about a very interesting open source project. I would also like to thank my company Sourcesense for giving me some time to spend on this project. Finally, I would like to thank my girlfriend Barbara, who encouraged me during the making of this book.

About the Reviewers

Antoine Herzog started with computers in 1981, with the ZX81, writing machine code for the CPU, and has never stopped programming since then. He started programming in Java in 2003 and he started building portals with JBoss Portal in 2005 (version 2.2), and followed on with GateIn. He contributes to the project, with debugging, jira, wiki, and forum posts. He is very pleased to contribute and help the community to develop and use this nice technology and its associated tools.

Since 2006, he works at Sysemo Sarl (www.sysemo.com), his own company, as an Expert in J2EE, JBoss AS, GateIn, JSF, RichFaces, EJB, and Hibernate.

He has helped many companies to build their portals, both at management level and the programming level.

He also founded www.presta-expert.com. He programs and runs this portal website, on a JBoss AS7 platform, with GateIn 3.4.0.

Antoine graduated from France's Ecole Nationale Superieure de Techniques Avancées in 1991 (ENSTA, Concours des Mines), where he enjoyed pursuing knowledge of sciences and engineering alongside his self-taught skills.

Gurkan Erdogdu is CTO and Co-Founder of MechSoft. He has been involved with Java and Java EE technologies since 1999. Gurkan is also very active in the open source world and is a member of several open source foundations. Gurkan is a member of Apache Software Foundation and is a founder of the project Apache OpenWebBeans. Gurkan also gives training and special consultancies on Java and Java EE technologies. Gurkan holds a BS in Computer Engineering from Middle East Technical University (METU). Gurkan lives in Ankara with his wife and little daughter. He can be reached at [email protected].

I owe thanks to my parents who provided encouragement, friendship, wisdom, and patience in my life. Without them, it would not have been possible to become the person I am now.

Rafael Liu has been working with Java for 7 years as a Developer, Architect, and Consultant. Focusing mainly on JEE and middleware solutions from mainstream vendors, he has worked in many mission-critical systems for the Government of Brazil, defining infrastructure architecture and doing pre-production support such as load tests, performance analysis, bottleneck diagnosis, and tuning of both applications and JVMs. He is currently Technical Account Manager at Red Hat, where he deals with Application Servers, SOA-related middleware solutions, portal, development frameworks, and basically most of the JBoss stack. Rafael is an open source enthusiast and a GNU/Linux fan. Speaking and writing about Java is one of his passions.

www.PacktPub.com

Support files, eBooks, discount offers and moreYou might want to visit www.PacktPub.com for support files and downloads related to your book.

Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.PacktPub.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at [email protected] for more details.

At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a range of free newsletters and receive exclusive discounts and offers on Packt books and eBooks.

http://PacktLib.PacktPub.com

Do you need instant solutions to your IT questions? PacktLib is Packt's online digital book library. Here, you can access, read and search across Packt's entire library of books.

Why Subscribe? f Fully searchable across every book published by Packt

f Copy and paste, print and bookmark content

f On demand and accessible via web browser

Free Access for Packt account holdersIf you have an account with Packt at www.PacktPub.com, you can use this to access PacktLib today and view nine entirely free books. Simply use your login credentials for immediate access.

Table of ContentsPreface 1Chapter 1: Getting Started 7

Introduction 7Installing GateIn from a binary package 8Building and installing GateIn from the source code 10Configuring the content storage 13Configuring GateIn to send e-mails 18Running GateIn on your machine 20Setting up the development environment 22

Chapter 2: Managing Portal Contents Using the GUI 29Introduction 29Managing portals 30Managing portal pages 37Managing registered portlets 42Managing the navigation tree 50Managing the dashboard 53

Chapter 3: Managing Portal Contents Using XML 59Introduction 59Managing portals using XML 60Managing portal pages using XML 66Managing registered portlets using XML 68Managing the navigation tree using XML 73Wrapping it all up in a separate EAR 75

Chapter 4: Managing Portal Users 89Introduction 89Managing users 90Managing groups 93

ii

Table of Contents

Assigning users to groups 97Integrating with an existing LDAP store 100Setting an automatic membership after user creation 115Adding a custom field in the user profile 117Integrating with Web SSO 118Integrating with SPNEGO for Desktop SSO 122

Chapter 5: Securing Portal Contents 129Introduction 129Securing portals 130Securing with JBoss AS 133Securing with Tomcat 134Choosing the JAAS modules 136Creating a login page 138Synchronizing users 140Securing pages 142Securing categories 145Securing applications 150Securing portlets 153

Chapter 6: Developing Portlets 157Introduction 157Creating a portlet with the Portlet 2.0 Specification 158Using an action to pass form parameters 162Using the user locale to localize portlet content 166Communicating between portlets using Public Render Parameters 172Communicating between portlets using events 181

Chapter 7: Developing Using Components API 193Introduction 193Getting started with WebUI 194Creating views 200Handling different skins in a portlet 207Adding the JavaScript resources to the portlet 218Handling different locales in a portlet 220

Chapter 8: Migrating from Existing Portals 227Introduction 227Migrating a transactional portlet 228Migrating an authenticated portlet 231Migrating a portlet that uses JCR 237Importing a skin from an existing website 244

iii

Table of Contents

Chapter 9: Managing Gadgets 253Introduction 253Importing existing gadgets 254Removing gadgets 260Creating gadgets 264Changing the category of a gadget 270Resizing gadgets 275Making the gadget a portlet 277Setting user preferences 280

Chapter 10: Frameworks in a Portal 285Introduction 285Creating a JSF 2 portlet 285Using jQuery in a portlet 298Using portlet events in JSF 2 305Creating a RichFaces 4 portlet 312

Chapter 11: Managing Portal Resources with the Management Component 319

Introduction 319Deploying the CLI to GateIn 320Retrieving a managed resource with RESTful Interface 322Exporting a portal page with the CLI 324Removing a portal page from the navigation 326Using the default portal as the foundation for a new portal site 329

Chapter 12: Managing Documents Using External ECM Systems 333Introduction 333Creating a portlet to integrate a CMIS repository 334Creating a portlet to integrate JBoss ModeShape 348Creating a portlet to integrate Apache JackRabbit 356Creating a portlet to integrate Alfresco using Spring WebScripts 360

Index 367

PrefaceEnterprises have websites constructed in different web frameworks and the need for them to work together cohesively. GateIn will provide the solution to effectively integrate them into a single website. GateIn is an open source website framework that does more than a web framework by letting you use your preferred one.

This GateIn Cookbook provides solutions whether you're planning to develop a new GateIn portal, migrate a portal, or only need to answer a specific query. It is filled with bite-sized recipes for quick and easy problem resolution. From the beginning to the end it will guide you through the process of configuring and securing a portal, managing content and resources, and developing applications as you go.

Beginning with installation and configuration, the book swiftly moves on to discussing content, users, and security. The second half covers all aspects of developing on a portal, such as portlets, gadgets, migration, and integration.

The goal of the book is to show GateIn as an open source website framework piece by piece. Starting with simple recipes, you will see each step analyzed with code examples and images, before progressing to more advanced recipes.

This GateIn Cookbook will help you with a quick approach to building portals.

What this book coversChapter 1, Getting Started, introduces the installation process of GateIn, using either a source code or the binary package. You will also learn how to configure storage and the email sender. Followed by how to set up the development environment for your customization.

Chapter 2, Managing Portal Contents Using the GUI, explains how to manage portal contents using the provided GUI. You will learn how to manipulate the core contents of the portal, starting with portal instances and continuing on to pages and portlets. You will then see how to configure the navigation tree of the portal and how to use dashboards.

Preface

2

Chapter 3, Managing Portal Contents Using XML, explores the same portal contents discussed in the previous chapter but shows how to manage everything through XML configuration. It will then guide you through implementing a separate portal instance with its own EAR.

Chapter 4, Managing Portal Users, introduces how to manage users and and how to setup an external LDAP store for managing authorities. It will show you how to extend and customize the default provider of the user profile and how to configure a Single Sign On (SSO) mechanism with some SSO providers.

Chapter 5, Securing Portal Contents, discusses how to configure portal security with Tomcat and JBoss, and explores all the JAAS modules available in GateIn. You will also learn how to set different roles and permissions for any portal contents.

Chapter 6, Developing Portlets, contains all the information you need to start implementing standard portlets. It will also cover the usage of the user locale, public render parameters and events.

Chapter 7, Developing Using Components API, introduces WebUI for creating new views and handling different skins in a portlet. You will also learn how to manage JavaScript and user locales.

Chapter 8, Migrating from Existing Portals, explains some methods for migrating existing portlets. You will see how to migrate transactional, authenticated, and JCR-based portlets. You will then learn how to import a skin from an existing website.

Chapter 9, Managing Gadgets, demonstrates how to import, remove, and create gadgets, and how to categorize them in the portal. You will see how to expose a gadget as a portlet and how to set user preferences.

Chapter 10, Frameworks in a Portal, includes an overview of the framework that you can use to implement portlets. You will learn how to implement a portlet using JSF 2, jQuery, and RichFaces 4.

Chapter 11, Managing Portal Resources with the Management Component, covers how to manage portal contents using the REST API and Command Line Interfaces (CLI).

Chapter 12, Managing Documents Using External ECM Systems, explores how to implement portlets for integrating some ECM systems such as any CMIS-compliant repository, JBoss ModeShape, Apache JackRabbit, and Alfresco. You will also see how to implement portlets using Spring WebScripts in Alfresco.

What you need for this bookIn Chapter 1, Getting Started, you will see all the required components for building and executing GateIn. In summary, you should have:

f Any operating system based on Linux, Mac OS X, or Windows

Preface

3

f Java Development Kit (JDK) 1.6

f Eclipse IDE

f Apache Subversion (latest version)

f Apache Maven (latest version)

f GateIn Portal 3.2.0 and beyond

Who this book is forThis book is for anyone who needs to administer or develop applications on GateIn. Java and RESTful architecture skills are suggested but not needed.

ConventionsIn this book, you will find a number of styles of text that distinguish between different kinds of information. Here are some examples of these styles, and an explanation of their meaning.

Code words in text are shown as follows: "We can include other contexts through the use of the include directive."

A block of code is set as follows:

<object type="org.exoplatform.services.organization.impl.NewUserConfig$JoinGroup"> <field name="groupId"> <string>/platform/users</string></field> <field name="membership"> <string>member</string></field> </object>

When we wish to draw your attention to a particular part of a code block, the relevant lines or items are set in bold:

<object type="org.exoplatform.services.organization.impl.NewUserConfig$JoinGroup"> <field name="groupId"> <string>/platform/users</string></field> <field name="membership"> <string>member</string></field> </object>

Any command-line input or output is written as follows:

>mvn clean package

Preface

4

New terms and important words are shown in bold. Words that you see on the screen, in menus or dialog boxes for example, appear in the text like this: "clicking the Next button moves you to the next screen".

Warnings or important notes appear in a box like this.

Tips and tricks appear like this.

Reader feedbackFeedback from our readers is always welcome. Let us know what you think about this book—what you liked or may have disliked. Reader feedback is important for us to develop titles that you really get the most out of.

To send us general feedback, simply send an e-mail to [email protected], and mention the book title through the subject of your message.

If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, see our author guide on www.packtpub.com/authors.

Customer supportNow that you are the proud owner of a Packt book, we have a number of things to help you to get the most from your purchase.

Downloading the example codeYou can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Preface

5

ErrataAlthough we have taken every care to ensure the accuracy of our content, mistakes do happen. If you find a mistake in one of our books—maybe a mistake in the text or the code—we would be grateful if you would report this to us. By doing so, you can save other readers from frustration and help us improve subsequent versions of this book. If you find any errata, please report them by visiting http://www.packtpub.com/support, selecting your book, clicking on the errata submission form link, and entering the details of your errata. Once your errata are verified, your submission will be accepted and the errata will be uploaded to our website, or added to any list of existing errata, under the Errata section of that title.

PiracyPiracy of copyright material on the Internet is an ongoing problem across all media. At Packt, we take the protection of our copyright and licenses very seriously. If you come across any illegal copies of our works, in any form, on the Internet, please provide us with the location address or website name immediately so that we can pursue a remedy.

Please contact us at [email protected] with a link to the suspected pirated material.

We appreciate your help in protecting our authors, and our ability to bring you valuable content.

QuestionsYou can contact us at [email protected] if you are having a problem with any aspect of the book, and we will do our best to address it.

1Getting Started

In this chapter we will cover:

f Installing GateIn from binary package

f Building and installing GateIn from the source code

f Configuring the content storage

f Configuring GateIn to send e-mails

f Running GateIn on your machine

f Setting up the development environment

IntroductionThis first chapter will discuss how to install a GateIn instance on your local environment. We will see that there are two main ways to achieve this task.

We will then look at how to configure a different content store for data so that you can use your preferred DBMS and your file system. Then we will see how to set up the GateIn mail sender to allow you to use the SMTP server available in your network.

At the end of the chapter, you will understand what the architecture of GateIn is and how to configure and run a portal instance on your machine.

GateIn is a portal framework dedicated to implementing highly customizable portal solutions with a powerful set of components based on well-known and adopted open source technologies and standards based on the Java web technologies.

Getting Started

8

Installing GateIn from a binary packageGateIn is released through different bundles that differ for the included application server. These bundles are dedicated to the following application servers—JBoss AS 5.x, JBoss AS 6.x, JBoss AS 7.x, Tomcat 6.x, Tomcat 7.x, and Jetty 6.x.

In this recipe, we will see which are the required packages you need to download before starting a GateIn instance.

Getting readyA pre-requisite for running GateIn is the availability of the Java Development Kit (JDK), which must be updated to the latest revision of version 1.6. Depending on your operating system, please be sure to correctly add the JDK binaries' path to the classpath of your machine. In order to verify the Java version actually installed on your machine, you can run the following command line:

java –version

It will return an output similar to the following:

java version "1.6.0_29"

Java(TM) SE Runtime Environment (build 1.6.0_29-b11-402-10M3527)

Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02-402, mixed mode)

If you don't find in the output the string Server VM, but you find the string Client VM in the place of Server VM, this means that the JDK is not installed on your machine. In this case, you can download the JDK 1.6 for your operating system at http://www.oracle.com/technetwork/java/javase/downloads/jdk-6u31-download-1501634.html.

How to do it...1. As introduced before, GateIn can be installed using different packages, so now you

can download your preferred bundle depending on which application server you need to use for your installation.

2. Typically, the right application server can be chosen by thinking about what you need for your software architecture. Let's assume that you need to deploy GateIn together with other standard Java EE applications that need explicit support for middleware components such as DataSource, Enterprise Java Bean (EJB), Java Message Service (JMS), or an Enterprise Service Bus (ESB). You would then probably want to use the JBoss AS bundle. JBoss AS is completely focused on middleware support having the availability of a total J2EE-compliant application server. Otherwise, if you only need to use a standard servlet container, you can choose to download the Tomcat bundle.

Chapter 1

9

3. Once you have chosen the right bundle for yourself, you are ready to download it from http://www.jboss.org/gatein/downloads.

4. After downloading the GateIn package, you can extract it in your preferred directory in your file system.

How it worksYou have just installed GateIn from binary package.

There's more...The difference between the various bundles is related to application server-specific configuration files and directories, with some added artifacts. For example, the JBoss 6.x directory structure consists of the following folders:

f bin

f client

f common

f docs

f lib

f server

The Tomcat 6.x bundle is based on a different structure:

f bin

f conf

f gatein

f lib

f logs

f temp

f webapps

f work

The Tomcat bundle also has a further execution script compared to the JBoss bundle, but we will see how to run a GateIn instance for all the different application servers later in the chapter, in another recipe.

Getting Started

10

See also f The Building and installing GateIn from the source code recipe

f The Running GateIn on your machine recipe

Building and installing GateIn from the source code

Another way to install GateIn is by getting the source code from the JBoss SVN repository and then compiling it to generate the build artifacts as you found in the binary package.

The goal of building GateIn from the source code is to have the possibility to freely customize it. Another useful reason for building from source is that you simply want to contribute to the project and test your fixes before applying the patch on the issue tracker.

Notice that this book is based on GateIn 3.2.0, which has its source code based on the SVN repository. From GateIn 3.3.x the source code is based on a GitHub repository. For more information about how to build from GitHub, please see https://github.com/gatein/gatein-portal.

Getting readyLet's first see if you have installed in your machine the following needed components:

f Java Development Kit (JDK) 1.6

f Subversion client (SVN)

f Apache Maven 3.x

Let's check your environment for the see following required tools:

1. You can test and install JDK 1.6 following the Getting ready section of the Installing GateIn from a binary package recipe.

2. To test if the SVN client is installed in your machine run the following command line:svn --version

If the SVN client is correctly installed you should see output similar to the following:svn, version 1.6.17 (r1128011)

compiled Aug 25 2011, 17:31:03

Copyright (C) 2000-2009 CollabNet.

Subversion is open source software, see http://subversion.apache.org/

Chapter 1

11

If the svn command is not found in the scope of your operating system, then you can install it by following the instructions available on the official website, depending on your operating system, at http://subversion.apache.org/packages.html.

3. Once the SVN client is installed, the next step is to test if Apache Maven is installed on your machine, by running the following command line:mvn --version

After running the command, you should see an output similar to the following:Apache Maven 3.0.4 (r1232337; 2012-01-17 09:44:56+0100)

Maven home: /Users/piergiorgiolucidi/tools/apache-maven-3.0.4

Java version: 1.6.0_29, vendor: Apple Inc.

Java home: /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home

Default locale: it_IT, platform encoding: MacRoman

OS name: "mac os x", version: "10.6.8", arch: "x86_64", family: "mac"

If the mvn command is not found in your machine then it could mean either of the following two things:

� You have to configure the PATH environment variable

� You have to install Apache Maven from scratch

We are going to assume that maven is not installed. In this way, we will describe both the cases:

� Download the Apache Maven binary at http://maven.apache.org/download.html.

� Extract the binary package and follow the specific instructions that are described in the web page according to your operating system.

Notice that you have to set two environment variables to allow Maven to work correctly, so please be sure to set the PATH and M2 variables before continuing. For instance, for a Linux-based operating system, you should have a snippet in your profile configuration file similar to the following:export M2_HOME="/Users/piergiorgiolucidi/tools/apache-maven-3.0.4"

export M2=$M2_HOME/bin

export MAVEN_OPTS="-Xms512m -Xmx1024m -Xss1024k -XX:MaxPermSize=256m -XX:NewSize=256m"

export PATH=$M2:$PATH

4. Finally, check if you have installed the latest stable release of Apache Maven before continuing the recipe.

Getting Started

12

How to do it...Now we are ready to check out the source code. For this book, we will consider the current latest stable version of GateIn Portal, which is version 3.2.0.

1. Check out the code on your machine by executing the following command line:svn checkout http://anonsvn.jboss.org/repos/gatein/portal/tags/3.2.0-GA/ gatein-portal-3.2.0

2. Once you have finished the download, you will find a new subfolder gatein-portal-3.2.0 in the folder where you have executed the command that contains all the source code of the project.

3. Now we are ready to build the project using Apache Maven. For this step, we have some parameters to set:

� Which type of bundle you have

� Whether the build process must download the application server distribution

� The path of the root folder of your application server distributions (if you have an existing distribution package)

� The name of the folder related to the application server distribution to use for building

4. Let's assume that you want to build GateIn with the Tomcat 6 bundle and, considering that the Tomcat distribution is stored in the /Applications/Apache Tomcat/apache-tomcat-6.0.32 folder, you have to execute the following command line from the root folder of the project that contains the main pom.xml file:mvn clean install -DskipTests -Ppkg-tomcat -Dexo.projects.directory.dependencies=/Applications/Apache Tomcat -Dexo.projects.app.tomcat.version=apache-tomcat-6.0.32

Upon completion, you should see the following output:[INFO] -------------------------------------------

[INFO] BUILD SUCCESS

[INFO] -------------------------------------------

The binary artifacts will be available on your machine inside the /gatein-portal-3.2.0/packaging/tomcat/pkg/tc6/target/tomcat6 folder.

How it works...As you have seen, the GateIn packaging process relies upon Apache Maven to execute the build process of the project. However, Maven allows you to manage the entire lifecycle process of the project and its own children modules by starting a command from a parent Project Object Model (POM).

Chapter 1

13

In this recipe, we have invoked two Maven phases to execute the build process of the project—the clean phase and the install phase.

The clean phase will remove all the compiled artifacts created by prior builds to initialize the build state of the project. Typically, the final result of the clean goal is that the target folder will be removed for each submodule of the project.

The install phase will start the complete Maven lifecycle executing all the phases up to install. The details of this execution process are as follows:

1. Validate

2. Compile

3. Test

4. Package

5. Integration-test

6. Verify

7. Install

Each phase is responsible for specific goals that need to be processed before the next phase.

The parameter skipTests used in the previous Maven command allows skipping the execution of tests from the lifecycle process. This means that when Maven is executing, the entire process will skip the execution of the test phase for each module.

So if you want to contribute to the project or you are customizing the product for your needs, you will probably want to remove this parameter. This is because if you want to be sure that your own implementation correctly works with all the other modules included in GateIn, you probably want to execute all the tests included in the project.

See also f The Installing GateIn from a binary package recipe

f The Running GateIn on your machine recipe

f The Setting up the development environment recipe

Configuring the content storageBy default, GateIn is configured to use the Hypersonic SQL database and a relative folder in the bundle to manage the content storage. This type of setting is only useful for running a product demo without specifying any parameter, but this is not the suggested way to set up the platform for a production environment.

Getting Started

14

For production system environments, you should configure GateIn for using external DBMS and using a file system with good I/O performance to prevent bottlenecks during reading and writing operations on contents and search indexes.

We are going to configure the components that manage content storage in GateIn:

f exoJCR: This is an open-source implementation of the Java Content Repository (JCR) specification provided by the eXo Community. It is used internally by GateIn to manage all the content data related to portals, pages, portlets, and metadata.

f PicketLink IDM: This is an open-source project that can be embedded in any standard Java EE application and allows managing user identities by storing the information in a database. It also supports many ways to negotiate credentials and Single Sign On (SSO) mechanisms.

Let's first configure exoJCR, which is the component responsible for the content storage in the portal. This component consists of two physical storage elements:

f Database

f File system

Finally, we will see how to configure a JBoss identity that is based only on a separate database instance.

Getting readyLocate the configuration file in the shared class loader according to your application server. Assuming that you are using Tomcat, the configuration file is located here:

<TOMCAT6_HOME>/gatein/conf/configuration.properties

If you are using JBoss AS 6 you will find it at the following location:

<JBOSS6_HOME>/server/default/conf/gatein/configuration.properties

If you want to use JBoss AS 7, you have to edit the configuration file here:

<JBOSS7_HOME>/standalone/configuration/gatein/configuration.properties

How to do it...Carry out the following steps to configure the content storage:

1. Locate the following properties inside the configuration file:

� gatein.jcr.datasource.driver

� gatein.jcr.datasource.url

Chapter 1

15

� gatein.jcr.datasource.username

� gatein.jcr.datasource.password

The default database settings of the JCR component of GateIn are based on these standard JDBC parameters:

� gatein.jcr.datasource.driver=org.hsqldb.jdbcDriver

� gatein.jcr.datasource.url=jdbc:hsqldb:file:${gatein.db.data.dir}/data/jdbcjcr_${name}

� gatein.jcr.datasource.username=sa

� gatein.jcr.datasource.password=

The above parameters will configure your HSQL database in a folder in your file system. As you can see, the parameter named gatein.db.data.dir, used for the value of the parameter gatein.jcr.datasource.url, allows you to set a specific folder that will contain all the content storage dedicated to the GateIn instance.

These parameters allow you to set the typical settings for a JDBC connection, so for example, if you need to configure Oracle DBMS for the JCR database, you should have a similar configuration to the following:

� gatein.jcr.datasource.driver=oracle.jdbc.OracleDriver

� gatein.jcr.datasource.url=jdbc:oracle:thin:@<HOST>:<PORT>:gateInJcr

� gatein.jcr.datasource.username=<ORACLE USERNAME>

� gatein.jcr.datasource.password=<ORACLE USER PASSWORD>

2. You or your DBA must create the database schema and the user above in your DBMS instance before running GateIn. The user credentials used by GateIn must have all the necessary permissions to manage tables and rows in the database instance. Notice that the default encoding for the database needed by GateIn is latin1, so be sure to set this parameter in your DBMS configuration.

3. Remember to copy the database JDBC driver (packaged as a JAR) to the library folder of your application server. In this way, GateIn can correctly use the existing database instance.

For Tomcat, you can copy the JDBC driver in this folder:<TOMCAT6_HOME>/lib

For JBoss 6 AS you can use this one:<JBOSS6_HOME>/server/default/lib

For JBoss 7 AS the following directory is available here:<JBOSS7_HOME>/standalone/lib

Getting Started

16

4. Now that we have configured the database for the JCR component, we can continue configuring the JCR file system location. This section allows you to set a specific file system dedicated to store all the binaries and search indexes files managed by GateIn. The default properties' values relating to the file system configuration are as follows:

� gatein.data.dir=../gatein/data

� gatein.jcr.data.dir=${gatein.data.dir}/jcr

� gatein.jcr.storage.data.dir=${gatein.jcr.data.dir}/values

� gatein.jcr.index.data.dir=${gatein.jcr.data.dir}/lucene

Using these parameters, you can change the default location of the content binaries of GateIn. Here, you can change the default configuration for contents and search indexes as needed for your architecture.

5. Configure another database schema dedicated to the identity manager.

6. The built-in properties' values for the database JDBC connection of the identity management are the following:

� gatein.idm.datasource.driver=org.hsqldb.jdbcDriver

� gatein.idm.datasource.url=jdbc:hsqldb:file:${gatein.db.data.dir}/data/jdbcidm_${name}

� gatein.idm.datasource.username=sa

� gatein.idm.datasource.password=

As seen above for the JCR database settings, a potential configuration for an Oracle database dedicated to the identity manager could be the following:

� gatein.idm.datasource.driver= oracle.jdbc.OracleDriver

� gatein.idm.datasource.url=jdbc:oracle:thin:@<HOST>:<PORT>:gateInIdm

� gatein.idm.datasource.username=<ORACLE USERNAME>

� gatein.idm.datasource.password=<ORACLE USER PASSWORD>

How it works...In the previous steps, we have configured the content storage of GateIn that will be used to create all the needed data structures (tables in the database and folders in the file system) for managing portal contents.

When the application server runs for the first time, GateIn will automatically create all the needed data structures in the database and inside the file system.

Chapter 1

17

The database connection used in GateIn is a standard JDBC connection that requires the usual parameters for configuration:

f JDBC driver class

f JDBC URL

f Username

f Password

The driver class is the Java class of the DBMS driver that allows remote access to the database instance for managing JDBC connections.The value for the JDBC URL must follow your specific DBMS conventions to locate the database instance remotely via JDBC.The username and password are related to a specific users credentials that GateIn must use to access and manipulate contents in the database.

There's more...You also have the possibility to configure contents and indexes to increase performance during searching or creating contents. This is done by changing locations to machines with better hardware for both of the components.

A typical approach is to dedicate the local file system storage to the Apache Lucene indexes (gatein.jcr.index.data.dir) and a high-performance storage location for contents, setting a different value for the gatein.jcr.storage.data.dir property.

Apache Lucene is an open-source text-search engine library used in many Enterprise products to create and manage content indexes. It supports execution of queries based on the Lucene Query Language, which is a very powerful query language that can search contents by ranking, phrase, and exact match. Lucene is licensed under Apache Software License Version 2.0.For more information about Lucene, you can visit the project website at http://lucene.apache.org/.

MySQL example configurationIn order to configure MySQL for the database storage of GateIn, you should edit the configuration file in a similar way for JCR, following the related conventions:

gatein.jcr.datasource.driver=com.mysql.jdbc.Driver

gatein.jcr.datasource.url=jdbc:mysql://<HOST>:<PORT>/gateInJcr

Getting Started

18

gatein.jcr.datasource.username=<MYSQL USERNAME>

gatein.jcr.datasource.password=<MYSQL USER PASSWORD>

For the identity manager you should have the following:

gatein.idm.datasource.driver=com.mysql.jdbc.Driver

gatein.idm.datasource.url=jdbc:mysql://<HOST>:<PORT>/gateInIdm

gatein.idm.datasource.username=<MYSQL USERNAME>

gatein.idm.datasource.password=<MYSQL USER PASSWORD>

Finally, remember to copy the MySQL JDBC driver library into the application server library folder in order to make the driver class available to GateIn.

See also f The Configuring GateIn to send e-mails recipe

f The Running GateIn on your machine recipe

Configuring GateIn to send e-mailsGateIn includes notification features that send e-mails to users when a specific action is performed on the portal, for instance the forgot password feature. There is another snippet to change in the configuration file to enable GateIn to send e-mails.

These are the usual properties required to configure an SMTP server in your local network.

Getting readyLocate the configuration file as described in the previous recipe.

How to do it...Carry out the following steps in order to configure GateIn to send e-mails:

1. Locate in the e-mail section of the configuration file the following snippet:# Emailgatein.email.smtp.username=gatein.email.smtp.password=gatein.email.smtp.host=smtp.gmail.comgatein.email.smtp.port=465gatein.email.smtp.starttls.enable=true

Chapter 1

19

gatein.email.smtp.auth=truegatein.email.smtp.socketFactory.port=465gatein.email.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory

2. Change the default configuration to allow GateIn, using your specific SMTP server settings for sending outgoing e-mails. As you can see, by default GateIn is configured to work on a Gmail account.

How it works...GateIn can send outgoing e-mails using its internal MailService, which needs to be configured using the usual parameters for setting up an SMTP connection:

f Username

f Password

f Host

f Port

f EnableTls

f Auth

f Socket factory port

f Socket factory class

Username and password are the values that identify the e-mail account that you want to use in GateIn.

Host and port are used to set your SMTP server endpoint address.

EnableTls and Auth are used to set the needed authentication mechanism to process any operation.

Socket factory parameters are specifically used for instancing secure connections on your SMTP server. In this case, be sure to have correctly installed any security library needed to process the SSL specific request.

See also f The Configuring the content storage recipe

f The Running GateIn on your machine recipe

Getting Started

20

Running GateIn on your machineLet's assume that you have correctly configured GateIn as shown in the previous recipes or are using the default settings found in the standard binary package. Now we are going to run GateIn for the first time.

Getting readyLocate the bin folder of your application server; this is the location where the start/stop script is stored in your machine. Here are the details for the scripts in different application servers:

f If you are using Tomcat then you have a specific script gatein.sh, for Windows gatein.bat, provided in the binary folder <TOMCAT_HOME>/bin

f For JBoss AS 6 you can use the standard run.sh script available in this folder <JBOSS6_HOME>/bin

f JBoss AS 7 requires using the script standalone.sh stored here <JBOSS7_HOME/bin>

How to do it...Supposing that we are using Linux as the operating system, we would follow these steps:

1. To start GateIn using Tomcat use the following command line from the current bin folder:./gatein.sh start

2. Once the command above executes, you can take a look at the log file with the following command:tail –f ../logs/catalina.out

Now you can start using the portal, pointing your browser at http://localhost:8080/portal.

Chapter 1

21

3. If the first deployment of GateIn finishes without errors we expect to see in the browser the following homepage of the default portal:

4. In order to stop the GateIn instance use the following command line:./gatein.sh stop

How it works...GateIn is distributed as a standard Java EE application that needs to be deployed in an application server.

In this recipe, we have seen how to start and stop a Tomcat instance by using a specific script file provided by GateIn developers.

For each specific application server you must follow the correct procedure to use the dedicated script file to start and stop the portal instance.

There's more…We will take a look at how to start GateIn with different versions of JBoss application servers.

Starting GateIn with JBoss AS 6 instanceIf you are using GateIn with JBoss AS 6, you have to use the following command line:

nohup ./run.sh &

Getting Started

22

Then you can access the log file in the following way:

tail –f nohup.out

To stop the instance you can use the following command line:

./shutdown.sh -S

Starting GateIn with JBoss AS 7 instanceOtherwise, if you are using JBoss AS 7 use the following command line:

nohup ./standalone.sh &

The log file can be viewed in this way:

tail –f nohup.out

To stop the JBoss 7 instance run the following command:

./jboss-cli.sh –connect command=:shutdown

See also f The Configuring the content storage recipe

f The Configuring GateIn to send e-mails recipe

Setting up the development environmentGateIn is developed using the Apache Maven lifecycle process and this guarantees that the project can be used with any standard development environment.

In this recipe, we will learn how to import the GateIn project in Eclipse IDE (you can use any other IDE that supports Maven). You will also see how to install JBoss tools in Eclipse to add support for developing standard portlets and all Java EE components provided by JBoss.

Getting ready1. Download Eclipse IDE for Java EE Developers at http://www.eclipse.org/

downloads/.

2. Install Eclipse on your machine, launching the executable and following the installation instructions.

Chapter 1

23

How to do it...Set up your development environment following these steps:

1. Launch Eclipse and create a new workspace dedicated to GateIn.

2. Click on Help | Eclipse Marketplace.

3. Search jboss tools and click on the Go button.

4. Locate the JBoss Tools version required for your specific Eclipse version and click on the Install button.

Getting Started

24

5. In the Confirm Selected Features window, select all the items and click on the Next button.

Chapter 1

25

6. In the Install Details window, click on the Next button.

7. In the Review Licenses window, read and accept the terms of the license agreements and click on the Finish button.

8. Wait for the installation process of JBoss tools to complete. Once the installation is finished, restart Eclipse.

Getting Started

26

9. After the restart, you should see the overview of what you can do with JBoss tools with a similar tab in your Eclipse environment:

10. Right-click on the Package Explorer tab and select Import.

11. Browse the Maven category and select Existing Maven Projects and press the Next button.

Chapter 1

27

12. Insert as Root Directory the GateIn source root folder that you have created in the previous recipe.

13. Click on the Next button and then click on Finish.

14. Be sure at the end of the import process to update the project configuration of all the Maven modules—in the Package Explorer window select all the modules, right-click, then select Maven | Update Project Configuration and finally click on OK.

Getting Started

28

How it works...The source code of GateIn is based on Maven, but by default, Eclipse IDE does not support it. The JBoss tools package includes the m2eclipse plugin that allows managing Maven projects inside Eclipse IDE. So in order to develop your own solutions make sure to use an IDE that supports Maven.

We have configured Eclipse to manage JBoss middleware and Maven projects and then we have imported the GateIn source code to let you contribute to the product or create your own customizations.

The following are all the example modules available in the GateIn source code:

f GateIn Portal Sample

� GateIn Portal Sample Extension

� Sample Configuration

� Sample Ear

� Sample War

� Sample Jar

� GateIn Portal Sample Portal

� GateIn Portal examples—Portlets

� API Sample Portlet

� JSF Sample Portlet

� JSP Sample Portlet

� Standard Sample Portlet

� Struts Sample Portlet

� Resource Serving Sample Portlet

� GateIn Skin Sample

See also f The Building and installing GateIn from the source code recipe

2Managing Portal

Contents Using the GUI

In this chapter we will cover:

f Managing portals

f Managing portal pages

f Managing registered portlets

f Managing the navigation tree

f Managing the dashboard

IntroductionIn this chapter, you will learn how you can manage the fundamental contents of the portal by using the default administration tools provided by GateIn.

First, we will discuss how to create new portal instances, and then we will cover in-depth how to create and remove pages from a portal. We will see how to add and remove portlets from pages and then how to create the navigation tree for your portal. Finally, we will describe how users can personalize their pages using the dashboard.

Managing Portal Contents Using the GUI

30

Once you have started GateIn, by default the portal is available at the following address:

http://localhost:8080/portal

This is the public portal, whose contents are available for any user; specifically, it delivers all the following pages:

f SiteMap

f Sign in

f Register

f Change Language

f Home

The SiteMap page contains a portlet that automatically creates the sitemap of the current default portal by navigating all the pages.

Managing portalsThis recipe will show you how to create and manage portal instances in GateIn using the built-in administration console. We are going to create a new portal instance for a financials site that can offer profiled services for clients.

Chapter 2

31

Getting ready1. Locate your browser at the default homepage of the portal.

2. Click on Sign in and authenticate with an administrator account, for instance using the root user:

� Username: root

� Password: gtn

3. After clicking on the Sign in button, you should see the administration toolbar on the top of the page with the following menu:

� GateIn icon

� Site

� Group

� Dashboard

� Site Editor

How to do it…We want to create a new private portal instance to offer financial services to clients of a potential company. During the first phase of the development of the portal, we only want administrators to be able to access the portal.

1. Click on Site | Add New Portal and insert the values shown in the following screenshot:

Managing Portal Contents Using the GUI

32

2. Click on Permission Setting to change the current options tab to where you can add and change permissions. Click on the Add Permission button and then click on Platform | Administrators.

3. Select manager from the Membership panel and click on Edit Permission Setting.

4. Click on the Select Permission button, and then click on Platform | Administrators.

5. Select manager from the Membership panel on the right and finally click on the Save button and you should see output similar to the following screenshot:

6. Check the new portal instance by visiting the following URL: http://localhost:8080/portal/financials

7. The portal now shows you a login form that you couldn't see earlier. Try to gain access using the administrator role and you should see the homepage of the financials portal.

How it works...The primary goal of this recipe was to show you how to create a new private portal instance.

By now, you will understand how to use the Site menu to create new portal instances simply by using the default options provided by the GUI.

We have created a new portal instance that can be accessed and edited only by administrators. This was just our starting approach, but notice that in the Permission Setting tab, you can adjust the visibility of the portal.

Remember that the Site menu is not only used to create new portal instances, but that it also allows you to quickly navigate all the portal pages.

Chapter 2

33

There's more...Let's now uncover other options we can set either during the creation of our portal instance or after modifying an existing one. We can take a look at the Properties tab (after the second step) in the following way:

1. Click on Site in the toolbar and then Edit Portal's Config for the financials portal.

2. Click on the Properties tab; here you can choose different ways to manage the sessions in the portal using the Keep session alive field values:

� Always: The session will terminate after a specific period

� On Demand: The session will terminate after a specific application request

� Never: The session will not terminate

3. The Show info bar by default is a checkbox to set the default value of a property for showing or hiding the information bar in the bottom of a portlet in any page.

� If you have checked the field, you will by default see the information bar for each application as the following:

� Otherwise, if the checkbox is not selected, you should see the same portlet without the border with the information bar

The visibility of the information bar can be changed for each portlet working on the Portlet Setting, as we will see later in the chapter.

Managing Portal Contents Using the GUI

34

Removing a portalIn order to remove an existing portal, you have to have administrator access to the portal. Suppose that we want to remove an old portal named oldFinancials. It can be removed by following the following steps:

1. Click on Site in the toolbar.

2. Click on Delete for the oldFinancials portal and then click on the OK button on the alert message.

Setting public access to the portalAnother option is to make the portal public in order to allow any user to navigate its pages.

Once you have created the new portal, supposing that you are authenticated as an administrator, you can edit access permission in this way:

1. Click on Site from the toolbar.

2. Click on Edit Portal's Config for the financials portal.

3. Click on Permission Setting.

4. As you can see, you can make the portal public simply checking the field Make it public. Notice that after checking this field, the Add Permission button will be removed from the form.

5. Click on the Save button.

Now the portal is public for everyone, meaning that anyone can visit all the public pages without receiving a prompt for the login.

Changing the layout of the portalGateIn will create new portal instances using the default layout. By using only the provided GUI, you can add, remove, or change the presentation blocks in any page in the portal. Changing the portal layout allows you to set a default presentation setting for any new page in the portal.

Let's take a look at the layout editor:

1. Click on Site in the toolbar.

Chapter 2

35

2. Click on Edit Layout for the financials portal.

3. As you can see, in the layout setting we have two different sections:

� The portal layout

� The Edit Inline Composer

4. Directly using the layout changes the order of the page blocks. The default page blocks included in a new portal are:

� Banner Portlet (GateIn logo)

� Navigation Portlet (portal navigation)

� Breadcrumbs Portlet (page tree)

� Page Description (copyright detail)

� Footer Portlet

Managing Portal Contents Using the GUI

36

5. We can change the order of the blocks in any page of the financials portal by moving the Navigation Portlet below the Breadcrumbs Portlet.

6. In the layout, leave your mouse pointer on the Navigation Portlet in the top-left section until the arrow became a hand.

7. Then, move the Navigation Portlet under the Breadcrumbs Portlet.

8. Finally, click on the disk icon in the top-right section of the Edit Inline Composer to save your changes.

9. Let's now see the new layout by clicking on Site from the toolbar and then clicking on financials:

Before changing this layout setting, the portal was based on a different order for presenting the two portlets, as shown in the following screenshot:

Chapter 2

37

See also f The Managing pages recipe

f The Managing registered portlets recipe

f The Assigning users to groups recipe in Chapter 4, Managing Portal Users

Managing portal pagesIn this recipe, we will discuss how to create and manage new pages in the portal. First, we will add a new user membership for editing the financials portal, and then we will see how to create new pages and how to modify all the options available in the GUI.

Getting readyIn order to manage pages, user must be authenticated in the portal with one of the following roles: Manager or Administrator. For this recipe, we will use John as the Manager of the financials portal.

First, we need to add the access permission to all the members of the administrators group, because although John is a member of the group, he is not a manager for the financials portal.

1. Log in using the root user.

2. Click on Site | Edit Portal's Config | Permission Setting | Edit Permission Setting.

3. Click on the Add Permission button.

4. In the group panel, select Platform | Administrators.

5. In the membership panel, select member.

6. Click on the Save button.

Now John can visit the new financials portal, but he cannot modify anything in the portal. To solve this problem, we are going to set a different editing permission for the portal:

1. Click on Edit Permission Setting.

2. Click on the Select Permission button.

3. Select Platform | Administrators from the group panel and then select * from the membership panel.

4. Click on the Save button.

Managing Portal Contents Using the GUI

38

Finally, John can edit the financials portal in the same way as the Root user can.

How to do it...We want to create a Contact us page where we will put a portlet dedicated to allow users to send e-mails to the company.

1. Open your browser on the financials home page: http://localhost:8080/portal/financials

2. Leave your pointer on Site Editor in the toolbar and click on Add New Page.

3. Now you should see the Page Creation Wizard. Click on the left panel on the green arrow to select the root node for the navigation. Be sure to select the page node /default:

Chapter 2

39

4. We will create a new page. Here we need to set properties for the new navigation node, in the same container of the default Home page, so select the same parent node (/default), and then fill in the fields related to the Contact page:

� Node name: This is the name of the node that contains the page (this is the JCR name of the node).

� Extended label mode: This allows enabling the i18n engine to translate the page label in different languages. When it is checked, it enables other two fields: Language and Display Name.

� Language: This allows selecting the specific language for the page label that you want to store in the page node using the related Display Name property. Selecting a new language allows you to insert a new label.

� Display Name: This is the label displayed for the page in the related locale of the language selected in the previous field.

� Visible: This sets the page visibility if it is checked; otherwise the page will not be visible.

� Publication date & time: This schedules when you want to publish the page. When it is checked, it enables two new fields: Start Publication Date and End Publication Date.

� Start Publication Date: This selects the publication date.

� End Publication Date: This selects the date for removing the visibility of the page in a specific period.

5. Notice that for this recipe it is not mandatory to set the publication dates, so now we can create a new page with the field values you saw in the previous screenshots without setting the publication schedule, by deactivating the checkbox Publication date & time.

6. Click on the Next button and choose a layout template for the page.

7. Leave the default template and click on the Next button to configure portlets in the page.

8. Click on the disk icon in the Page Editor to save the new empty page Contact us.

Managing Portal Contents Using the GUI

40

How it works...In this example, we have seen how to enable a manager user for your new portal. This is a typical task that you have to accomplish before you start editing your portal, because sometimes you don't want administrators to directly create all the pages of the portal.

Once manager users are ready to work, they can start adding and editing pages in the portal by choosing the parent page and the page properties.

Notice that in the recipe, we have chosen to add a new page node at the same level as the Home page, but remember that you are free to add any child page node on any page defined in the portal. This is done by selecting a different parent page from the left panel in the first step of the Page Creation Wizard.

As you know now, GateIn has a very flexible structure that allows you to define new users and groups that can have different roles in the portal. We will discuss more about permissions in Chapter 5, Securing Portal Contents.

There's more...We now suppose that you need to change some properties for the new page created in this recipe. We can access some of the details in the following way:

1. Navigate the portal to the Contact us page using the GUI or using the following URL: http://localhost:8080/portal/financials/contactus

2. Leave the pointer on Site Editor and then click on Edit Page | View Page Properties:

Chapter 2

41

3. Here you can only change the Page title. The fields in the preceding screenshot are as follows:

� Page Id: This is the identifier of the page in the GateIn repository.

� Owner type: This specifies the type of ownership, which can be either of two types: portal or group. The portal value means that a user who has edit permissions for the portal created the page for a specific portal. The group value means that this is a page created by a user for a specific group; this user has the manager role for the group.

� Owner Id: This is an identifier for the owner of the page, which could be a portal, a group, or a specific user. If the page is created for a portal, then the value will be the portal name, and so on in the case of a group or a user.

� Page name: This must be an alphabetical value, and it must be unique in the repository folder.

� Page title: This changes the HTML title of the current page.

� Show Max Window: If checked, this allows showing the page at maximum size in the browser.

4. If you take a look at Permission Setting, you can see that all the permissions are inherited by the portal permissions.

5. Change the Page title to Contact us and then click on the Save button.

An alternative way to access the page properties is based on using the Page Management portlet, in this way:

1. Access the portal as an administrator of the financials portal (root).

2. Leave your pointer on Group | Administration and then click on Page Management.

3. Fill the Title field with contact value and then click on the search button.

4. Click on the image that has the name Edit Page when hovered on under the Action column.

Managing Portal Contents Using the GUI

42

Removing a pageUsing the Page Management console that we have seen in the previous paragraph you can also remove pages:

1. Access the portal as an administrator of the financials portal (root).

2. Leave your pointer on Group | Administration and then click on Page Management.

3. Search to remove the pages using the Title and Site Name fields and click on the search icon.

4. Click on the trash icon for each page that you want to remove and then click on the OK button in the JavaScript pop-up box.

See also f The Managing portals recipe

f The Managing registered portlets recipe

Managing registered portletsSo far, we have discussed portals and how to create pages. Now, you are ready to learn how to use portlets.

A Portlet is an application that contains specific business logic that provides a fragment of content in a portal page. The information shown as the output of a portlet could depend on specific user permissions. Permissions are related to the user accessing the page; this means that the potential output for the fragment is dynamic for each user. Each portlet is hosted in a Portlet Container that manages the execution of portlet requests and provides a user-session mechanism. The sessions are used to check permissions and to show user-oriented information for each active session in the portal.

In this example, you will learn how to add and remove applications against portal pages.

We want to create a standard portlet for showing details in the Contact us page that we have created in the previous recipe.

Chapter 2

43

In this recipe, we will start by implementing a portlet as a Gadget without writing specific Java code.

A Gadget is a simple application written in XML and JavaScript that allows you to create custom dashboards for specific user-oriented features that can be used by any gadget container.

Getting readyLog in as root in the portal.

How to do it...First, let's create a new gadget showing the HTML content for our new Contact us page:

1. Leave your pointer on Group | Administration and click on Application Registry.

2. Click on Create a new gadget.

3. Set the value Contactus in the Name field and type the following code in the Source field:<?xml version="1.0" encoding="UTF-8" ?><!-- ** * * Author : Piergiorgio Lucidi *

Managing Portal Contents Using the GUI

44

* Apr 06, 2012 *--><Module> <ModulePrefs author="Piergiorgio Lucidi" title="Contact us" directory_title="Contact us" title_url="http://www.open4dev.com" description="This is the Contact us page of the example Financials portal." height="600"> <Locale lang="en"/> </ModulePrefs> <Content type="html"> <![CDATA[ <body> <div> <h2>Your company name</h2> <strong>Our Office</strong> <p>Your company address</p> <p>Your company phone number</p> <strong>Email us</strong> <p><a href="mailto:[email protected]">[email protected]</a></p> </div> </body> ]]> </Content></Module>

4. Click on the Save button and you should find the new gadget available in the list on the left panel.

5. Add a category for this new gadget by clicking on Click here to add into categories.

6. Select Gadgets and click on the Save button.

7. Now we have a new portlet available in the Gadgets category. In order to use it in the Contact us page, leave your pointer on Site | financials and click on Contact us.

Chapter 2

45

8. Leave your pointer on Site Editor and click on Edit Page.

9. In the Page Editor panel, click on Gadgets in the Applications tab.

10. Listed among Gadgets you should see the new Contact us gadget. Drag-and-drop it in the page panel on the left.

11. Click on Switch View Mode to take a look at the page preview before saving your changes.

12. Click on the disk icon at the top-right of the Page Editor.

13. Finally, you should see the HTML fragment in the Contact us page as shown in the following screenshot:

How it works...GateIn supports standard portlets and standard gadgets as applications for pages.

In this recipe, we have seen how to create a new standard custom gadget to create an HTML fragment for a block in the page.

This is done because GateIn doesn't provide any mechanism for dynamic web content management, but you can implement your own portlet to achieve this goal for your specific requirements.

Managing Portal Contents Using the GUI

46

Notice that the Enterprise versions of GateIn, provided by JBoss and eXo, include specific types of portlets dedicated to managing web content.The differences between portlets and gadgets are related to your specific requirements, and depend on the type of business logic that you need to implement. A gadget is based on XML and JavaScript code, this means you can't write complex business logic rules for your client application. You can only implement a simple client application for rendering the HTML fragment for the page. However, if you need an Enterprise-class code for your business logic and you need to write your own Java code, you have to implement a standard portlet for your client. In this way you will have all the capabilities offered by the portlet standard that are available in GateIn as a standard portlet container.

There's more...Of course, you can try to use other portlets in the Page Editor. The categories available in the Applications tab are as follows:

f Administration

f Dashboard

f Gadgets

f Web

Chapter 2

47

Under the Administration category, you will find the following portlets:

f Application Registry: This allows to manage applications (portlet and gadgets)

f New Account: This exposes the registration feature

f Organization Management: This manages users, groups, and memberships

The Dashboard category contains the following:

f Dashboard Portlet: This manages the dashboard of users

f Gadget Wrapper Portlet: The default wrapper used for gadgets

Note that the dashboard will be discussed in the recipe Managing the dashboard of this chapter.

The Gadget Wrapper Portlet is the default wrapper that is automatically applied when you drag-and-drop a gadget in the page. This is done because in any portlet container, you need a portlet implementation for each application and GateIn provides a standard wrapper for supporting gadget applications.

The default Gadgets provided in GateIn are:

f Calculator

f Calendar

f Rss Reader: An example of a simple RSS Reader

f Todo: Manages a simple to-do list

Finally, the last category available is Web, which contains the following portlets:

f IFrame: Renders an HTML iframe tag configuring the src attribute in the portlet preferences

f SiteMap: Dynamically generates the HTML fragment for the default sitemap

Using the Containers tab, you can add some sections in the page with a dedicated layout, with the following options:

f Rows Layout: This allows positioning portlets in rows

f Columns Layout: This allows positioning portlets in columns

f Autofit Columns Layout: This allows positioning portlets in autofit columns

f Tabs Layout: This allows creating tab sections

Managing Portal Contents Using the GUI

48

f Mixed Layout: This allows using mixed-layout sections

By simply dragging-and-dropping portlets on the page, you can set up a different output for the page. For instance, we can now try to set up a three-columns layout and then add three different applications in the same row:

1. Leave your pointer on the Site Editor and click on Edit Page.

2. Remove the existing Contact us portlet. from the page.

3. Click on the Containers tab in the Page Editor.

4. Click on Columns Layout and drag-and-drop the Three Columns layout in the page.

5. Click on Applications tab.

6. Drag-and-drop the Contact us page.

7. Drag-and-drop the Calendar gadget.

8. Drag-and-drop the Calculator gadget.

9. Click on the disk icon at the top right of the Page Editor to save the page.

Chapter 2

49

You should see output in your page similar to the following screenshot:

Managing portlet settingsIn this section, we will see how we can manage the settings for a portlet:

1. With the Page Editor you can drag-and-drop applications inside page sections. It is also possible to change settings for existing portlets using the tooltip available in the page layout:

� Grid icon: Allows changing the location of the portlet

� Pencil icon: Allows changing portlet settings

� X icon: Removes the portlet instance from the page

2. Click on the pencil icon.

3. The default selected tab is Portlet Setting. Here you can change the following options for categorizing and changing the output of the portlet:

� Portlet Title: Sets the title of the portlet

� Width: Sets the size in pixels of the width

Managing Portal Contents Using the GUI

50

� Height: Sets the size in pixels of the height

� Show Info Bar: Manages the rendering of the information bar

� Show Portlet Mode: Manages the rendering of the portlet mode

� Show Window State: Manages the rendering of the windows state

� Description: Changes the description

4. Click on the Select Icon tab to select a new icon for the portlet.

5. If you don't want to change the portlet icon, skip to the next step. Otherwise, select the category on the left panel and then select your preferred icon for the portlet. Click on the Save And Close button to save your new choice.

6. Click on Decoration Themes tab to choose a new theme for the portlet.

7. If you don't want to change the portlet theme you can skip to the next step. Otherwise, choose the style from the select list and then select one of the available styles in the left panel. Notice that you have a complete preview of the selected theme on the right panel. When you find the right theme for your portlet, click on the Save And Close button.

8. Click on the Access Permission tab for setting specific permissions for the current portlet instance. By default, the portlet has public access, but by deselecting the checkbox Make it public, you will be able to set different permissions.

9. In order to set different permissions click on the Add Permission button.

10. Select a group in the left panel.

11. Select the membership type on the right panel.

12. Click on the Save And Close button.

See also f The Managing portal pages recipe

f The Managing the navigation tree recipe

f The Managing the dashboard recipe

Managing the navigation treeFor each portal instance, you can explicitly manage the navigation tree. We will see how to create new nodes for the navigation tree, select the page instance for the navigation node, and change access permission on-the-fly.

In order to manage the navigation tree you need to know that each navigation node must be associated with a specific page node.

Chapter 2

51

We discussed in the Managing portal pages recipe that while creating a new page, the portal automatically creates a navigation node for the page for showing the page in the portal structure.

The navigation structure will be rendered in a different way for each user based on the user identity and group memberships.

Getting ready1. Log in to GateIn as administrator of the financials portal.

2. Click on Site in the toolbar.

How to do it...The navigation tree can be managed by following these steps:

1. Click on Edit Navigation for the financials portal to open Navigation Management.

2. Right-click on the Contact us node:

Managing Portal Contents Using the GUI

52

3. As you can see in the preceding screenshot, for each navigation node you can execute the following operations:

� Add new Node: Adds a child node for the selected node

� Edit Node's Page: Manages the related page node with the Page Editor

� Edit this Node: Changes the selected navigation node properties

� Copy Node: Copies the current node in order to create a new one

� Clone Node: Copies the current node with the same content of the current node in its own page

� Cut Node: Cuts the current node for pasting in another section

� Delete Node: Removes the current node from the tree

� Move Up

� Move Down

Move Up and Move Down allow you to change the presentation order inside the portal by moving the selected navigation node according to your needs.

4. Click on Edit this Node.

5. Click on Page Selector to access to the same field settings that we have discussed in the Page Creation Wizard in the Managing portal pages recipe:

Chapter 2

53

6. The Page Selector tab manages the association between the current navigation node and the page node. Here you can press two buttons:

� Clear Page: Removes the associated node page

� Search and Select Page: Allows you to choose a new page node

7. Click on the Search and Select Page button.

8. In the Select Page window, you can select a different page to associate to the current navigation node. First, using the Title, Site name, and Type fields you can search your preferred page by clicking on the search icon, then you can click on the confirmation icon to associate the new page to the current navigation node.

9. By clicking on the Icon tab, you can change the navigation node icon to show in the navigation tree.

10. Select the icons category from the left panel, and then you can select your preferred icon from the right panel, then click on the Save button.

How it works...GateIn manages pages using different node types:

f Navigation nodes

f Pages nodes

By now, you should understand that for each page the portal needs to define an association instance between a navigation node and a page node.

See also f The Managing portal pages recipe

f The Managing registered portlets recipe

f The Managing the dashboard recipe

Managing the dashboardGateIn allows each user to manage his own dashboard for personalizing private pages with applications. In this section, we will describe how users can customize and organize all the stuff in private pages under the dashboard to suit their needs.

Let's start customizing the dashboard by adding new pages, layout, and applications.

Managing Portal Contents Using the GUI

54

Getting readyAuthenticate in the portal using the demo user (this is a standard registered user).

How to do it...Carry out the following steps to manage dashboards:

1. Click on Dashboard in the toolbar.

2. Type My Gadgets for the current dashboard page title in the tab field.

3. In order to add new dashboard pages or edit existing ones, leave your pointer on Dashboard Editor, and then you can use the features that we have discussed in the Managing portal pages recipe, so you can:

� Add New Page

� Edit Page

� Edit Layout

4. Click on Add Gadgets to open the Dashboard Workspace window:

5. By default, users can drag-and-drop gadgets in the dashboard page. All the available gadgets in the Dashboard Workspace are categorized under the Gadgets category. However, if you want to expose a new gadget in this panel, remember to assign the Gadgets category to it.

Chapter 2

55

6. Drag-and-drop your preferred applications on the page:

How it works...GateIn provides a dashboard for any user registered in the portal. Each user can decide how to manage private pages using the same tools provided for administrators, but this time all the contents are related to the specific user dashboard, that is, the root node page of personalized contents.

As you can see, users can create pages only under the main dashboard page, without storing pages under any other parent page.

Notice that all the applications that you want to use in dashboards must be categorized under the Gadgets category, otherwise no gadgets will be shown in the Dashboard Workspace window.

There's more…If a user wants to create more pages in the dashboard, there are two ways to do this:

f Use the Page Creation Wizard: Click on Add New Page, leaving your pointer on the Dashboard Editor

f Quick way: Click on the plus icon on the right of the current tab of the dashboard page

Managing Portal Contents Using the GUI

56

The classic way allows you to explicitly set the Display Name, the Node Name, and the extended label name for i18n. The page layout allows you to directly customize the page using the Page Creation Wizard.

1. Leave your pointer on Dashboard Editor.

2. Click on Add New Page.

3. Set your preferred values for the new dashboard page.

4. Click on the Next button to select the page layout.

5. Select the Dashboard Layout on the right panel.

6. Click on the Next button to use the Page Editor.

7. Here you can again change the page properties and add applications.

8. Click on the disk icon in the Page Editor to save your changes.

The quick way allows you to immediately create a new dashboard page by only setting the Display Name and using the default values for all the other properties:

1. Click on the plus icon on the right of the current dashboard page.

2. Set the Display Name value in the new tab and press Enter.

Chapter 2

57

In this case, the page will have the following default values:

f Node Name with a suffix automatically generated

f Dashboard Layout

f no gadgets applied

For more information about gadgets, please see Chapter 9, Managing Gadgets.

See also f The Managing portal pages recipe

f The Managing registered portlets recipe

3Managing Portal

Contents Using XML

In this chapter we will cover:

f Managing portals using XML

f Managing portal pages using XML

f Managing registered portlets using XML

f Managing the navigation tree using XML

f Wrapping it all up in a separate EAR

IntroductionIn this chapter, we will reprise all the concepts introduced in the previous chapter by trying to understand how to manage some of the portal contents using the extension mechanism provided by GateIn.

The main goal of this XML engine is to create contents on the first bootstrap of the portal instance without using the GUI. The GUI is a nice tool for prototyping the portal and after this initial elaboration phase, for industrial-size portals, the XML configuration is a reliable way to code and deploy a portal from testing to a production environment.

We will therefore start by discussing how to initialize portals and pages in the XML format, and we will then describe how to inject registered applications in to pages. Finally, you will learn how to create a unique WAR/EAR Java EE deployment component for encapsulating all your customization items.

Managing Portal Contents Using XML

60

As you will learn in this chapter, the entire portal configuration will be essentially based on three different configuration sections: portal, pages, and navigation:

f The portal section will be responsible for information about the layout of and applications available for the portal

f The pages section will describe all the details of each page in the portal, including layout and applications

f The navigation section will configure the navigation tree of the portal

This means that before starting your own portal application, you will need to configure each of these sections by following all the recipes in this chapter.

Note that to build your own portal using XML, you must follow all the recipes in this chapter without missing any configuration files. Otherwise, you may have some issues during the deployment of your custom portal. This is because different configuration files are explained in different recipes.This chapter will show you the different configuration sections. Whenever you need to restore a new configuration of the portal, if you are using the default portal bundle, you willl have to delete the data folder of GateIn. Otherwise, if you are using any other DBMS, you need to remove all the tables that are referred to for the content storage previously configured for the portal.

Managing portals using XMLIn this recipe, we will see how to set a specific site as the default portal available for the entire portal instance. We will set the Financials site as the default portal of the GateIn instance.

Getting readyLocate the portal configuration file at the following path in your application server, assuming that we are using JBoss AS:

/gatein.ear/02portal.war/WEB-INF/conf/portal/portal-configuration.xml

Otherwise, if you are using the Tomcat distribution, you should have the same file starting from the portal web application:

/GateIn-3.2.0.Final-tomcat6/webapps/portal/WEB-INF/conf/portal/portal-configuration.xml

Chapter 3

61

How to do it...1. Locate the snippet related to the UserPortalConfigService:

<component> <key>org.exoplatform.portal.config.UserPortalConfigService</key> <type>org.exoplatform.portal.config.UserPortalConfigService</type>…</component>

Downloading the example codeYou can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

2. You can change the default.portal value and page.templates.location that are highlighted here:<component> <key>org.exoplatform.portal.config.UserPortalConfigService</key> <type>org.exoplatform.portal.config.UserPortalConfigService</type> <component-plugins> <component-plugin> <name>new.portal.config.user.listener</name> <set-method>initListener</set-method> <type>org.exoplatform.portal.config.NewPortalConfigListener</type> <description>this listener init the portal configuration</description> <init-params> <value-param> <name>default.portal</name> <description>The default portal for checking db is empty or not</description> <value>financials</value> </value-param> <value-param> <name>page.templates.location</name> <description>the path to the location that contains Page templates</description> <value>war:/conf/portal/template/pages</value> </value-param>

Managing Portal Contents Using XML

62

3. You can also override the default templates for any page type (portal, user, and group): <object-param> <name>site.templates.location</name> <description>description</description> <object type="org.exoplatform.portal.config.SiteConfigTemplates"> <field name="location"> <string>war:/conf/portal</string> </field> <field name="portalTemplates"> <collection type="java.util.HashSet"> <value> <string>basic</string> </value> <value> <string>classic</string> </value> </collection> </field> <field name="groupTemplates"> <collection type="java.util.HashSet"> <value> <string>group</string> </value> </collection> </field> <field name="userTemplates"> <collection type="java.util.HashSet"> <value> <string>user</string> </value> </collection> </field> </object> </object-param>

4. Be sure to have completed all the configuration files explained in this chapter. Some sections that are needed are described later.

5. Save the file and restart the application server and try to access to http://localhost:8080/portal. You should be redirected to the Financials portal at the following URL: http://localhost:8080/portal/financials

Chapter 3

63

How it works…The extension mechanism provided by GateIn allows overriding the default portal configuration with your own settings. Each portal can be configured in many ways and it is possible to configure each aspect of a portal instance. For example, in the preceding snippet, we have seen the following settings:

f The default portal is available from the homepage

f The location of all the default page templates

All the other options that we can override will be described later in the chapter.

Notice that we have assumed that you have created the Financials portal before starting this recipe.

There's more...If you need to create a new site in the same portal instance, you have to declare the default portal layout in a specific XML navigation file. By taking a look at the examples in the GateIn source code, you will see how to set all parameters.

We are going to create the Financials portal instance by defining the following portal page layout:

1. Create a new folder financials in the following path: 02portal.war/WEB-INF/conf/portal/portal

2. Create a new portal.xml file for defining the new portal layout and enter the following:<portal-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_2 http://www.gatein.org/xml/ns/gatein_objects_1_2" xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_2"> <portal-name>financials</portal-name> <label>Financials</label> <description>Financials portal</description> <locale>en</locale> <access-permissions>Everyone</access-permissions> <edit-permission>*:/platform/administrators</edit-permission> <properties> <entry key="sessionAlive">onDemand</entry> <entry key="showPortletInfo">0</entry> </properties> <portal-layout>

Managing Portal Contents Using XML

64

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

<portlet-application> <portlet> <application-ref>web</application-ref> <portlet-ref>NavigationPortlet</portlet-ref> <preferences> <preference> <name>useAJAX</name> <value>false</value> <read-only>false</read-only> </preference> </preferences> </portlet> <access-permissions>Everyone</access-permissions> <show-info-bar>false</show-info-bar> </portlet-application> <portlet-application> <portlet> <application-ref>web</application-ref> <portlet-ref>BreadcumbsPortlet</portlet-ref> <preferences> <preference> <name>useAJAX</name> <value>false</value> <read-only>false</read-only> </preference> </preferences>

Chapter 3

65

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

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

Notice that the first part of the snippet contains all the parameters that you learned in Chapter 2, Managing Portal Contents using the GUI, the following are related to the creation of a new portal:

f label: This is the portal label

f description: A description for the portal instance

f access-permission: The group that can access the portal

f edit-permission: The group that can edit the portal

f sessionAlive: This shows how to manage the user session

f showPortletInfo: Manages the default behavior information bar

f page-body: This is the center section of the body for the portal page

Managing Portal Contents Using XML

66

Each XML element declared as portlet-application is used to define a specific block for the default layout of the portal page. In this configuration snippet, as you can see, you need to set the following XML sub-elements:

f application-ref: This is the portlet category

f portlet-ref: This is the name of the portlet declaration

f preferences: Sets the portlet preferences

f access-permissions: Sets permissions to access the portlet

f show-info-bar: Manages the information bar

See also f The Managing portals recipe in Chapter 2, Managing Portal Contents Using the GUI

f The Managing portal pages using XML recipe

Managing portal pages using XMLAfter creating a new portal site and the default portal layout, in this section we can continue to use the XML configuration to define all the pages for a new portal.

Getting readyCreate a new configuration file named pages.xml in the following path of the portal:

02portal.war/WEB-INF/conf/portal/portal/financials/page.xml

How to do it...For each page you want to define for the portal, you need to enter a page XML element as follows:

<page> <name>contactus</name> <title>Contact us</title>

<access-permissions>Everyone</access-permissions> <edit-permission>*:/platform/administrators</edit-permission> <portlet-application>

. . . </portlet-application> <gadget-application>

Chapter 3

67

. . . </gadget-application>

<page-body></page-body> </page>

How it works...The configuration engine of GateIn allows for the declaring new pages and also for the defining of all the applications (portlets or gadgets) that must be included inside of them.

The XML element dedicated to define new pages is the page element; it allows declaring the following properties for the page:

f name: This is the internal name of the page node

f title: This is the title of the page

f access-permission: Defines the policy for accessing the page

f edit-permission: Defines who can edit this page

f portlet-application: Declares a portlet to embed in the page

f gadget-application: Declares a gadget to embed in the page

f page-body: Declares where GateIn will render the current page

f site-body: Declares where GateIn will render the current page for the site

f container: Helps to manage an associative element for multiple sub-elements (portlet, gadget, page-body, site-body, or again container)

In order to take a quick look at some of the default components of the portal, you can also use the export tool of GateIn in Chapter 11, Managing Portal Resources with the Management Component. In this way, you can see how to change a specific setting of a portal by trying to import your changes at runtime without restarting your portal instance.

See also f Chapter 11, Managing Portal Resources with the Management Component

f The Managing portals using XML recipe

f The Managing registered portlets in Chapter 2, Managing Portal Contents Using the GUI

f The Managing the navigation tree in Chapter 2, Managing Portal Contents Using the GUI

Managing Portal Contents Using XML

68

Managing registered portlets using XMLThis recipe explains how to attach applications to a page using the XML configuration of GateIn.

Getting readyLet's assume that you have defined the page as the previous recipe in the same file:

02portal.war/WEB-INF/conf/portal/portal/financials/page.xml

How to do it...In order to set specific portlets in a page, you need to declare a portlet-application element for each portlet instance you want to use:

<page> <name>contactus</name> <title>Contact us</title> <access-permissions>Everyone</access-permissions> <edit-permission>*:/platform/administrators</edit-permission> <portlet-application> <portlet> <application-ref>web</application-ref> <portlet-ref>ContactUsPortlet</portlet-ref> <preferences> <preference> <name>useAJAX</name> <value>false</value> <read-only>false</read-only> </preference> </preferences> </portlet> <title>Contact us</title> <access-permissions>Everyone</access-permissions> <show-info-bar>false</show-info-bar> </portlet-application> </page>

In this XML definition, we have added a new portlet that contains the ContactUsPortlet previously added in the Application Registry, as we have seen in Chapter 2, Managing Portal Contents Using the GUI. We have assumed that this portlet exists in the Application Registry.

Chapter 3

69

How it works...A portlet can be declared using a portlet-application element in a page definition; this means that you have to provide the following sub-elements inside the snippet:

f application-ref: Shows the portlet category as stored in the Application Registry

f portlet-ref: Shows the portlet reference as stored in the Application Registry

f preferences: Properties for the current portlet instance

In GateIn, you will find the following default values for the application-ref element:

f web: This is the Web category

f exoadmin: This is the Administration category

f dashboard: This is the Dashboard category

Notice that you can use your own categories, previously created using the Application Registry.

However, this is not the only way to deploy portlets in GateIn, as a standard portlet-container, such as any portlet that would be deployed in WAR, with its WEB-INF/portlet.xml file, can be used in a portal page. The application-ref is then set with the name of the WebApp of the WAR. The portlet-ref is set with the name of the portlet declared in the portlet.xml file.

There's more...You have so far learned how to attach portlet instances in pages, but if you want to use gadgets, you need to use a different XML element named gadget-application.

This is an example of how to declare a gadget for a page:

<gadget-application> <gadget> <gadget-ref>ServicesManagement</gadget-ref> </gadget> <title>Services Management</title> <access-permissions>manager:/platform/administrators</access-permissions> <show-info-bar>false</show-info-bar> </gadget-application>

As you can see in the preceding definition, we already know some of the elements that were introduced for the portlet declaration: title, access-permission, and show-info-bar.

The unique new element introduced here is gadget-ref; this allows defining the identifier of the gadget declared in the Application Registry.

Managing Portal Contents Using XML

70

Defining the categories configuration for a portalIf you want to add or change the default settings for application categories related to a specific portal, you can take a look at the following file to understand how to override or define the current XML components:

/02portal.war/WEB-INF/conf/portal/application-registry-configuration.xml

Let's look at the default configuration of GateIn and look at how to use these initialization scripts for your custom portal:

<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd http://www.exoplaform.org/xml/ns/kernel_1_2.xsd" xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd">. . .<component> <key>org.exoplatform.application.registry.ApplicationRegistryService</key> <type>org.exoplatform.application.registry.impl.ApplicationRegistryServiceImpl</type> <component-plugins> <component-plugin> <name>new.portal.portlets.registry</name> <set-method>initListener</set-method> <type>org.exoplatform.application.registry.ApplicationCategoriesPlugins</type> <description>this listener init the portlets are registered in PortletRegister</description> <init-params> <object-param> <name>administration</name> <description>description</description> <object type="org.exoplatform.application.registry.ApplicationCategory"> <field name="name"> <string>Administration</string> </field> <field name="displayName"> <string>Administration</string> </field> <field name="description"> <string>application for administration</string> </field> <field name="accessPermissions">

Chapter 3

71

<collection type="java.util.ArrayList" item-type="java.lang.String"> <value> <string>*:/platform/administrators</string> </value> <value> <string>*:/organization/management/executive-board</string> </value> </collection> </field>. . .</configuration>

The preceding snippet shows how GateIn declares the creation of the default Administration category and sets the access permissions and all the applications that must be included.

To define a new category we therefore need to provide:

f The ApplicationRegistry class that we want to use; if you want, you can use the default one provided by GateIn. To identify a component in the configuration engine, you have to set the key and the type element.

f A component plugin declared as ApplicationCategoriesPlugin because it is the responsible class for creating categories during the first execution of the portal instance.

f An init-params element that contains for each category a specific object-param element.

f Definitions for each category with the following elements:

� name: This is the node name for the category

� displayName: The label of the category

� description: This is the description for the current category

� accessPermission: The permissions for accessing applications defined for this category

� applications: The list of all the applications (portlets or gadgets) that we want to initialize under this category

Managing Portal Contents Using XML

72

An application must be declared in a category definition in the following way:

<field name="applications"><collection type="java.util.ArrayList"><value> <object type="org.exoplatform.application.registry.Application"> <field name="applicationName"> <string>ApplicationRegistryPortlet</string> </field> <field name="categoryName"> <string>administration</string> </field> <field name="displayName"> <string>Application Registry</string> </field> <field name="description"> <string>Application Registry</string> </field> <field name="type"> <string>portlet</string> </field> <field name="contentId"> <string>exoadmin/ApplicationRegistryPortlet</string> </field> <field name="accessPermissions"> <collection type="java.util.ArrayList" item-type="java.lang.String"> <value> <string>*:/platform/administrators</string> </value> <value> <string>*:/organization/management/executive-board</string> </value> </collection> </field> </object></value>

Chapter 3

73

Finally, let's look at all the needed elements to initialize an application under a category:

f applicationName: The name of the application instance as registered in the Application Registry

f categoryName: The name of the category

f displayName: The default label to use for the current application

f description: The description for the application

f type: The type of the implementation; the value could be portlet or gadget

f contentId: The identifier of the application definition in the portal instance

f accessPermissions: The permissions to access the current application

See also f The Managing the navigation tree using XML recipe

f The Managing portal pages using XML recipe

f The Managing portals using XML recipe

Managing the navigation tree using XMLIn this recipe, we will describe how you can change the default structure of the navigation tree to initialize your own portal for your specific requirements.

Getting readyThree different configuration files manage the configuration for the navigation of a portal, so let's start discussing about these files:

f portal.xml: Contains the default layout and the standard applications for a portal

f pages.xml: Contains the pages layout and the standard applications for pages

f navigation.xml: Contains all the pages of the tree

You already know the first two files, as we have already discussed them in the previous recipes: portal.xml is covered in the Managing portals using XML recipe, while pages.xml is explained in the Managing portal pages using XML recipe.

Locate the navigation.xml file inside this path:

/02portal.war/WEB-INF/conf/portal/portal/classic

Again, we want to understand how GateIn was developed so that we can extend it for our needs.

Managing Portal Contents Using XML

74

How to do it...Taking a look at the navigation.xml file, we can see that only need to define, for each page that we want to show in the portal instance, a specific navigation node. For instance, to define the Contact Us page visible in the Financials portal, we have to enter the following snippet:

<node-navigation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_2 http://www.gatein.org/xml/ns/gatein_objects_1_2" xmlns="http://www.gatein.org/xml/ns/gatein_objects_1_2"> <priority>1</priority> <page-nodes> <node> <name>contactus</name> <label xml:lang="en">Contact us</label>

. . . <page-reference>portal::financials::contactus</page-reference>

</node> <node>

How it works...The navigation must be declared as a unique root element node-navigation that must conform to the following XML namespace:

http://www.gatein.org/xml/ns/gatein_objects_1_2

Inside the root element, you need to define a container element named page-nodes that will include the page nodes. The fundamental sub-elements to show a page in the portal are defined in the page node element (node):

f name: The internal name of the page node

f label: This is the label of the page (multi-value)

f page-reference: This is the page reference

The label element can be used to set a label for the page for each supported locale. This is done by using the xml:lang attribute.

Chapter 3

75

The page-reference must be used to set values according to the following convention:

portal::portal-instance-name::page-name

In order to take a quick look at some default components of the portal, you can also use the export tool of GateIn as shown in Chapter 11, Managing Portal Resources with the Management Component. This way, you can see how to change a specific setting of a portal by trying to import your changes at runtime without restarting your portal instance.

See also f Chapter 11, Managing Portal Resources with the Management Component

f The Managing portals using XML recipe

f The Managing portal pages using XML recipe

f The Wrapping it all up in a separate EAR recipe

Wrapping it all up in a separate EARWe will now look at how to create a separated portal application basing the entire configuration on the extension mechanism described in the previous recipes.

First, we will take a look at the example portal project provided in the GateIn source code, and we will start copying this example project to customize it for our needs.

Getting ready f Download the source code of GateIn following the instructions described in the recipe

Building and installing GateIn from the source code from Chapter 1, Getting Started

f Configure your developing environment following the recipe Setting up the development environment from Chapter 1, Getting Started

f Locate the Maven project dedicated to the portal example at the following path: <GATEIN_SOURCE_ROOT>/examples/portal

Managing Portal Contents Using XML

76

How to do it...Carry out the following steps in order to include all your components in a separate EAR:

1. We are going to copy the portal folder and use that copy to create the new Maven module project that we want to customize. Supposing that we are using Eclipse IDE, select the portal folder:

2. Right-click and select Copy.

3. Select the examples folder.

4. Right-click and click on Paste.

Chapter 3

77

5. Type financials-portal for the new folder and click on the OK button.

6. Locate the configuration file for the portal container: /financials-portal/config/src/main/java/conf/configuration.xml

7. Change the configuration for the portal plugins in the following way:<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>

Managing Portal Contents Using XML

78

<!-- 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>financials-portal</name> <object type="org.exoplatform.container.definition.PortalContainerDefinition"> <!-- The name of the portal container --> <field name="name"><string>financials-portal</string></field> <!-- The name of the context name of the rest web application --> <field name="restContextName"><string>rest-financials-portal</string></field> <!-- The name of the realm --> <field name="realmName"><string>gatein-domain-financials-portal</string></field> <field name="externalSettingsPath"> <string>configuration.properties</string> </field> <!-- All the dependencies of the portal container ordered by loading priority --> <field name="dependencies"> <collection type="java.util.ArrayList"> <value> <string>eXoResources</string> </value> <value> <string>portal</string> </value> <value> <string>dashboard</string> </value> <value> <string>exoadmin</string> </value> <value> <string>eXoGadgets</string> </value>

Chapter 3

79

<value> <string>gwtGadgets</string> </value> <value> <string>eXoGadgetServer</string> </value> <value> <string>rest-financials-portal</string> </value> <value> <string>web</string> </value> <!--<value profiles="jboss"> <string>gatein-wsrp-integration</string> </value>--> <value> <string>financials-portal</string> </value> </collection> </field> </object> </object-param> </init-params> </component-plugin> </external-component-plugins></configuration>

8. Update pom.xml in the config module with the following snippet:<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"> <parent> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.parent</artifactId> <version>3.2.0-GA</version> <relativePath>../../../pom.xml</relativePath> </parent>

<modelVersion>4.0.0</modelVersion> <artifactId>exo.portal.financials.portal.config</artifactId> <name>Financials Portal Configuration</name> <packaging>jar</packaging> <description>Financials Portal Configuration</description></project>

Managing Portal Contents Using XML

80

9. Locate the EAR configuration file: /financials-portal/ear/src/main/application/META-INF/application.xml

10. Change the enterprise application configuration in the following way:<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN" "http://java.sun.com/dtd/application_1_3.dtd"><application> <display-name>financials-portal</display-name> <description>Financials Portal Ear</description>

<module> <java>exo.portal.financials.portal.config-3.2.0-GA.jar</java>

</module> <module> <web>

<web-uri>financials-portal.war</web-uri> <context-root>financials-portal</context-root> </web> </module> <module> <web> <web-uri>rest-financials-portal.war</web-uri> <context-root>rest-financials-portal</context-root> </web> </module> <module> <java>exo.portal.financials.portal.jar-3.2.0-GA.jar</java> </module></application>

11. Update the pom.xml for the EAR module to conform to the new setting:<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"> <parent> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.parent</artifactId> <version>3.2.0-GA</version> <relativePath>../../../pom.xml</relativePath> </parent>

<modelVersion>4.0.0</modelVersion> <artifactId>gatein-financials-portal</artifactId>

Chapter 3

81

<packaging>ear</packaging> <name>Financials Portal Ear</name> <description>Financials Portal Ear</description>

<dependencies> <dependency> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.financials.portal.config</artifactId> <version>3.2.0-GA</version><version>3.2.0-GA</version> </dependency> <dependency> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.financials.portal.jar</artifactId> <version>3.2.0-GA</version> </dependency> <dependency> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.component.web.api</artifactId> <version>3.2.0-GA</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.financials.portal.war</artifactId> <version>3.2.0-GA</version> <type>war</type> </dependency> <dependency> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.financials.portal.rest-war</artifactId> <version>3.2.0-GA</version> <type>war</type> </dependency> </dependencies> <build> <finalName>${project.artifactId}</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-ear-plugin</artifactId> <configuration> <displayName>financials-portal</displayName> <modules>

Managing Portal Contents Using XML

82

<jarModule> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.financials.portal.config</artifactId> <includeInApplicationXml>true</includeInApplicationXml> </jarModule> <webModule> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.financials.portal.war</artifactId> <bundleFileName>financials-portal.war</bundleFileName> <contextRoot>financials-portal</contextRoot> </webModule> <webModule> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.financials.portal.rest-war</artifactId> <bundleFileName>rest-financials-portal.war</bundleFileName> <contextRoot>rest-financials-portal</contextRoot> </webModule> <jarModule> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.financials.portal.jar</artifactId> <includeInApplicationXml>true</includeInApplicationXml> </jarModule> </modules> </configuration> </plugin> </plugins> </build></project>

12. Update the realm configuration for the Financials portal in the file financials-portal/ear/src/main/application/META-INF/gatein-jboss-beans.xml as follows:<deployment xmlns="urn:jboss:bean-deployer:2.0">

<application-policy xmlns="urn:jboss:security-beans:1.0" name="gatein-domain-financials-portal"> <authentication>

Chapter 3

83

<login-module code="org.gatein.wci.security.WCILoginModule" flag="optional"> <module-option name="portalContainerName">financials-portal</module-option> <module-option name="realmName">gatein-domain-financials-portal</module-option> </login-module> <login-module code="org.exoplatform.web.security.PortalLoginModule" flag="required"> <module-option name="portalContainerName">financials-portal</module-option> <module-option name="realmName">gatein-domain-financials-portal</module-option> </login-module> <login-module code="org.exoplatform.services.security.jaas.SharedStateLoginModule" flag="required"> <module-option name="portalContainerName">financials-portal</module-option> <module-option name="realmName">gatein-domain-financials-portal</module-option> </login-module> <login-module code="org.exoplatform.services.security.j2ee.JbossLoginModule" flag="required"> <module-option name="portalContainerName">financials-portal</module-option> <module-option name="realmName">gatein-domain-financials-portal</module-option> </login-module> </authentication> </application-policy> </deployment>

13. Override the default portal configuration in the file financials-portal/war/src/main/webapp/WEB-INF/conf/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"> <import>war:/conf/financials-portal/common/common-configuration.xml</import> <import>war:/conf/financials-portal/jcr/jcr-configuration.xml</import> <import>war:/conf/financials-portal/portal/portal-configuration.xml</import>

Managing Portal Contents Using XML

84

<import>war:/conf/financials-portal/web/web-inf-extension-configuration.xml</import></configuration>

14. Set the specific realm in the JBoss configuration file financials-portal/rest-war/src/main/webapp/WEB-INF/jboss-web.xml:<jboss-web> <security-domain>java:/jaas/gatein-domain-financials-portal</security-domain></jboss-web>

15. Change the JCR configuration file for the new portal instance in the file financials-portal/war/src/main/webapp/WEB-INF/conf/financials-portal/jcr/jcr-configuration.xml in the following way:<external-component-plugins> <!-- The full qualified name of the RepositoryServiceConfiguration --> <target-component>org.exoplatform.services.jcr.config.RepositoryServiceConfiguration</target-component> <component-plugin> <!-- The name of the plugin --> <name>Sample RepositoryServiceConfiguration Plugin</name> <!-- The name of the method to call on the RepositoryServiceConfiguration in order to add the RepositoryServiceConfigurations --> <set-method>addConfig</set-method> <!-- The full qualified name of the RepositoryServiceConfigurationPlugin --> <type>org.exoplatform.services.jcr.impl.config.RepositoryServiceConfigurationPlugin</type> <init-params> <value-param> <name>conf-path</name> <description>JCR configuration file</description> <value>war:/conf/financials-portal/jcr/repository-configuration.xml</value> </value-param> </init-params> </component-plugin> </external-component-plugins>

16. Change the workspace JCR configuration (content store and index store) in the file /financials-portal/war/src/main/webapp/WEB-INF/conf/financials-portal/jcr/repository-configuration.xml as follows:<repository-service default-repository="repository"> <repositories>

Chapter 3

85

<repository name="repository" system-workspace="${gatein.jcr.workspace.system:system}" default-workspace="${gatein.jcr.workspace.default:portal-system}">. . .<workspace name="financials-ws"> <container class="org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.CQJDBCWorkspaceDataContainer"> <properties> <property name="source-name" value="${gatein.jcr.datasource.name}${container.name.suffix}" /> <property name="dialect" value="auto" /> <property name="multi-db" value="false" /> <property name="update-storage" value="true" /> <property name="max-buffer-size" value="204800" /> <property name="swap-directory" value="${gatein.jcr.data.dir}/swap/financials-ws${container.name.suffix}" /> </properties> <value-storages> <value-storage id="financials-ws" class="org.exoplatform.services.jcr.impl.storage.value.fs.TreeFileValueStorage"> <properties> <property name="path" value="${gatein.jcr.storage.data.dir}/financials-ws${container.name.suffix}" /> </properties> <filters> <filter property-type="Binary" /> </filters> </value-storage> </value-storages> </container>. . .<query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex"> <properties> <property name="index-dir" value="${gatein.jcr.index.data.dir}/financials-ws${container.name.suffix}"/>. . . </repository> </repositories></repository-service>

Managing Portal Contents Using XML

86

17. Update the latter configuration files starting from the root portal configuration file financials-portal/war/src/main/webapp/WEB-INF/conf/financials-portal/portal/portal-configuration.xml as you learnt in the previous recipes following your specific needs for portal, pages, layout, and applications.

18. Then, in the case where you have extended the portal application, add in the Maven POMs provided with this chapter and any new dependencies for your custom features and your own Java extensions.

19. Finally, in order to build the new portal application, launch the following Maven commands: clean and then install. Notice that these commands can be executed using Eclipse or from a terminal command prompt. If you are using Eclipse you can follow these steps:

� Right-click on the financials-portal/pom.xml

� Select Run As | Maven clean and wait for a BUILD SUCCESS output

� Select Run As | Maven install and wait for a BUILD SUCCESS output as follows:

[INFO] Reactor Summary:[INFO] [INFO] Financials Portal Configuration ................... SUCCESS [7.919s][INFO] Financials Portal Java Classes .................... SUCCESS [9.990s][INFO] Financials Portal War ............................. SUCCESS [1.850s][INFO] Financials Portal Rest War ........................ SUCCESS [0.828s][INFO] Financials Portal Ear ............................. SUCCESS [0.990s][INFO] Financials Portal ................................. SUCCESS [0.111s][INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time: 22.644s[INFO] Finished at: Sun Apr 22 17:40:39 CEST 2012[INFO] Final Memory: 23M/81M

20. If you are not using Eclipse, you can execute this command to build from the root of the project: mvn clean install

Chapter 3

87

21. Now you should find the EAR package in the following path: examples/financials-portal/ear/target

22. Start the JBoss AS instance.

23. Be sure to deploy the starter application provided by the GateIn source code (starter.ear, built from the Maven module starter). This is the web application provided with GateIn dedicated to enabling the extension mechanism of the portal.

24. Deploy the new portal application (gatein.ear) in JBoss by copying the artifact in the deploy folder.

25. Access the new portal application by visiting the following URL from your browser: http://localhost:8080/financials-portal

Note that the portal container is now the financials-portal, and the URL path starts with this name. Note also that the regular portal named portal is still running in the portal container and can be called with http://localhost:8080/portal.

Notice that if you want to build and deploy your custom portal WAR artifact, for example in Tomcat, you have to follow the instructions included in the README.txt file that can be downloaded from the Packt website (http://www.packtpub.com/support). All source code and code bundles are available for download from the above page. Select the book and the code bundles can then be downloaded.

Managing Portal Contents Using XML

88

How it works...The example portal project provided by the GateIn source code consists of the following Maven modules:

f config: Includes the portal configurations about the portlet container

f jar: Includes Java code for your custom logic and filters

f rest-war: Contains the configuration for all the REST API of the portal

f war: Contains all the portal configuration files and builds the WAR artifact

f ear: Builds the EAR package that includes all the previous module artifacts

As you have seen, for each of these modules, we need to configure correctly every portal component, starting from the storage and then the configurations of the specific portal pages, applications, and layouts.

Remember to adjust any references in POM files. This allows you to add references to the right Maven modules or to add any new library that you want to use in the Java classpath.

For a complete overview of all the settings that you need to update from an example portal project, please see the source code relating to this chapter. You can download it from http://www.packtpub.com/support.

In this recipe, we introduced some configuration files about security concepts (realms) that will be discussed in detail in Chapter 5, Securing Portal Contents.

See also f The Managing portals using XML recipe

f The Managing portal pages using XML recipe

f The Managing the navigation tree using XML recipe

f The Managing registered portlets using XML recipe

4Managing Portal Users

In this chapter, we will cover:

f Managing users

f Managing groups

f Assigning users to groups

f Integrating with an existing LDAP store

f Setting an automatic membership after user creation

f Adding a custom field in the user profile

f Integrating with Web SSO

f Integrating with SPNEGO for Desktop SSO

IntroductionIn this chapter, we will consider how to manage authorities for the portal.

An authority in the security model can be an object instance related to a single user or a group. An authority can have different types of permissions in the portal context aimed at providing security for accessing specific contents.

We will see how to apply permissions on portal contents for any authority type.

We will also look at how to configure the portal for integrating users from an external store, and how to then configure GateIn to integrate it with a Single Sign On (SSO) system.

Managing Portal Users

90

Single Sign On (SSO) is a feature of some software systems that allows managing user authentication (and sessions) in a centralized fashion. An SSO system could be based on different authentication mechanisms that will be used to conform all the external systems to a unique supported authentication binding. In this way, users will be able to authenticate on the central system avoiding re-authenticating whenever they need to use any of the systems involved in the same federated infrastructure. The goal of this mechanism is to reduce the number of times that users enter their passwords, in order to avoid phishing and increase security.

Managing usersIn this recipe, we will see how you can create, update, and remove users from the portal instance using the GUI.

Getting readyLog in to the portal using an administrator account.

How to do it...We will see how to update or remove any existing user stored in the portal. For example, let's search for John using the following steps:

1. Click on Group | Organization | Users and groups management to access the user search feature, as is shown in the following screenshot:

2. Type john in the Search field and click on the search icon.

3. Click on Edit User Info under the Action column.

Chapter 4

91

4. Now you can modify all the properties for the selected user. These are divided in to three different tabs:

� Account Info

� User Profile

� Membership

5. The Account Info tab allows you to update relevant login information that is stored to be used by the portal

6. The User Profile tab contains some optional properties relating to the user, such as Given Name, Birthday, Job Title, and so on

7. There is also other information available for a single user, such as Home Info and Business Info. You can set or change these properties relating to how you contact the user, such as the portal address, e-mail address, the website address, and so on

8. The User Membership tab allows for the managing of the permissions for the current user and the removing of some of the existing membership settings, as shown in the following screenshot:

How it works…GateIn assigns a dedicated section of a portlet for administering users and memberships using a friendly web interface. This administration tool allows for the easy creation and management of users' information by providing standard forms and panels.

Managing Portal Users

92

All the details about a user can be shown or changed by using the following tabs:

f Account Info: Contains the account information

f User Profile: Contains basic information about the user

f Home Info: Contains contact information

f Business Info: Contains contact information for business

f User Membership: This tab allows for the management of user roles

Each user must be correctly configured to access or change a specific section of the portal and that is why it is necessary to specify memberships inside the User Membership tab for each user.

There's more...Now let's see some other ways to manipulate user account details inside the administration tools.

Creating usersIt is also possible to create a new user account, as follows:

1. Click on Group | Organization | New Staff.

2. Fill in all the required fields for the new user inside the Account Info tab.

3. Optionally, you can provide other details about the user in the User Profile tab, adding the same information previously described in this recipe. The three available panels are Profile, Home Info, and Business Info.

4. Click on Save to submit the creation of the new user or click on Reset to restore the default empty values.

Removing usersIf you need to remove an existing user, you need to perform the following steps:

1. Click on Group | Organization | Users and groups management.

2. Type the username value of the user that you want to remove in the Search field and click on the search icon.

3. Once the result is returned, click on the trash icon in the Action column.

4. Click on OK to confirm the delete action.

Chapter 4

93

Removing membershipsIn order to remove a membership from a user, you have to follow these steps:

1. Click on Group | Organization | Users and groups management.

2. Type the username of the user in the Search field and click on the search icon.

3. Click on Edit User Info under the Action column.

4. Click on User Membership.

5. Click on the trash icon of the membership to be removed.

6. Finally, click on OK on the confirmation alert.

See also f The Managing groups recipe

f The Assigning users to groups recipe

Managing groupsIn the previous recipe, we saw how we can add, remove, update, and search users in the portal instance. Now we would like to see how we can do the same operations for groups.

Notice that a group can be nested in another group, allowing for the creation of sub-groups, but in the security model of the portal, there is no inheritance for permissions.

We will create new groups dedicated to the Financials portal:

f Customer Care

f Customers

f Bank Operators

Getting readyLog in to the portal using an administrator account.

Managing Portal Users

94

How to do it...In order to create a new Financials group, carry out the following steps:

1. Click on Group | Organization | Users and groups management.

2. Click on Group Management to arrive at the following administration tool:

3. In the Groups panel on the left, click on the plus icon.

4. Create the root group by filling in the form with these properties:

� Group Name: financials

� Label: Financials

� Description: This is the root group for the Financials portal

5. Click on Save to create the group.

6. Click again on the plus icon in the Groups panel on the left.

7. Create the Customer Care group by filling the form with these properties:

� Group Name: customerCare

� Label: Customer Care

� Description: This is the child group dedicated to all the users that work on the Customer Care department

8. Click on Save.

Chapter 4

95

9. Repeat the same procedure, from steps 6 to 8, for the Customers and the Bank Operators group.

10. The final result of the Financials group structure should be similar to the following screenshot:

Notice that the Group Name must be filled with a unique string, without using separated words, that is, without any blank spaces used as a separator. You can use letters, underscore, and numbers. This property is used internally by the portal to identify the information about a specific group that is stored in a repository node. This property cannot be changed because it is the internal ID of the group.

How it works…GateIn provides the Group Management section inside one of the administration portlets as shown in the preceding screenshot.

Using this section, you can easily manage groups using a web interface that allows you to create groups and sub groups simply by clicking on the group that you want to edit, and then add a new sub group or change it for updating the existing properties and so on.

When you are adding a new sub group for a parent group, the groups panel will show you the tree structure related to the hierarchy of all the involved groups.

Managing Portal Users

96

There's more...Here we will see all the other options available for manipulating groups in the portal.

Removing groupsIn order to remove an existing group, you have to follow these steps:

1. Click on Group | Organization | Users and groups management.

2. Click on Group Management.

3. Select an existing group that you want to remove from the Groups panel.

4. Click on the trash icon.

5. Finally, click on OK in the confirmation alert.

Updating groupsLet's see how to change properties for an existing group if you need to update some information about it:

1. Click on Group | Organization | Users and groups management.

2. Click on Group Management.

3. Select an existing group that you want to update from the Groups panel.

4. Click on the note icon for accessing the Edit Current Group form that allows you to update the same properties that we have previously seen in this recipe:

� Group Name: This is the name of the repository node dedicated to the group described in the previous recipe

� Label: This is the label for the group

� Description: This is the field for adding a meaningful description for the current group

5. Click on Save for submitting changes.

See also f The Managing users recipe

f The Assigning users to groups recipe

Chapter 4

97

Assigning users to groupsWe have so far discussed users and groups separately, so let's start working on assigning users to existing groups available in the portal.

It's very important in a user domain to create a group model that can be used not only for managing users, but also for associating permissions in an easy way.

It's good practice to create a group model for associating permissions to groups. In this way, the group is also used as an authority. This means that our work for the permission model becomes very simple for managing permissions.

Sometimes it is not possible to create a unique group model due to specific requirements that you may have for certain domains. For instance, you could retrieve information about groups from an existing LDAP or another user storage that was based on a different permissions mechanism compared to the one provided in GateIn.

Finally, you could have a different tree structure on a specific LDAP1 and another one on a potential LDAP2. If you are in this situation it could be useful to create a new LDAP storage based on a merge of the existing LDAP1 and LDAP2. In this way you can create a new and unique tree for managing the portal permissions.

We will see how to manage members in some groups related to the Financials portal.

Getting readyLog in to the portal with the root user.

How to do it...In order to add a new user as a member of the Customer Care group, follow these steps:

1. Click on Group | Organization | Users and groups management.

2. Click on Group Management.

3. Select the Financials group from the Groups panel.

4. Expand the Financials group, if it is not already expanded.

5. Select the Customer Care group.

6. As you have seen before in this chapter, on the right you have the following two panels:

� Group Info: Shows all the member of the current group

� Add member: Allows you to add new members to the group

Managing Portal Users

98

7. We want to add John in the Customer Care group with a Manager role. Therefore, in the Add member panel, type john in the Username field.

8. Select Manager in the Membership list.

9. Click on Save.

How it works…GateIn is based on a set of services for managing all the components dedicated for storing information. The Organization Service is one of the services of the GateIn API and it is hidden by some administration portlets using a friendly user interface as shown in the previous recipe.

The association engine behind the Organization Service is managed by a web interface provided by GateIn for adding and removing members from groups.

This operation is needed to allow any user to be enabled to read or manipulate pages inside the portal. This because each operation inside the portal is allowed only for specific groups that are defined in the security model.

There's more...If you want to remove a membership from a group, you have to follow these steps:

1. Click on Group | Organization | Users and groups management.

2. Click on Group Management.

3. Select the group that contains the membership you want to remove.

4. Identify the specific membership in the Group Info panel.

5. Click on the trash icon.

6. Click on OK in the confirmation alert to finally remove the membership from the selected group.

Notice that this is an alternative way to remove a membership of a user; we have discussed this in a previous recipe in this chapter.

Configuring the Organization ServiceThe Organization Service is the core for the identity management in GateIn. Thanks to this service we can manage users, roles, and memberships. The service manages its internal logic through an identity manager that we can choose in its configuration file.

Chapter 4

99

Each portal application in GateIn must have its own configuration file for the Organization Service. The organization is the handling of users, groups, memberships, and profiles. In this folder (WEB-INF/conf/organization) we can decide for example where to take the users, or how to store the user passwords. We can also plug in custom business logic against the creation of users, groups, and memberships.

The configuration is usually declared in the <PORTAL_WAR_ROOT>/WEB-INF/conf/configuration.xml file. The default configuration provided in GateIn is the following code:

…..<import>war:/conf/organization/idm-configuration.xml</import>…….<import>war:/conf/organization/organization-configuration.xml</import>

The first row represents the type of the organization. By default, GateIn uses the JBoss IDM manager called PicketLink.

The second row is a set of generic configurations and we always suggest taking a look at it in the Organization Framework.

The Organization Framework is the main framework used to configure the Organization Service. For more details about it, please see: http://docs.jboss.com/gatein/portal/3.2.0.Final/reference-guide/en-US/html/chap-Reference_Guide-Authentication_And_Identity.html

Therefore, it is enough to choose the correct file to decide what product or technology the organization framework must use. Here we list all available configurations, where each configuration file is named by a suffix –configuration.xml:

f IDM

f Active Directory

f Hibernate

f InstallCS

f JDBC

f LDAP

The following recipes will show some custom operations in the organization making use of the configuration files.

Managing Portal Users

100

See also f The Managing users recipe

f The Managing groups recipe

Integrating with an existing LDAP storeTypically, while working on enterprise-class architectures, you have to integrate applications with a specific user store provider. LDAP is the widely adopted standard dedicated to manage users and groups in large environments.

We will discuss how we can configure an external LDAP server with GateIn to authenticate and get information about users and groups directly from an LDAP server.

Getting readyLocate the configuration.xml file in the following path in your application server deployments folder:

portal/WEB-INF/conf/configuration.xml

How to do it...Let's now see which steps are required to configure your LDAP with GateIn:

1. Uncomment the section related to the LDAP configuration in the configuration.xml file and change the file location to the correct path conf/organization/exo that should be included by default in the portal application, as shown in the following snippet:...

<!--for organization service used active directory which is user lookup server --> <!-- <import>war:/conf/organization/exoactivedirectory-configuration.xml</import> --> <!--for organization service used ldap server which is user lookup server --> <import>war:/conf/organization/exo/ldap-configuration.xml</import>

Chapter 4

101

<!-- <import>war:/conf/security-configuration.xml</import> -->

<import>war:/conf/organization/organization-configuration.xml</import> <import>war:/conf/jcr/component-plugins-configuration.xml</import> <import>war:/conf/mail/portal-mail-configuration.xml</import>

<!-- Portal related services --> <import>war:/conf/portal/portal-configuration.xml</import> <import>war:/conf/portal/application-registry-configuration.xml</import> <import>war:/conf/portal/controller-configuration.xml</import> <import>war:/conf/portal/web-configuration.xml</import> <import>war:/conf/portal/gadget-configuration.xml</import></configuration>

2. Locate the LDAP configuration file in the following path:

portal/WEB-INF/conf/organization/exo/ldap-configuration.xml.

3. First, we will identify the LDAPConnectionConfig component to set the endpoint properties for your LDAP server:<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd http://www.exoplaform.org/xml/ns/kernel_1_2.xsd" xmlns="http://www.exoplaform.org/xml/ns/kernel_1_2.xsd"> <component> <key>org.exoplatform.services.ldap.LDAPService</key> <type>org.exoplatform.services.ldap.impl.LDAPServiceImpl</type> <init-params> <object-param> <name>ldap.config</name> <description>Default ldap config</description> <object type="org.exoplatform.services.ldap.impl.LDAPConnectionConfig"> <!-- for multiple ldap servers, use comma seperated list of host:port (Ex. ldap://127.0.0.1:389,10.0.0.1:389) --> <field name="providerURL"><string>ldap://127.0.0.1:389,10.0.0.1:389</string></field>

Managing Portal Users

102

<field name="rootdn"><string>CN=Manager,DC=exoplatform,DC=org</string></field> <field name="password"><string>secret</string></field> <field name="version"><string>3</string></field> <field name="minConnection"><int>5</int></field> <field name="maxConnection"><int>10</int></field> <field name="referralMode"><string>follow</string></field> <!-- <field name="referralMode"><string>ignore</string></field> --> <field name="serverName"><string>default</string></field>

4. Set the following properties for configuring your LDAP server with GateIn:

� providerURL: The LDAP endpoint (ldap://<IP ADDRESS>:<PORT>), can be multi-value; you can add more servers using commas.

� rootdn: This is the principal account identified by the LDAP Distinguished Name (DN). This account will be used by GateIn for read-only access to the server to retrieve all the users' and groups' information.

� password: This is the password value for the principal account.

� minConnection: The minimum number of connections that GateIn must create against the LDAP server when it is running.

� maxConnection: The maximum number of connections that GateIn can create against the LDAP server when it is running.

� serverName: This defines the type of the LDAP server. GateIn supports the following LDAP servers: default, active-directory, open.ldap, netscape.directory, redhat.directory.

Chapter 4

103

5. Identify the OrganizationService, which is the second component in the configuration file that is dedicated to set all the LDAP attributes against the UserProfile attributes provided in GateIn, and check that the configuration is compliant with your LDAP server:<component> <key>org.exoplatform.services.organization.OrganizationService</key> <type>org.exoplatform.services.organization.ldap.OrganizationServiceImpl</type> <component-plugins> <component-plugin> <name>init.service.listener</name> <set-method>addListenerPlugin</set-method> <type>org.exoplatform.services.organization.ldap.OrganizationLdapInitializer</type> <description>this listener populate organization ldap service create default dn</description> </component-plugin> </component-plugins> <init-params> <value-param> <name>ldap.userDN.key</name> <description>The key used to compose user DN</description> <value>cn</value> </value-param> <object-param> <name>ldap.attribute.mapping</name> <description>ldap attribute mapping</description> <object type="org.exoplatform.services.organization.ldap.LDAPAttributeMapping"> <field name="userLDAPClasses"><string>top,person,organizationalPerson,inetOrgPerson</string></field> <field name="profileLDAPClasses"><string>top,organizationalPerson</string></field> <field name="groupLDAPClasses"><string>top,organizationalUnit</string></field> <field name="membershipTypeLDAPClasses"><string>top,organizationalRole</string></field> <field name="membershipLDAPClasses"><string>top,groupOfNames</string></field> <field name="baseURL"><string>dc=exoplatform,dc=org</string></field>

Managing Portal Users

104

<field name="groupsURL"><string>ou=groups,ou=portal,dc=exoplatform,dc=org</string></field> <field name="membershipTypeURL"><string>ou=memberships,ou=portal,dc=exoplatform,dc=org</string></field> <field name="userURL"><string>ou=users,ou=portal,dc=exoplatform,dc=org</string></field> <field name="profileURL"><string>ou=profiles,ou=portal,dc=exoplatform,dc=org</string></field> <field name="userUsernameAttr"><string>uid</string></field> <field name="userPassword"><string>userPassword</string></field> <field name="userFirstNameAttr"><string>givenName</string></field> <field name="userLastNameAttr"><string>sn</string></field> <field name="userDisplayNameAttr"><string>displayName</string></field> <field name="userMailAttr"><string>mail</string></field> <field name="userObjectClassFilter"><string>objectClass=person</string></field> <field name="membershipTypeMemberValue"><string>member</string></field> <field name="membershipTypeRoleNameAttr"><string>cn</string></field> <field name="membershipTypeNameAttr"><string>cn</string></field> <field name="membershipTypeObjectClassFilter"><string>objectClass=organiz ationalRole</string></field> <field name="membershiptypeObjectClass"><string>organizationalRole</string></field> <field name="groupObjectClass"><string>organizationalUnit</string></field> <field name="groupObjectClassFilter"><string>objectClass=organizational Unit</string></field> <field name="membershipObjectClass"><string>groupOfNames</string></field> <field name="membershipObjectClassFilter"><string>objectClass=group OfNames</string></field>

Chapter 4

105

<field name="ldapCreatedTimeStampAttr"><string>createdTimeStamp</string></field> <field name="ldapModifiedTimeStampAttr"><string>modifiedTimeStamp</string></field> <field name="ldapDescriptionAttr"><string>description</string></field> </object> </object-param> </init-params> </component>

6. The parameters that you can configure are as follows:

� userLDAPClasses: Sets all the accepted LDAP classes for the users

� profileLDAPClasses: Sets all the accepted LDAP classes for profiles

� groupLDAPClasses: Sets all the accepted LDAP classes for groups

� membershipTypeLDAPClasses: Sets all the accepted LDAP classes for the membership types

� membershipLDAPClasses: Sets all the accepted LDAP classes for the memberships

� baseURL: The base root LDAP query

� groupsURL: The base LDAP query to get all the groups

� membershipTypeURL: The base LDAP query for getting memberships

� userURL: The base LDAP query for getting all the users

� profileURL: The base LDAP query to retrieve all the profiles information

� userUsernameAttr: The LDAP attribute for retrieving the username

� userPassword: The LDAP attribute used for authenticating users

� userFirstNameAttr: The LDAP attribute used for storing the first name of users

� userLastNameAttr: The LDAP attribute dedicated to the last name of users

� userDisplayNameAttr: The LDAP attribute used for the display name of users

� userMailAttr: The LDAP attribute for the e-mail address of the users

� userObjectClassFilter: The LDAP query for identifying the user object

� membershipTypeMemberValue: The attribute used by LDAP for assigning user to groups

� membershipTypeRoleNameAttr: This is the value used by LDAP to identify the association of a user with a group

Managing Portal Users

106

� membershipTypeNameAttr: The name attribute used by LDAP for the association of an user

� membershipTypeObjectClassFilter: The LDAP filter used to identify the type of membership

� membershipTypeObjectClass: The LDAP object class used for membership types

� groupObjectClass: The LDAP object class used to identify a group

� groupObjectClassFilter: The LDAP filter to identify groups

� membershipObjectClass: The LDAP object used for groups

� membershipObjectClassFilter: The LDAP filter for groups

� ldapCreatedTimeStampAttr: The LDAP attribute dedicated for the created timestamp

� ldapModifiedTimeStampAttr: The LDAP attribute for the modified timestamp

� ldapDescriptionAttr: The description attribute

7. Save the file.

8. Restart the portal.

9. Log in as an administrator.

10. Click on Group | Organization | Users and groups management.

11. Finally, click on Group Management to navigate all the groups previously defined in the LDAP server

How it works...The LDAP Service provided in GateIn is a sub service dedicated to identify and synchronize users' information. It is based on PicketLink IDM, which is the JBoss Identity Project. This module is used in the portal to manage users.

The advantage is that this allows the management of users, from an LDAP source without re-writing any specific lines of code.

Chapter 4

107

There's more...The latest improvements on GateIn now allow for the use of PicketLink as the identity manager of the portal.

PicketLink is the identity manager implementation of the JBoss Community. It is an open source project and it can be easily embedded in any standard J2EE application. It allows for the managing of user identities, storing the information in a database. It also supports many ways to negotiate credentials and Single Sign On (SSO) mechanisms.

GateIn provides different examples of how to use PicketLink inside the portal configuration in the following folder:

<PORTAL_WAR_ROOT>/WEB-INF/conf/organization/picketlink-idm/examples

You will find the following example files for PicketLink:

f picketlink-idm-ldap-config.xml: A generic LDAP example

f picketlink-idm-openldap-config.xml: An example of using OpenLDAP

f picketlink-idm-msad-config.xml: An example of using Active Directory

f picketlink-idm-msad-readonly-config.xml: An example of using read-only constraints against an Active Directory store

To set a specific configuration, you have to modify the file idm-configuration.xml that you find in the path <PORTAL_WAR_ROOT>/WEB-INF/conf/organization and choose one of the previous example files, updating the file in this way:

<component> <key>org.exoplatform.services.organization.idm.PicketLinkIDMService</key> <type>org.exoplatform.services.organization.idm.PicketLinkIDMServiceImpl</type> <init-params> <value-param> <name>config</name> <value>war:/conf/organization/picketlink-idm/picketlink-idm-ldap-config.xml</value>

Inside these configuration files you will set parameters that are very similar to the previous ones that you saw in this recipe. You will need to set the endpoint and all the necessary information for authenticating and selecting the correct tree in the store.

Managing Portal Users

108

PicketLink manages different identity stores for the portal domain dedicated to users. Taking a look at the OpenLDAP example, in order to configure a user domain using PicketLink, you need to configure:

f realms: Contains the definitions of the JAAS realms dedicated to authenticate users and for creating user sessions.

f repositories: A repository manages the user profile information with the related store. For each portal must be defined only one repository.

f stores: Defines all the identity objects required for the portal. GateIn supports these types of source for the store: JDBC, LDAP, and Hibernate.

By default, in the configuration file you will find two realms: idm_realm is defined for the standard portal and idm_realm_sample-portal is defined for the sample portal:

<realms> <realm> <id>idm_realm_sample-portal</id> <repository-id-ref>DefaultPortalRepository</repository-id-ref> <identity-type-mappings> <user-mapping>USER</user-mapping> </identity-type-mappings> <options> <option> <name>cache.providerRegistryName</name> <value>apiCacheProvider</value> </option> </options> </realm> <realm> <id>idm_realm</id> <repository-id-ref>PortalRepository</repository-id-ref> <identity-type-mappings> <user-mapping>USER</user-mapping> </identity-type-mappings> <options> <option> <name>template</name> <value>true</value> </option> <option> <name>cache.providerRegistryName</name> <value>apiCacheProvider</value> </option> </options> </realm> </realms>

Chapter 4

109

As you can see, for each realm a repository is defined. The repository contains all the contents of the portal and it will be used to cooperate with the involved identity stores for managing different roles and access permissions.

In this configuration file, the PortalRepository is the repository related to the default portal application provided by GateIn that we now want to configure. For this repository you will find a specific store named PortalLDAPStore dedicated to provide information from an LDAP server:

<repository> <id>PortalRepository</id> <class>org.picketlink.idm.impl.repository.FallbackIdentityStoreRepository</class> <external-config/> <default-identity-store-id>HibernateStore</default-identity-store-id> <default-attribute-store-id>HibernateStore</default-attribute-store-id> <identity-store-mappings> <identity-store-mapping> <identity-store-id>PortalLDAPStore</identity-store-id> <identity-object-types> <identity-object-type>USER</identity-object-type> <identity-object-type>platform_type</identity-object-type> <identity-object-type>organization_type</identity-object-type> </identity-object-types> <options/> </identity-store-mapping> </identity-store-mappings> <options> <option> <name>allowNotDefinedAttributes</name> <value>true</value> </option> </options> </repository>

Notice that for this repository we have three identity objects defined: USER, platform_type (membership), and organization_type (membership). PortalLDAPStore is the identity store preconfigured for LDAP servers; look at its definition:

<identity-store> <id>PortalLDAPStore</id> <class>org.picketlink.idm.impl.store.ldap.LDAPIdentityStoreImpl</class> <external-config/>

Managing Portal Users

110

<supported-relationship-types> <relationship-type>JBOSS_IDENTITY_MEMBERSHIP</relationship-type> </supported-relationship-types>

In order to configure the endpoint address for this LDAP store you need to change the following options defined for PortalLDAPStore with references relating to your LDAP server:

<options> <option> <name>providerURL</name> <value>ldap://localhost:1389</value> </option> <option> <name>adminDN</name> <value>cn=Manager,dc=my-domain,dc=com</value> </option> <option> <name>adminPassword</name> <value>secret</value> </option>

This means that you need to configure at least the parameters shown above:

f providerURL: This is the endpoint address of your LDAP server

f adminDN: This is the Distinguished Name (DN) to identify the principal user that will be used to read all the store

f adminPassword: This is the password value of the principal user

Each identity store can manage different identity objects; here you need to set some attributes for the USER object type to synchronize user profile information. By default, the USER object consists of these parameters: firstName, lastName, and email. In the configuration file you find the USER definition:

<identity-object-type> <name>USER</name> <relationships/> <credentials> <credential-type>PASSWORD</credential-type> </credentials> <attributes> <attribute> <name>firstName</name> <mapping>cn</mapping> <type>text</type> <isRequired>false</isRequired>

Chapter 4

111

<isMultivalued>false</isMultivalued> <isReadOnly>false</isReadOnly> </attribute> <attribute> <name>lastName</name> <mapping>sn</mapping> <type>text</type> <isRequired>false</isRequired> <isMultivalued>false</isMultivalued> <isReadOnly>false</isReadOnly> </attribute> <attribute> <name>email</name> <mapping>mail</mapping> <type>text</type> <isRequired>false</isRequired> <isMultivalued>false</isMultivalued> <isReadOnly>false</isReadOnly> <isUnique>true</isUnique> </attribute> </attributes>

Moreover, for each attribute of the object you need to configure the specific mapping between the portal attribute (the name element) and the LDAP attribute value (the mapping element).

For example, in the preceding snippet we found the firstName attribute defined by default, meaning that that the firstName field will be synchronized with the value from the cn attribute in the LDAP store. Change these attributes for your own configuration making sure that you check your LDAP attributes.

For each attribute, you need to define some XML elements to add constraints for a specific field:

f type: The base type for the field

f isRequired: A Boolean value to set this attribute as required or not

f isMultivalued: A Boolean value to set this attribute as multi-value or not

f isReadOnly: A Boolean value to set this attribute to read-only mode or not

f isUnique: A Boolean value to set this attribute as unique for the identity object or not (optional)

Managing Portal Users

112

The next step shows you how to provide the options for allowing GateIn to correctly identify users in the LDAP tree:

<options> <option> <name>idAttributeName</name> <value>uid</value> </option> <option> <name>passwordAttributeName</name> <value>userPassword</value> </option> <option> <name>ctxDNs</name> <value>ou=People,o=portal,o=gatein,dc=my-domain,dc=com</value> </option> <option> <name>allowCreateEntry</name> <value>true</value> </option> <option> <name>createEntryAttributeValues</name> <value>objectClass=top</value> <value>objectClass=inetOrgPerson</value> <value>sn= </value> <value>cn= </value> </option> </options> </identity-object-type>

By changing these settings, the portal will correctly find users in the LDAP store. Each user is identified by a uid and we need to correctly set attributes that contain values for authenticating users from the portal application against the store:

f idAttributeName: This is the name of the attribute in the store that contains the value for the user identifier

f passwordAttributeName: This is the name of the attribute in the store that contains the password value

f ctxDNs: This is the DN related to the LDAP tree to select all the users involved in the portal

f allowCreateEntry: This allows the management and configuration of the user creation, and specifies the objectClass defined in the LDAP store

Chapter 4

113

The second identity object is platform_type and it contains two relationships and has no attributes, and so the definition should be the following:

<identity-object-type> <name>platform_type</name> <relationships> <relationship> <relationship-type-ref>JBOSS_IDENTITY_MEMBERSHIP</relationship-type-ref> <identity-object-type-ref>USER</identity-object-type-ref> </relationship> <relationship> <relationship-type-ref>JBOSS_IDENTITY_MEMBERSHIP</relationship-type-ref> <identity-object-type-ref>platform_type</identity-object-type-ref> </relationship> </relationships> <credentials/> <attributes/>

This object contains information about users membership and, by default, the member attribute is used for selecting groups for users:

<options> <option> <name>idAttributeName</name> <value>cn</value> </option> <option> <name>ctxDNs</name> <value>ou=Platform,o=portal,o=gatein,dc=my-domain,dc=com</value> </option> <!--<option>--> <!--<name>entrySearchFilter</name>--> <!--<value></value>--> <!--</option>--> <option> <name>allowCreateEntry</name> <value>true</value> </option> <option> <name>parentMembershipAttributeName</name> <value>member</value>

Managing Portal Users

114

</option> <option> <name>parentMembershipAttributePlaceholder</name> <value>ou=placeholder,o=portal,o=gatein,dc=my-domain,dc=com</value> </option> <option> <name>isParentMembershipAttributeDN</name> <value>true</value> </option> <option> <name>allowEmptyMemberships</name> <value>true</value> </option> <option> <name>createEntryAttributeValues</name> <value>objectClass=top</value> <value>objectClass=groupOfNames</value> <value>member=ou=placeholder,o=portal,o=gatein,dc=my-domain,dc=com</value> </option>

Here, we have some new attributes that are used to read or write memberships between the portal and the LDAP store; some of them are:

f idAttributeName: This is the name of the attribute used as identifier for groups.

f ctxDNs: This is the DN for finding this group in the LDAP store tree.

f parentMembershipAttributeName: This is the name of the attribute that manages members for groups.

f parentMembershipAttributePlaceholder: This is the name of the attribute used as placeholder in the case of read/write operation against the LDAP store. It is required only if you want to use GateIn to manage users in LDAP.

f createEntryAttributeValues: This contains the necessary options that are dedicated to create new entries in the store. It is required only if you want to use GateIn to manage users in LDAP.

Chapter 4

115

For more details about all the parameters and configurations of PicketLink, please see http://www.jboss.org/picketlink.

In this way, GateIn will automatically synchronize user profiles to get information from your LDAP store. You can therefore now start your portal instance and check your user profile in GateIn.

See also f The Setting an automatic membership after user creation recipe

f The Integrating with Web SSO recipe

f The Integrating with SPNEGO for Desktop SSO recipe

Setting an automatic membership after user creation

In this recipe, we will show you how to set a default membership for each new created user.

Getting readyLocate the XML WebUI descriptor for the Organization Service at this path:

<PORTAL_WAR_ROOT>/WEB-INF/conf/organization/organization-configuration.xml

How to do it…1. Identify the configuration of the event for the creation of the user:

<component-plugin> <name>new.user.event.listener</name> <set-method>addListenerPlugin</set-method> <type>org.exoplatform.services.organization.impl.NewUserEventListener </type>...

Managing Portal Users

116

2. Identify, or add if not present, the field membership in the object NewUserConfig$JoinGroup and add the required values for the groupId and the membership as follows:<object type="org.exoplatform.services.organization.impl.NewUserConfig$JoinGroup"> <field name="groupId"> <string>/platform/users</string></field> <field name="membership"> <string>member</string></field></object>

3. Save and restart the portal instance.

4. Create a new user.

5. Finally, check the membership of the new created user.

How it works…All the events executed by the creation of the user are captured by the NewUserEventListener. This is done by an extension of the org.exoplatform.services.organization.UserEventListener class. In this configuration, the listener receives the new user according to the rules assigned to the NewUserConfig, in this case groupId and membership.

We can modify these attributes and add the name of a special user that doesn't use the default membership type. Here is an example of this specific case:

<field name="ignoreUser"> <string>newadmin</string></field>

See also f The Adding a custom field in the user profile recipe

f The Integrating with Web SSO recipe

f The Integrating with SPNEGO for Desktop SSO recipe

Chapter 4

117

Adding a custom field in the user profileWe want to add a new field for the users and show it through the user profile console.

Getting readyIt is enough to access the groovy folder inside the portal configuration in the root application folder of Gatein. Here is the complete path for the groovy folder, if you are using JBoss:

JBOSS_CONF/deploy/gatein.ear/02portal.war/groovy

Otherwise, if you are using Tomcat, you will find it at:

webapps/portal/groovy

How to do it…1. Locate the OrganizationPortlet that adds the profiles fields to the users.

2. Add the following code in the first rows of the script file /groovy/ webui/form/UIVTabInputSet.gtmpl:if (uicomponent.findComponentById("Nameofthenewprofileblock") == null) { org.exoplatform.webui.form.UIFormInputSet personalInputSet = new org.exoplatform.webui.form.UIFormInputSet("Nameofthenewprofileblock "); def String[] fields = ["user.name.mynewfieldname"]; uicomponent.addInput(personalInputSet, campi); uicomponent.addUIFormInput(personalInputSet); }

3. Finally, you need to add your custom field for the localization in the resource bundle gatein.ear/exoadmin.war/WEB-INF/classes/locale/portlet/exoadmin/AccountPortlet_$$.properties:UIUserInfo.label.user.mynewfield=My New Field Name:

The parameter $$ will be the locale value for the related resource bundle that is selected for the user session (the default value is en).

Managing Portal Users

118

How it works…This file represents the page for the creation of the users. The new field will be automatically inserted and it will pass to the organization framework configured in the organization-configuration.xml. The following is the final form page returned by the framework:

See also f The Integrating with Web SSO recipe

f The Integrating with SPNEGO for Desktop SSO recipe

Integrating with Web SSOIn this section, we will discuss how to configure GateIn to provide Single Sign On (SSO). One possible way of configuring SSO with GateIn is to use an OpenSSO server.

OpenSSO is an SSO framework that can be configured to provide an authentication mechanism with different applications sharing realm configurations.

For more information about OpenSSO, please visit the following URL:http://www.oracle.com/technetwork/middleware/id-mgmt/overview/index.html.

Chapter 4

119

Getting ready1. Download and install the OpenSSO server as described on the official website.

2. Download the latest GateIn SSO support package from the following Maven repository location:https://repository.jboss.org/nexus/content/groups/public/org/gatein/sso/sso-packaging

3. The latest version available at the time of writing the book is 1.1.1-GA, it is downloadable from this URL:https://repository.jboss.org/nexus/content/groups/public/org/gatein/sso/sso-packaging/1.1.1-GA/sso-packaging-1.1.1-GA.zip

4. Extract the package in a folder in your filesystem.

How to do it...First, we will start configuring all the artifacts relating to the classpath.

1. Copy the files from GATEIN_SSO_HOME/opensso to OPENSSO_HOME, adding all the needed files in the application server of GateIn.

2. If you are using OpenSSO in the same machine of GateIn, edit the file of the HTTP connector for the application server and change the connector port of OpenSSO to 8888, change the AJP port from 8009 to 8809.

3. Edit the file OPENSSO_HOME/webapps/opensso/config/auth/default/ AuthenticationPlugin.xml in the following way:<?xml version='1.0' encoding="UTF-8"?><!DOCTYPE ModuleProperties PUBLIC "=//iPlanet//Authentication Module Properties XMLInterface 1.0 DTD//EN""jar://com/sun/identity/authentication/Auth_Module_Properties.dtd"><ModuleProperties moduleName="AuthenticationPlugin" version="1.0" ><Callbacks length="2" order="1" timeout="60"header="GateIn OpenSSO Login" ><NameCallback><Prompt>Username</Prompt></NameCallback><PasswordCallback echoPassword="false" ><Prompt>

Managing Portal Users

120

Password</Prompt></PasswordCallback></Callbacks></ModuleProperties>

4. If you are using JBoss, uncomment the following snippet inside the file gatein.ear/META-INF/gatein-jboss-beans.xml:<authentication><login-module code="org.gatein.sso.agent.login.SSOLoginModule" flag="required"><module-option name="portalContainerName">portal</module-option><module-option name="realmName">gatein-domain</module-option></login-module><login-module code="org.exoplatform.services.security.j2ee.JbossLoginModule"flag="required"><module-option name="portalContainerName">portal</module-option><module-option name="realmName">gatein-domain</module-option></login-module></authentication>

5. For Tomcat, update the file TOMCAT_HOME/conf/jaas.conf and uncomment this snippet:org.gatein.sso.agent.login.SSOLoginModule required;org.exoplatform.services.security.j2ee.TomcatLoginModule requiredportalContainerName=portalrealmName=gatein-domain;

6. Add a new valve in the Tomcat configuration inside the file TOMCAT_HOME/webapps/portal.war/META-INF/context.xml:. . .<Context path='/portal' docBase='portal' ... >

<Valve className='org.gatein.sso.agent.tomcat.ServletAccessValve' />. . .</Context>. . .

Finally, we can configure the realm for the authentication process inside the OpenSSO server:

1. Start the OpenSSO server.

2. Point your browser to this URL: http://localhost:8888/opensso.

3. Create a standard configuration.

Chapter 4

121

4. Access the OpenSSO server as an administrator and click on Configuration | Authentication | Core.

5. Add a new value with the following class name: org.gatein.sso.opensso.plugin.AuthenticationPlugin

6. Click on Access control and create a new realm gatein.

7. Click on the new gatein realm and click on Authentication.

8. In the Authentication Chaining section, click on ldapService.

9. Change the value from Datastore to AuthenticationPlugin enabling the GateIn REST services for authenticating users.

10. Click on Advanced Properties and set Dynamic for the UserProfile, in this way all the user profiles will be created after a successful authentication from the GateIn side.

11. Click on Access control | Top Level Realm | Privileges | All authenticated users and check these two settings:

� Read and write access only for policy properties

� Read and write access to all realm and policy properties

With these configuration steps done, the GateIn portal is configured to run with SSO executed by OpenSSO.

How it works…GateIn provides a support package dedicated to SSO integrations. In this way, all the required components and configuration are separated by the product and are used only if needed because it depends on your specific requirements.

During the first section of the configuration steps, we enabled the AuthenticationPlugin on OpenSSO and then we also configured JAAS for the application server. These are the typical steps to federate a realm against an authentication provider using Java.

We then configured OpenSSO using the administration console for exchanging user sessions with GateIn. Finally, we added the AuthenticationPlugin provided by GateIn and we used it for configuring the new dedicated realm.

See also f The Integrating with SPNEGO for Desktop SSO recipe

Managing Portal Users

122

Integrating with SPNEGO for Desktop SSOAnother possible scenario for integrating GateIn using SSO in several infrastructures is related to the use of the SPNEGO mechanism. We will see how to configure GateIn for this type of integration.

SPNEGO stands for Simple and Protected GSSAPI Negotiation Mechanism and it provides an automatic way to check if the operating system or the application server supports NT LAN Manager (NTLM) or Kerberos to authenticate user sessions.

For more information about SPNEGO, please see the following URL:http://en.wikipedia.org/wiki/SPNEGO

Getting ready1. Download the latest GateIn SSO support package from the following Maven

repository location:https://repository.jboss.org/nexus/content/groups/public/org/gatein/sso/sso-packaging

2. The latest version available at the time of writing the book is 1.1.1-GA; it is downloadable from this URL:https://repository.jboss.org/nexus/content/groups/public/org/gatein/sso/sso-packaging/1.1.1-GA/sso-packaging-1.1.1-GA.zip

3. Extract the package in a folder in your filesystem.

4. Download the latest GA version of jboss-negotiation JAR (at the time of writing the book, it is 2.1.0.GA) from the following URL: https://repository.jboss.org/nexus/content/groups/public/org/jboss/security/jboss-negotiation/

5. We assume that you have correctly configured an SPNEGO server on your environment.

How to do it...Let's start to configure GateIn with SPNEGO as follows:

1. Locate the login-config.xml file in this path: JBOSS_HOME/server/default/conf

2. Add the following snippet:<!-- SPNEGO domain --><application-policy name="host"><authentication>

Chapter 4

123

<login-module code="com.sun.security.auth.module.Krb5LoginModule" flag="required"><module-option name="storeKey">true</module-option><module-option name="useKeyTab">true</module-option><module-option name="principal">HTTP/[email protected]</module-option><module-option name="keyTab">/etc/krb5.keytab</module-option><module-option name="doNotPrompt">true</module-option><module-option name="debug">true</module-option></login-module></authentication></application-policy>

3. The keyTab parameter must be generated by the kadmin tool. If you are using Linux, this parameter must have the same value that you set in the kdc.conf.

4. Locate the file JBOSS_HOME/server/default/deployers/jbossweb.deployer/META-INF/wardeployers-jboss-beans.xml and change it to the following:<deployment xmlns="urn:jboss:bean-deployer:2.0"><property name="authenticators"><map class="java.util.Properties" keyClass="java.lang.String"valueClass="java.lang.String"><entry><key>BASIC</key><value>org.apache.catalina.authenticator.BasicAuthenticator</value></entry><entry><key>CLIENT-CERT</key><value>org.apache.catalina.authenticator.SSLAuthenticator</value></entry><entry><key>DIGEST</key><value>org.apache.catalina.authenticator.DigestAuthenticator</value></entry><entry><key>FORM</key><value>org.apache.catalina.authenticator.FormAuthenticator</value></entry><entry><key>NONE</key>

Managing Portal Users

124

<value>org.apache.catalina.authenticator.NonLoginAuthenticator</value></entry><!-- Add this entry --><entry><key>SPNEGO</key><value>org.gatein.sso.spnego.GateInNegotiationAuthenticator</value></entry></map></property>

5. Copy sso-agent-VERSION.jar: from GATEIN_SSO_HOME/spnego/gatein.ear/lib to JBOSS_HOME/server/default/deploy/gatein.ear/lib.

6. Copy spnego-VERSION.jar:from GATEIN_SSO_HOME/spnego/gatein.ear/lib to JBOSS_HOME/server/default/lib.

7. Copy the jboss-negotiation-VERSION.jar file in JBOSS_HOME/server/default/lib.

8. Change the file gatein-jboss-beans.xml that is included in the path JBOSS_HOME/server/defaut/deploy/gatein.ear/META-INF as follows:<deployment xmlns="urn:jboss:bean-deployer:2.0"><application-policy xmlns="urn:jboss:security-beans:1.0" name="gatein-form-authdomain"><authentication><login-module code="org.gatein.wci.security.WCILoginModule" flag="optional"><module-option name="portalContainerName">portal</module-option><module-option name="realmName">gatein-domain</module-option></login-module><login-module code="org.exoplatform.services.security.jaas.SharedStateLoginModule"flag="required"><module-option name="portalContainerName">portal</module-option><module-option name="realmName">gatein-domain</module-option></login-module><!-- Uncomment this part to check on each login if user is member of "/platform/users"group and if notcreate such membership --><!--<login-moduleflag="required">

Chapter 4

125

<module-option name="portalContainerName">portal</module-option><module-option name="realmName">gatein-domain</module-option><module-option name="membershipType">member</module-option><module-option name="groupId">/platform/users</module-option></login-module>--><login-module code="org.exoplatform.services.security.j2ee.JbossLoginModule"flag="required"><module-option name="portalContainerName">portal</module-option><!-- logout needs to be performed from 'gatein-domain' as it is used for JaasSecurityManager.--><module-option name="realmName">gatein-domain</module-option></login-module></authentication></application-policy><application-policy xmlns="urn:jboss:security-beans:1.0" name="gatein-domain"><authentication><login-modulecode="org.gatein.sso.spnego.SPNEGOLoginModule"flag="requisite"><module-option name="password-stacking">useFirstPass</module-option><module-option name="serverSecurityDomain">host</module-option><module-option name="removeRealmFromPrincipal">true</module-option><module-option name="usernamePasswordDomain">gatein-form-auth-domain</moduleoption></login-module><login-modulecode="org.gatein.sso.agent.login.SPNEGORolesModule"flag="required"><module-option name="password-stacking">useFirstPass</module-option><module-option name="portalContainerName">portal</module-option><module-option name="realmName">gatein-domain</module-option></login-module></authentication></application-policy></deployment>

Managing Portal Users

126

9. Change the GateIn portal application web.xml as follows:<!-- <login-config><auth-method>FORM</auth-method><realm-name>gatein-domain</realm-name><form-login-config><form-login-page>/initiatelogin</form-login-page><form-error-page>/errorlogin</form-error-page></form-login-config></login-config>--><login-config><auth-method>SPNEGO</auth-method><realm-name>SPNEGO</realm-name><form-login-config><form-login-page>/initiatelogin</form-login-page><form-error-page>/errorlogin</form-error-page></form-login-config></login-config>

10. Add the SPNEGO filters in the web.xml:<filter><filter-name>LoginRedirectFilter</filter-name><filter-class>org.gatein.sso.agent.filter.LoginRedirectFilter</filter-class><init-param><!-- This should point to your SSO authentication server --><param-name>LOGIN_URL</param-name><param-value>/portal/private/classic</param-value></init-param></filter><filter><filter-name>SPNEGOFilter</filter-name><filter-class>org.gatein.sso.agent.filter.SPNEGOFilter</filter-class></filter><filter-mapping><filter-name>LoginRedirectFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><filter-mapping><filter-name>SPNEGOFilter</filter-name><url-pattern>/login</url-pattern></filter-mapping>

Chapter 4

127

11. Update the Sign in link modifying the template JBOSS_HOME/server/default/deploy/gatein.ear/web.war/groovy/groovy/webui/component/UIBannerPortlet.gtml as follows:<!--<a class="Login" onclick="$signInAction"><%=_ctx.appRes("UILoginForm.label.Signin")%></a>--><a class="Login" href="/portal/sso"><%=_ctx.appRes("UILoginForm.label.Signin")%></a>

12. Now you have completed all the configuration settings.

13. Finally, remember to start GateIn using the following command with security settings:sudo ./run.sh -Djava.security.krb5.realm=LOCAL.NETWORK -

Djava.security.krb5.kdc=server.local.network -c default -b server.local.network

14. Set the correct server.local.network value for your Kerberos configuration,

How it works…In the first part of the configuration steps, we configured the application server to add the support to the new realm with the Kerberos keystore for the domain defined for the SPNEGO server.

We then changed the SSL configuration for the application server to support the security layer for SPNEGO.

In the next step, we added specific libraries taken from the GateIn SSO support package to use custom login modules and the SPNEGO agent.

We then changed the portal configuration for adding the authentication support for the new domain defined in SPNEGO. We also added the specific authentication filter to receive the needed requests against the new authentication process.

Finally, in the last steps we modified the HTML code for the login form to update the context path related to the link for the SSO mechanism that we have now enabled.

See also f The Integrating with Web SSO recipe

5Securing Portal

Contents

In this chapter we will cover:

f Securing portals

f Securing with JBoss AS

f Securing with Tomcat

f Choosing the JAAS modules

f Creating a login page

f Synchronizing users

f Securing pages

f Securing categories

f Securing applications

f Securing portlets

IntroductionThis chapter discusses the configurations aimed at providing security features to portals and all the related components. We will see that we can work using either the web console or the XML configuration files. As you would expect, the latter is more flexible in most instances.

Many of the configuration snippets shown in the chapter are based on Enterprise Deployment Descriptors (DD). Keep in mind that XML always remains the best option for configuring a product. We will configure GateIn in different ways to show how to adapt some of the internal components for your needs.

Securing Portal Contents

130

Enterprise Deployment Descriptors (DD) are configuration files related to an enterprise application component that must be deployed in an application server. The goal of the deployment descriptor is to define how a component must be deployed in the container, configuring the state of the application and its internal components.These configuration files were introduced in the Java Enterprise Platform to manage the deployment of Java Enterprise components such as Web Applications, Enterprise Java Beans, Web Services, and so on. Typically, for each specific container, you have a different definition of the descriptor depending on vendors and standard specifications.

Typically, a portal consists of pages related to a public section and a private section. Depending on the purpose, of course, we can also work with a completely private portal.

The two main mechanisms used in any user-based application are the following:

f Authentication

f Authorization

We talked about authentication in the previous chapter and now we will discuss authorization: how to configure and manage permissions for all the objects involved in the portal. As an example, a User is a member of a Group, which provides him with some authorizations. These authorizations are the things that members of the Groups can do in the portal.

On the other side, as an example, a page is defined with some permissions, which says which Groups can access it. Now, we are going to see how to configure and manage these permissions, for the pages, components in a page, and so on in the portal.

Securing portalsThe authorization model of the portal is based on the association between the following actors: groups, memberships, users, and any content inside the portal (pages, categories, or portlets).

In this recipe, we will assign the admin role against a set of pages under a specific URL of the portal. This configuration can be found in the default portal provided with GateIn so you can take the complete code from there.

Chapter 5

131

Getting readyLocate the web.xml file inside your portal application.

How to do it…We need to configure the web.xml file assigning the admin role to the following pages under the URL http://localhost:8080/portal/admin/* in the following way:

<security-constraint> <web-resource-collection> <web-resource-name> admin authentication </web-resource-name> <url-pattern>/admin/*</url-pattern> <http-method>POST</http-method> <http-method>GET</http-method> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>NONE</transport-guarantee> </user-data-constraint></security-constraint>

The role must be declared in a different section under the security-constraint tag through the security-role tag. The role-name tag defines the id of the role:

<security-role> <description>the admin role</description> <role-name>admin</role-name></security-role>

How it works…GateIn allows you to add different roles for every sections of the portal simply by adding a path expression that can include a set of sub-pages using wildcard notation (/*).

This is done by first defining all the needed roles using the security-role element, and then defining a security-constraint element for each set of pages that you want to involve.

This role definition in GateIn is the group seen in the previous chapter. PicketLink is also for users and memberships, and can manage the organization of the groups.

Securing Portal Contents

132

There's more...

Configuring GateIn with JAASGateIn uses JAAS (Java Authentication Authorization Service) as the security model.

JAAS (Java Authentication Authorization Service) is the most common framework used in the Java world to manage authentication and authorization. The goal of this framework is to separate the responsibility of users' permissions from the Java application. In this way, you can have a bridge for permissions management between your application and the security provider. For more information about JAAS, please see the following URL:http://docs.oracle.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html

Java EE Application servers and JSP/servlet containers, such as JBoss and Tomcat, also support JAAS with specific deployment descriptors.

The default JAAS module implemented in GateIn synchronizes the users and roles from the database. In order to add your portal to a specific realm, add the following snippet in web.xml:

<login-config>. . . <realm-name>gatein-domain</realm-name>. . .</login-config>

Notice that a realm can be managed by JAAS or another authorization framework—it is not important which is used for the Java Enterprise Edition.

gatein-domain is the ID of the default GateIn domain that we will use as the default reference for the following recipes.

See also f The Securing with JBoss AS recipe

f The Securing with Tomcat recipe

Chapter 5

133

Securing with JBoss ASIn this recipe, we will configure GateIn with JAAS using JBoss AS (5.x and 6.x).

Getting readyLocate the WEB-INF folder inside your portal application.

How to do it…Create a new file named jboss-web.xml in the WEB-INF folder with the following content:

<jboss-web> <security-domain> java:/jaas/gatein-domain </security-domain></jboss-web>

How it works…This is the JNDI URL where the JAAS module will be referenced. This URL will automatically search the JAAS modules called gatein-domain.

The configuration of the modules can be found inside the file gatein-jboss-beans.xml. Usually, this file is inside the deployed <PORTAL_WAR_ROOT>/META-INF, but it could be placed anywhere inside the deploy directory of JBoss, thanks to the auto-discovery feature provided by the JBoss AS.

Here is an example:

<deployment xmlns="urn:jboss:bean-deployer:2.0">

<application-policy xmlns="urn:jboss:security-beans:1.0" name="gatein-domain"> <authentication> <login-module code= "org.gatein.wci.security.WCILoginModule" flag="optional"> <module-option name="portalContainerName"> portal </module-option> <module-option name="realmName"> gatein-domain

Securing Portal Contents

134

</module-option> </login-module> <login-module code= "org.exoplatform.web.security.PortalLoginModule" flag="required">……….. </application-policy>

</deployment>

JAAS allows adding several login modules, which will be executed in cascade mode according to the flag attribute. The following represents a description of the valid values for the flag attribute and their respective semantics as mentioned in the Java standard API:

f Required: The LoginModule is required to succeed. If it succeeds or fails, authentication still continues to proceed to the next LoginModule in the list.

f Requisite: The LoginModule is required to succeed. If it succeeds, authentication continues on the next LoginModule in the list. If it fails, the control immediately returns to the application and the authentication process does not proceed to the next LoginModule.

f Sufficient: The LoginModule is not required to succeed. If it does succeed, the control immediately returns to the application and the authentication process does not proceed to the next LoginModule. If it fails, authentication continues forward to the next LoginModule.

f Optional: The LoginModule is not required to succeed. If it succeeds or fails, authentication still continues to proceed to the next LoginModule.

Look at the recipe Choosing the JAAS modules for details about each login module.

See also f The Securing portals recipe

f The Securing with Tomcat recipe

f The Choosing the JAAS modules recipe

Securing with TomcatIn this recipe, we will configure a JAAS realm using Tomcat 6.x.x/7.x.x.

Getting readyLocate the declaration of the realm inside <PORTAL_WAR_ROOT>/META-INF/context.xml.

Chapter 5

135

How to do it…1. Change the default configuration for your needs, as described in the previous recipe.

The default configuration is the following:<Context path='/portal' docBase='portal' debug='0' reloadable='true' crossContext='true' privileged='true'> <Realm className= 'org.apache.catalina.realm.JAASRealm' appName='gatein-domain' userClassNames= 'org.exoplatform.services.security.jaas.UserPrincipal' roleClassNames= 'org.exoplatform.services.security.jaas.RolePrincipal' debug='0' cache='false'/> <Valve className='org.apache.catalina.authenticator.FormAuthenticator' characterEncoding='UTF-8'/></Context>;

2. Change the default configuration of the JAAS domain that is defined in the TOMCAT_HOME/conf/jaas.conf file. Here is the default configuration:<gatein-domain { org.gatein.wci.security.WCILoginModule optional;

org.exoplatform.services.security.jaas.SharedStateLoginModule required;

org.exoplatform.services.security.j2ee.TomcatLoginModule required;

};

How it works…As we have seen in the previous recipe, we can configure the modules in Tomcat using a different configuration file. This means that we can change and add login modules that are related to a specific JAAS realm.

The context.xml file is stored inside the web application. If you don't want to modify this file, you can add a new file called portal.xml in the conf folder to override the current configuration.

Securing Portal Contents

136

See also f The Security with JBoss AS recipe

f The Choosing the JAAS modules recipe

Choosing the JAAS modulesIn this recipe, we will see the available JAAS modules provided by GateIn and how to customize them. They are based on a simple approach, so if you are in a particular environment, you may need to create a new module or extend one of them, which is easy to do.

Getting readyWe are going to choose one of the available modules for JAAS:

f Locate the login modules file at the following path: gatein.ear/META-INF/gatein-jboss-beans.xml

f Locate the JAAS configuration file in your application server. See the previous two recipes Securing with JBoss AS and Securing with Tomcat for the locations of the JAAS configuration files

How to do it…Depending on your security requirements, you can choose the login modules required for your architecture.

How it works…The complete list of all the JAAS modules provided by GateIn is as follows:

f org.gatein.wci.security.WCILoginModule: A standard wrapper module used between Tomcat, JBoss, and Jetty. It's the default first module.

f org.exoplatform.web.security.PortalLoginModule: Dedicated to refresh the current authentication session. It can be used on cluster environment.

f org.exoplatform.services.security.jaas.SharedStateLoginModule: Keeps user credentials in the cache to avoid reauthentication requests when the session is closed.

f org.exoplatform.services.security.jaas.DefaulLoginModule: This is a simple generic login module compliant with all application servers.

f org.exoplatform.services.security.jaas.IdentitySetLoginModule: Stores the identity for the logged user in the default registry.

Chapter 5

137

f org.exoplatform.services.security.j2ee.JbossLoginModule: Adapts to the login and logout operations on JBoss. It always must be used as last module in the JBoss authentication chain.

f org.exoplatform.services.security.j2ee.JettyLoginModule: This is the base module for Jetty.

f org.exoplatform.services.security.j2ee.TomcatLoginModule: This is the base module for Tomcat.

f org.exoplatform.services.security.j2ee.DigestAuthenticationJbossLoginModule: The JBoss module for digest authentication.

f org.exoplatform.services.security.j2ee.DigestAuthenticationJettyLoginModule: The Jetty module for digest authentication.

f org.exoplatform.services.security.j2ee.DigestAuthenticationTomcatLoginModule: The Tomcat module for digest authentication.

f org.exoplatform.services.organization.idm.CustomMembershipLoginModule: This login module can be used to add authenticated user to some group after a successful login. For example, a user can be added as member to the group /platform/users after the login process. Group name and Membership type are configurable, and if they are not provided by the configuration, then the value member is used as the default value for membership type and /platform/users for group

This list describes minimally the function of the modules. If you need more details about the modules, you should consult the GateIn reference guide available at: http://docs.jboss.com/gatein/portal/latest/reference-guide/en-US/html/

All these modules use the Organization Service internally used by GateIn to manage users, groups, and membership information. The unique exception is WCILoginModule, which is a wrapper dedicated to provide wide compatibility for application servers.

More information about the Organization Service is provided in the Assigning users to groups recipe in Chapter 4, Managing Portal Users.

Securing Portal Contents

138

There's more…In order to implement your own JAAS module, you should take a look at the source code and use your preferred Java IDE. The concrete class must extend the following Java interface:

javax.security.auth.spi.LoginModule

All the methods of this interface that you need to extend are:

Public interface LoginModule { public abstract void initialize( javax.security.auth.Subject arg0, javax.security.auth.callback.CallbackHandler arg1, java.util.Map arg2, java.util.Map arg3 ); public abstract boolean login(); public abstract boolean commit(); public abstract boolean logout(); public abstract boolean abort();}

Once you create your own module implementation, you only need to add it to the module's configuration file. See the previous two recipes Securing with JBoss AS and Securing with Tomcat for the locations of the JAAS configuration files

See also f The Securing portals recipe

f The Security with Tomcat recipe

f The Security with JBoss AS recipe

Creating a login pageIn this recipe, we will configure a login page for a new portal.

Getting readyTo complete this task, we need to have some knowledge of the following technologies:

f Servlets/JSPs

f JAAS

Chapter 5

139

f GateIn, as many Java products use JAAS for authentication management, JSPs for the login forms, and servlets for operations such as login, logout, remind me, change password, and so on.

How to do it…1. In the web.xml of the main GateIn web application, add the code:

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

2. Add a JSP inside the portal root with this path:login---jsp-----login.jsp

A simple login.jsp can be written using the default JAAS variables as:

<form method="POST" action="j_security_check"> Login:<input type="text" name="j_username"><br/> Password:<input type="password" name="j_password"><br/> <input type submit="Login"/></form>

The web container will search the current JAAS module and will know where to redirect the log in action.

How it works…This authentication system is implemented through an HTML form connected to the domain, gatein-domain, and two paths for the login page and error page that reference the internal servlets, org.exoplatform.web.login.InitiateLoginServlet and org.exoplatform.web.login.ErrorLoginServlet. These servlets call a controller, the WCI Controller, which by default reads the pages inside the login/jsp directory.

The goal of the WCI Controller is simply to allow the compatibility between the application servers guaranteed by GateIn, meaning fewer problems in a migration.

Securing Portal Contents

140

There's more...A more complete login.jsp page can be seen in the main portal under the path seen above, so we will not explore the details.

Instead of the j_security_check, we can use a custom action in the form provided by the eXo team that calls the servlet org.exoplatform.web.security.PortalLoginController. It adds the feature for the "remember me". Here is an example of the configuration of the servlet in the web.xml file:

<servlet> <servlet-name>PortalLoginController</servlet-name> <servlet-class>org.exoplatform.web.security.PortalLoginController</servlet-class> </servlet>… <servlet-mapping> <servlet-name>PortalLoginController</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>

Here is an example of login.jsp using this servlet:

<form name="loginForm" action="<%= request.getContextPath()+ "/login"%>" method="post" style="margin: 0px;"> … add the jaas fields seen before for the username and password <input type="checkbox" name="rememberme" value="true"/></form>

Synchronizing usersThrough WEBUI, we can configure initializers that register the default users.

Getting readySimply add in the XML file /WEB-INF/conf/organization/organization-configuration.xml, the users, roles, and membership types to pre-charge.

Chapter 5

141

How to do it…Use the following code to add, for example, the root user:

<object type="org.exoplatform.services.organization.OrganizationConfig$User"> <field name="userName"> <string>root</string></field> <field name="password"> <string>gtn</string></field> <field name="firstName"> <string>Root</string></field> <field name="lastName"> <string>Root</string></field> <field name="email"> <string>root@localhost</string></field> <field name="groups"> <string>manager:/platform/administrators,member:/platform/users, member:/organization/management/executive-board </string> </field> </object>

The membership type for the root user:

<object type="org.exoplatform.services.organization.OrganizationConfig$MembershipType"> <field name="type"> <string>manager</string></field> <field name="description"> <string>manager membership type</string></field></object>

And the group called administrators for the root user:

<object type="org.exoplatform.services.organization.OrganizationConfig$Group"> <field name="name"> <string>administrators</string></field> <field name="parentId"> <string>/platform</string></field> <field name="description"> <string>the /platform/administrators group</string> </field>

Securing Portal Contents

142

<field name="label"> <string>Administrators</string></field></object>

Notice some details in this snippet are related to the tree structure of the group; the parentId field represents the name field of an other group to insert in the XML.

How it works…GateIn provides one class to import users, membership types, and groups in the database:

org.exoplatform.services.organization.OrganizationDatabaseInitializer

Through this service, we can add permissions available for the portal. The identity objects will be inserted in the database at the first start of the portal.

Securing pagesIn this recipe, we will see how to set access permissions on a single page. As for the portals, we can configure those through the web console and XML.

Getting readyStart Gatein and connect through a browser to the web console. Enter in the portal root application to see the XML files.

How to do it…1. On the web console, select one page, for example Application Registry:

Chapter 5

143

2. Now edit the page by clicking on the Edit Page link.

3. Click on the View Page properties button in the Page Editor box as shown in the following screenshot:

Securing Portal Contents

144

4. Here we can choose the permissions:

By using XML, we can open the file of the portal configuration where the dashboard page is configured, inside gatein.ear/02portal.war/WEB-INF/conf/portal/group/organization/management/executive-board/pages.xml. Here is the configuration:

<page> <name>management</name> ... <access-permissions> *:/organization/management/executive-board </access-permissions> <edit-permission> *:/organization/management/executive-board </edit-permission> ...</page>

How it works…Who receives the information of the access on the page and coordinates it? JCR is the key. Everything is registered in the JCR repository. See the How it works… section of the Securing categories recipe to learn more about this.

Chapter 5

145

Securing categoriesCategories are used by GateIn to categorize applications, meaning that, we can search an application in a simple manner because the applications are ordered. These categories can be secured too.

Getting readyWe will test the security with the two applications Application Registry Portlet and WSRP Portlet. The first can be found via the web console inside Administration/Application Registry. The second is inside the Application Registry portlet or in the box of applications visible in any page in edit mode, assuming you have the correct permissions.

How to do it…1. To secure the categories, access via the browser the portal as administrator and click

on Application Registry:

Securing Portal Contents

146

This is the Application Registry Portlet:

2. Click on the Edit Category button to see the window with the permissions:

Chapter 5

147

Here you can make changes according to your needs. The available categories for a user or group can be seen inside the Management box. For example, going to the Group Editor on the voice Edit Page panel with demo user you will see only the categories Dashboard and Gadgets:

If we set an authorized permission on the portlet, but we do not have permissions on the category, the portlet will not be shown unless we set a different available category to the portlet. Now we will see how to put the WSRP Portlet in the Dashboard category.

1. Login as the administrator user and go to the Portlet Registry as seen before and click on the + button:

Securing Portal Contents

148

2. Select the WSRP portlet and click on the Add button:

3. Re-login as a demo user to see the new portlet in the Edit Page Panel:

Chapter 5

149

The WSRP Portlet is available for the demo user because he/she has the necessary privileges to see it. Otherwise, it will not be shown.

The category permissions are also configurable through WEBUI in the file gatein.ear/02portal.war/WEB-INF/conf/portal/application-registry-configuration.xml inside the ApplicationCategoriesPlugins component Plugin of the ApplicationRegistryService component:

<object type="org.exoplatform.application.registry.ApplicationCategory"> ... <field name="accessPermissions"> <collection type="java.util.ArrayList" item-type="java.lang.String"> <value> <string>*:/platform/administrators</string> </value> <value> <string>*:/organization/management/executive-board</string> </value> </collection> </field>...</object>

Edit permissions are useless in category management. Also, the relationships of the portlets are configurable in this component:

<object type="org.exoplatform.application.registry.ApplicationCategory"> …… <field name="applications"> <collection type="java.util.ArrayList"> <value> <object type= "org.exoplatform.application.registry.Application"> <field name="applicationName"> <string> ApplicationRegistryPortlet</string> </field>……. </field></object>

Securing Portal Contents

150

As the portal allows you to change manually everything on the web console, these XML configurations only kick in on the first boot when the database is clean, otherwise you would risk an override of the manual configurations at each restart. Notice that WEBUI does not have the concept of portlets. WEBUI calls the portlets as applications. This allows you to manage other components like the portlet, such as gadgets.

How it works…The categories, applications, and pages are components based on JCR. JCR allows for an easy way of development and maintenance of these contents because all the metadata is registered. For example, we can query a JCR node in any part of the portal and we can take each little piece of information about content.

GateIn uses Chromattic to access JCR and eXo JCR as the JCR repository. The details of Chromattic can be seen here: http://code.google.com/p/chromattic/. The details for eXo JCR are available at the following URL:

http://www.jboss.org/exojcr.html

Securing applicationsIn this recipe, we will talk about applications as a set of portlets and gadgets, and we will see how to secure them.

Getting readyWe need the Application Registry Portlet to start. See the previous recipe to find out how to use it.

How to do it…As for the categories, the permissions of the applications can be managed through Application Registry. They can be configured graphically, as for the categories, and through XML. Here is the configuration of the Account Portlet:

<object type="org.exoplatform.application.registry.Application"> <field name="applicationName"> <string>AccountPortlet</string> </field>……..

Chapter 5

151

<field name="accessPermissions"> <collection type="java.util.ArrayList" item-type="java.lang.String"> <value> <string>*:/platform/administrators</string> </value> <value> <string>*:/organization/management/executive-board</string> </value> </collection> </field>………</object>

As the application is not used only for administration, we can set the access for the instance. For example, we could get more applications of the same type in a portal. Doing this through the web console is very simple.

1. Select a page and click on Edit Page:

Securing Portal Contents

152

2. Select the body of the application. A tool row will appear at the top-left corner, as shown in the following screenshot:

3. Click on the pencil icon to edit the application and go to the permission panel. In this panel, we can set the required permissions, as we did in the Managing registered portlets recipe in Chapter 2, Managing Portal Contents Using the GUI.

The Application Registry seen before automatically injects the default permissions. Here we can change the permissions for this page. For example, we can get a public page with two applications, one visible only by one group and another by another group depending upon the login.

How it works…As for the categories, the pages are imported from eXo JCR through Chromattic. All permissions are content metadata in GateIn. We can access Chromattic anywhere (groovy scripts, portlets, POJO) and get the information that we need if we have the correct permissions.

As for the categories, there is no difference between the Edit and Access permissions. The access permissions are read by pages and toolboxes. If a toolbox cannot access an application, it means that the toolbox has no edit permission for that application.

Chapter 5

153

Securing portletsNow let us see how to secure a new portlet that uses only standard configurations.

Getting readyIn this recipe, we will access these Java Web Technologies:

f JSP/Servlet

f Portlet 286

f WebUI

How to do it…This is the structure of a simple project working with portlets:

The web.xml file contains only an empty web-app tag to allow the deployment of the application.

Here is the welcome.jsp:

<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>

<portlet:defineObjects />

<div class="portlet-section-header"> | <%=renderRequest.isUserInRole("users")%> | <%=renderRequest.isUserInRole("administrators")%></div>

Securing Portal Contents

154

Add the following roles in the portlet.xml file and deploy the application:

<security-role-ref> <role-name>administrators</role-name> <role-link>administrators</role-link></security-role-ref><security-role-ref> <role-name>users</role-name> <role-link>users</role-link></security-role-ref>

If we add the new portlet in a page when we login as a root user, the portlet will show the following result:

How it works…What does it mean? It means that the developer is now ready to work with the permissions. The role names configured in the portlet, administrators, and users are mapped automatically in GateIn as the roles: /platform/administrators and /platform/users. The same thing can be done for any group name; the important thing is that the group exists inside the /platform folder in any level.

There's more...If we don't want to use the /platform folder for our groups, we can use the root folder (/) and add as first node the group of interest. For example, for the group /organization/management/executive-board, you need to use the role organization in your portlet.

If we don't want to use this, either, we need to extend the Role Extractor Component. This component is used to extract the roles of the portal to use for Java standard operations, for example, JAAS authorization. Here the extension is to add in a configuration.xml file (for example the idm-configuration.xml):

<component> <key> org.exoplatform.services.security.RolesExtractor </key> <type>

Chapter 5

155

org.exoplatform.services.security.impl.DefaultRolesExtractorImpl </type> <init-params> <value-param> <name>user.role.parent.group</name> <description>authentication service use this value to authenticate</description> <value>platform</value> </value-param> </init-params></component>

Put the desired value instead of platform.

However, remember that the platform folder contains the administrators, guests, and common users, so before you do it, it is mandatory to create new groups and users associations with the new group folder. Make sure that you do this!

When we deploy the portlet, it is configured as public in the Application Registry:

If we don't want a public portlet, we can simply unflag the public option and configure the permissions through the web console.

6Developing Portlets

In this chapter, we will cover:

f Creating a portlet with the Portlet 2.0 Specification

f Using an action to pass form parameters

f Using the user locale to localize portlet content

f Communicating between portlets using Public Render Parameters

f Communicating between portlets using events

IntroductionThis chapter will introduce portlets through the Portlet 2.0 Specification. The Portlet 2.0 Specification defines how portlets work within a portlet container, and what functionality they can provide.

The Portlet 2.0 Specification is known as Java Specification Request 286 within the Java Community Process, and had a final release on June 12, 2008.

This chapter will focus on portlets that utilize JSP as the view layer, though there is no restriction on which technology creates the view for a portlet. In Chapter 10, Frameworks in a Portal, we discuss JSF portlets.

Developing Portlets

158

Creating a portlet with the Portlet 2.0 Specification

This recipe will guide you through the process of creating a basic portlet that conforms to the Portlet 2.0 Specification. Once created, we will deploy the portlet into GateIn to see the result.

Getting readyThe following are required for this recipe:

f Apache Maven

f An IDE of your choice

f GateIn-3.2.0.Final

How to do it...To create a simple portlet based on the Portlet 2.0 Specification:

1. Create a new Maven project within your IDE, specifying Group ID: gatein.cookbook, Artifact ID: chapter6, and Packaging: war.

2. Inside the project's pom.xml, add the following dependency: <dependency> <groupId>javax.portlet</groupId> <artifactId>portlet-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency>

3. Create a class named HelloWorldPortlet that extends javax.portlet.GenericPortlet within a package named gatein.cookbook.chapter6.

4. Create a method named display within HelloWorldPortlet as follows: @RenderMode(name = "view") public void display(RenderRequest request, RenderResponse response) throws PortletException, IOException { getPortletContext().getRequestDispatcher("/helloWorld.jsp"). include(request, response); }

Chapter 6

159

The @RenderMode annotation is used to inform the GenericPortlet which portlet-mode this method will render the content for. The display method will be used to render the view portlet mode, which is equivalent to overriding the doView method from GenericPortlet. Additional portlet modes include help and edit.

5. Create a file named helloWorld.jsp in the src/main/webapp folder of the project.

6. In helloWorld.jsp add a simple message, without any code, such as "Hello World from your new portlet!"

7. Create portlet.xml in the src/main/webapp/WEB-INF folder of the project.

8. Add the following to portlet.xml:<?xml version="1.0" encoding="UTF-8"?><portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0"> <portlet> <portlet-name>HelloWorld</portlet-name> <portlet-class>gatein.cookbook.chapter6.HelloWorldPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>Hello World portlet</title> </portlet-info> </portlet></portlet-app>

9. Create web.xml in the src/main/webapp/WEB-INF folder of the project.

10. Add the following to web.xml:<?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_5.xsd" version="2.5"></web-app>

Developing Portlets

160

11. Run the following in the root of the project directory to build the web archive:> mvn clean package

12. Copy the generated web archive, chapter6-1.0.0.SNAPSHOT.war, from the target folder into the deployment folder where you unpacked the GateIn installation.

13. Start the server and log in to the server as an administrator.

14. Access the Application Registry, as seen in Chapter 2, Managing Portal Contents Using the GUI, and click on Import Applications to make our portlet available.

15. Create a new page for the HelloWorld portlet as seen in Chapter 2, Managing Portal Contents Using the GUI.

16. Navigate to the page you added the HelloWorld portlet to; you should now see a page similar to the following screenshot:

How it works...What we've done is create a very simple portlet that displays text, which is being run from within the portlet container. There are three main parts to a portlet that we covered earlier:

f A portlet class for controlling integration with the portlet container. In our case this is HelloWorldPortlet, which is the central point for our portlet. It extends GenericPortlet so that we don't need to implement all the methods of the Portlet interface directly. For our portlet we only created the display() method to handle the rendering of the portlet, while leaving all the other integration with the portlet container to be completed by the GenericPortlet. Within the display() method we redirect to the Java Server Page we created, helloWorld.jsp, to generate the HTML content to be rendered by the portlet container.

f A JSP that creates the HTML output we want rendered in the browser. Although helloWorld.jsp contained only text, we could add HTML or additional JSP content to generate the HTML output we needed.

Note that markup generated for a portlet cannot contain <html>, <head>, <body> elements, or any markup that cannot be present within the <body> element, as it is the responsibility of the portlet container to generate these.

Chapter 6

161

f A portlet descriptor file, portlet.xml, describes to the portlet container which class controls the portlet and what features it makes available for use. We specified the portlet-class, what mime-type the portlet returns content in, what portlet-mode the portlet understands, and a title for the portlet. Specifying a portlet-mode of view informs the portlet container to only render our portlet content, and to not display help or editing options.

All the various parameters that can be set within the portlet descriptor can be seen by checking the schema at http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd.

There's more...In the portlet class we simply created a method annotated with @RenderMode(name = "view") to define how we would render the content for the portlet. There are several other ways in which the same outcome could have been achieved, which are described below.

Using a PrintWriter within the portlet classInstead of delegating to a JSP to create the HTML content within display(), we could also have used the following code within display() to write HTML content directly onto the response stream:

PrintWriter out = response.getWriter();out.println("Hello World from your new portlet!");

Overriding doView method from GenericPortletGenericPortlet provides default implementations for all the integration with the portlet container, saving development time, but it also provides convenient points to extend GenericPortlet functionality as needed.

Instead of creating display() in HelloWorldPortlet, we can replace display() with doView() from GenericPortlet and include a different implementation. Replacing display(), including the @RenderMode annotation, with the following will generate the same portlet content:

@Overrideprotected void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { getPortletContext().getRequestDispatcher("/helloWorld.jsp").include(request, response);}

Developing Portlets

162

Implementing Portlet interfaceInstead of extending from GenericPortlet, another option is to create a new class that implements the Portlet interface.

See also f The Using an action to pass form parameters recipe

Using an action to pass form parametersThis recipe will extend the portlet created in the previous recipe by adding a form for the user to enter their name, and then responding with a welcome message that includes their name.

Getting readyThe following are required for this recipe:

f Apache Maven

f An IDE of your choice

f GateIn-3.2.0.Final

f HelloWorldPortlet project from the Creating a portlet with the Portlet 2.0 Specification recipe

How to do it...Passing form parameters and acting on that input involves the following:

1. Create welcome.jsp in the src/main/webapp folder of the project.

2. Add the following to welcome.jsp:<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

<div class="portlet-section-header">Welcome to the HelloWorld portlet</div><br/><div class="portlet-section-body"> <form action='<portlet:actionURL name="nameAction"/> method="post"'> <span class="portlet-form-label">Name:</span> <input class="portlet-form-input-field" type="text" name="yourname"/>

Chapter 6

163

<input class="portlet-form-button" type="Submit" value="Say Hello"/> </form></div><br/>

3. Change the code of display() to the following: if (null == request.getParameter("yourname") || "".equals(request.getParameter("yourname"))) { getPortletContext().getRequestDispatcher("/welcome.jsp").include(request, response); } else { getPortletContext().getRequestDispatcher("/helloWorld.jsp"). include(request, response); }

4. Create a new method in HelloWorldPortlet that has the following code:@ProcessAction(name = "nameAction")public void nameAction(ActionRequest request, ActionResponse response) throws PortletException { response.setRenderParameter("yourname", request.getParameter("yourname"));}

5. Change the JSP code in helloWorld.jsp to the following:<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

<portlet:defineObjects/>

<div class="portlet-section-header">Hello!</div><br/><div class="portlet-section-body">Hello <%= renderRequest.getParameter("yourname") %> from your first portlet!</div><br/>

6. Run the following in the root of the project directory to build the web archive:> mvn clean package

7. Copy the generated web archive, chapter6-1.0.0.SNAPSHOT.war, from the target folder into the deployment folder where you unpacked the GateIn installation.

8. Start the server and access the portal page created in the Creating a portlet with the Portlet 2.0 Specification recipe.

Developing Portlets

164

9. The portlet should now look like the following screenshot:

10. In the Name field, enter some text and click on Say Hello. An example result from the submission can be seen in the following screenshot:

How it works...In welcome.jsp we added the tag library from the Portlet 2.0 Specification so that we can use portlet:actionURL and portlet:defineObjects. A simple form was all that was needed to capture some text, in this case a name, that can be passed as a parameter to the nameAction method of the HelloWorldPortlet by using the portlet:actionURL tag.

In HelloWorldPortlet nameAction() was added to process the ActionRequest from the portlet container. It simply takes the request parameter containing the form parameter that was submitted, and adds it as a render parameter onto the response, which makes it available to the process of rendering a portlet.

The portlet:defineObjects tag implicitly adds request and response objects based on the portlet phase, portlet config, portlet session, and portlet preferences.

Use of the render parameter in nameAction() is referred to as a private render parameter. A private render parameter is only available to the portlet that added it while rendering, in this case the HelloWorldPortlet. In Communicating between portlets using Public Render Parameters, render parameters that are available to all portlets while rendering will be covered.

Chapter 6

165

The HelloWorldPortlet display() was altered to check for the presence of the form parameter on the request. Rendering of the portlet content is dispatched to the welcome.jsp page if the form parameter is not present, and to the helloWorld.jsp page if it was.

Finally, helloWorld.jsp was modified to retrieve the form parameter from the request and use it as part of a message to display.

There's more...Next we'll describe some additional enhancements such as using render parameters and resetting the form content.

Passing parameters instead of rendering via an Action URLIn this recipe, we were capturing dynamic text for use in an Action URL. If the text was not dynamic, or did not require user input in a form, then the following code in welcome.jsp could have been used instead:

<a href="<portlet:renderURL><portlet:param name='yourname' value='John Doe'/></portlet:renderURL>">John Doe</a>

In the above approach there is no Action being processed, hence nameAction() would not be required on HelloWorldPortlet. The request parameter we need, yourname, is being directly passed to the RenderRequest, without the need for an intermediate ActionRequest.

Adding a link to reset user inputOnce the User has entered some text and submitted the form, there is currently no way for them to return to the initial state of the portlet that shows the form again. In most situations that would be fine, but if it is necessary to return to a "blank slate", then it can be easily accomplished by adding the following link code into helloWorld.jsp in an appropriate location on the page:

<a href="<portlet:renderURL portletMode='view' />">Reset</a>

Using the renderURL portlet tag, we inform the portlet container that we want a URL created that will cause the portlet to be rendered with the portletMode of view, which will generate a URL that does not include the state generated as part of the form submit action. Clicking on the link will return the user to welcome.jsp and a blank form.

Developing Portlets

166

The following screenshot shows an example of what this could look like in the portlet:

See also f The Creating a portlet with the Portlet 2.0 Specification recipe

Using the user locale to localize portlet content

In this recipe, we will modify the portlet to use property files for user interface text and create a German language version of them so that we can switch to the German locale and see the appropriate text displayed in the portlet.

Getting readyThe following are required for this recipe:

f Apache Maven

f An IDE of your choice

f GateIn-3.2.0.Final

f HelloWorldPortlet project from the Creating a portlet with the Portlet 2.0 Specification recipe

How to do it...To recognize the locale being used in the portal, and to support internationalized messages, do the following:

1. Add the following into portlet.xml after </supports>: <supported-locale>en</supported-locale> <supported-locale>de</supported-locale> <resource-bundle>gatein.cookbook.chapter6.HelloWorldPortlet</resource-bundle>

Chapter 6

167

2. Create HelloWorldPortlet.properties and HelloWorldPortlet_de.properties in the src/main/resources/gatein/cookbook/chapter6 folder of the project.

3. Add the following content to HelloWorldPortlet.properties:javax.portlet.title=Hello World Portletjavax.portlet.display-name=Hello World Portletlink.reset=Resetlink.return=Returnwelcome.heading=Welcome to the Hello World portletwelcome.form.name=Namewelcome.form.submit=Say Hellohelloworld.heading=Hello!helloworld.text.hello=Hellohelloworld.text.message=from your first portlet!

4. Add the following content to HelloWorldPortlet_de.properties:javax.portlet.title=Hallo Welt Portletjavax.portlet.display-name=Hallo Welt Portletlink.reset=Zurücksetzenlink.return=zurückkehrenhelloworld.heading=Hallo!helloworld.text.hello=Hallohelloworld.text.message=von Ihrem ersten portlet!welcome.heading=Willkommen in der Hallo Welt Portletwelcome.form.name=Namewelcome.form.submit=Sag Hallo

5. Replace the content of welcome.jsp with the following:<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %><%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>

<portlet:defineObjects/><fmt:setBundle basename="gatein.cookbook.chapter6.HelloWorldPortlet" />

<div>

<div class="portlet-section-header"><fmt:message key="welcome.heading" /></div> <br/>

<div class="portlet-section-body">

Developing Portlets

168

<form action='<portlet:actionURL name="nameAction"/>' method="post">

<span class="portlet-form-label"><fmt:message key="welcome.form.name" />:</span>

<input class="portlet-form-input-field" type="text" name="yourname"/>

<input class="portlet-form-button" type="Submit" value="<fmt:message key="welcome.form.submit" />"/> </form> </div> <br/></div>

6. Replace the content of helloWorld.jsp with the following:<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %><%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>

<portlet:defineObjects/><fmt:setBundle basename="gatein.cookbook.chapter6.HelloWorldPortlet" />

<div>

<div class="portlet-section-header"><fmt:message key="helloworld.heading" /></div> <br/>

<div class="portlet-section-body"><fmt:message key="helloworld.text.hello" /> <%= renderRequest.getParameter("yourname") %> <fmt:message key="helloworld.text.message" /></div> <br/>

<a href="<portlet:renderURL portletMode='view' />"><fmt:message key="link.reset" /></a> <br/></div>

7. Run the following in the root of the project directory to build the web archive:> mvn clean package

Chapter 6

169

8. Copy the generated web archive, chapter6-1.0.0.SNAPSHOT.war, from the target folder into the deployment folder where you unpacked the GateIn installation.

9. Start the server and access the portal page created in Creating a portlet with the Portlet 2.0 Specification.

10. Click on Change Language link at the top-right corner, select German, and click on Apply.

11. The HelloWorld portlet should now look like the following screenshot:

12. Navigate around the portlet to ensure that all portlet text is showing in German.

13. When finished, click on Sprache wechseln to change the portal language back to English.

How it works...Step 1 modifies the portlet.xml file to inform the portlet container that it supports the English and German locales, and that it can find the necessary text resources for each locale in the properties file specified by <resource-bundle>.

Steps 2, 3, and 4 create the properties files for the English and German locales with the appropriate resource labels and values in each of them.

Steps 5 and 6 modify the existing JSPs to incorporate the changes required for supporting localized text in a portlet. Each of the changes we've made will be detailed below.

At the top of each file a taglib definition was added that provides access to the JSTL formatting tags necessary for localizing JSP content.

Just below the taglib definition the fmt:setBundle tag is used to specify the location of the properties file that should be used to retrieve text from.

Note that the basename identifying the filename containing localized text does not include the .properties extension.

Developing Portlets

170

The last changes were to replace any text in the portlet with <fmt:message key="" />, with the key being a unique name within the properties files that specifies which piece of text to retrieve for each locale.

There's more...

Localizing navigation node nameAs the earlier screenshots showed, the name of the portlet within the portal menus remained Hello World, as that identifier is set on the navigation node within the portal and not from the portlet. To also localize the name of the navigation node, do the following:

1. Start the server and log in as an administrator.

2. Click on Site in the menu at the very top of the window.

3. Click on Edit Navigation and the Navigation Management pop-up will appear.

4. Select Hello World with a left mouse click and then right-click on Hello World to display the menu as shown in the following screenshot:

Chapter 6

171

5. Select Edit this Node and a pop-up will appear as seen in the following screenshot:

6. Select the drop-down for Language and choose German.

7. In the Label field enter Hallo Welt and click on Save and click Save again on the Navigation Management window.

8. Sign out as an administrator and access the portal page created in Creating a portlet with the Portlet 2.0 Specification.

9. Select Change Language in the top-right corner and select German then click on Apply.

10. The navigation node of the portal for the portlet should now be correctly localized, as seen in the following screenshot:

See also f The Creating a portlet with the Portlet 2.0 Specification recipe

Developing Portlets

172

Communicating between portlets using Public Render Parameters

Create two portlets, each in a separate portlet application, one to search for stocks and the other to show a list of stocks in a watchlist. When a stock is retrieved in the first portlet it will provide the option to watch that stock, and if clicked the stock will be visible within the watchlist by passing that stock through Public Render Parameters.

A portlet application equates to a single web archive, and there is no restriction on how many portlets can be within a single portlet application.

Getting readyThe following are required for this recipe:

f Apache Maven

f An IDE of your choice

f GateIn-3.2.0.Final

How to do it...To create the portlet that will search for stocks, do the following:

1. Create a new Maven project within your IDE, specifying Group ID: gatein.cookbook, Artifact ID: chapter6-prp, and Packaging: war.

2. Inside the project's pom.xml, add the following dependency: <dependency> <groupId>javax.portlet</groupId> <artifactId>portlet-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency>

3. Create a class named StockSearchPortlet that extends javax.portlet.GenericPortlet within a package named gatein.cookbook.chapter6.

4. Add a private variable to StockSearchPortlet named availableStocks of type String[] and set it to null.

Chapter 6

173

5. Create a method named init within StockSearchPortlet with the following content: @Override public void init(PortletConfig config) throws PortletException { super.init(config); initStockList(); }

6. Create a method named initStockList within StockSearchPortlet with the following content: private void initStockList() { availableStocks = new String[5]; availableStocks[0] = "IBM:International Business Machines Corp."; availableStocks[1] = "IBN:Icici Bank Limited"; availableStocks[2] = "REV:Revlon"; availableStocks[3] = "RHI:Robert Half International Inc."; availableStocks[4] = "RHT:Red Hat"; }

7. Create a method named display within StockSearchPortlet as follows: @RenderMode(name = "view") public void display(RenderRequest request, RenderResponse response) throws PortletException, IOException { request.setAttribute("stockList", filterStocks(request)); getPortletContext().getRequestDispatcher("/stockSearch.jsp").include(request, response); }

8. Create a method named filterStocks within StockSearchPortlet with the following content: private String[] filterStocks(RenderRequest request) { String filter = request.getParameter("ticker"); if (null != filter && filter.trim().length() > 0) { filter = filter.trim().toLowerCase(); StringBuffer filterStocks = new StringBuffer(60); boolean found = false; for (String stock : availableStocks) { if (stock.toLowerCase().startsWith(filter)) { if (found) { filterStocks.append(";"); } filterStocks.append(stock);

Developing Portlets

174

found = true; } } return found == true ? filterStocks.toString().split(";") : null; } return availableStocks; }

9. Create a method named searchStocks within StockSearchPortlet with the following content: @ProcessAction(name = "searchStocks") public void searchStocks(ActionRequest request, ActionResponse response) throws PortletException { response.setRenderParameter("ticker", request.getParameter("ticker")); }

10. Create a method named addWatch within StockSearchPortlet with the following content: @ProcessAction(name = "watch") public void addWatch(ActionRequest request, ActionResponse response) throws PortletException { String stock = request.getParameter("stock"); response.setRenderParameter("watch_stock", stock); }

11. Create stockSearch.jsp in the src/main/webapp folder of the project.

12. Add the following content into stockSearch.jsp:<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

<portlet:defineObjects/>

<div class="portlet-section-header">Search Stocks</div><br/><div class="portlet-section-body"> <form action='<portlet:actionURL name="searchStocks"/>' method="post"> <span class="portlet-form-label">Ticker:</span> <input class="portlet-form-input-field" type="text" name="ticker"/> <input class="portlet-form-button" type="Submit" value="Search"/>

Chapter 6

175

</form> <br/><% String ticker = (String)renderRequest.getParameter("ticker"); String[] results = (String[])renderRequest.getAttribute("stockList");%> <c:choose> <c:when test="<%= results != null && results.length > 0 %>"> <table border="1"> <thead class="portlet-table-header"> <tr> <th>Ticker</th> <th>Company</th> <th></th> </tr> </thead> <tbody class="portlet-table-body"> <% for (String stock: results) { if (null != stock) { String[] split = stock.split(":"); %> <tr> <td align="center" class="portlet-table-text"><%= split[0] %></td> <td align="center" class="portlet-table-text"><%= split[1] %></td> <td align="center" class="portlet-table-text"><a class="portlet-font-dim" href="<portlet:actionURL name='watch'><portlet:param name='stock' value='<%= split[0] %>' /></portlet:actionURL>">Watch!</a></td> </tr> <% } } %> </tbody> </table> </c:when> <c:otherwise> <c:if test="<%= ticker != null && ticker.trim().length() > 0 %>"> <span>No results found for ticker: <%= ticker %></span> </c:if>

Developing Portlets

176

</c:otherwise> </c:choose></div><br/>

13. Create portlet.xml in the src/main/webapp/WEB-INF folder of the project.

14. Add the following content to portlet.xml:<?xml version="1.0" encoding="UTF-8"?><portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0"> <portlet> <portlet-name>StockSearch-PRP</portlet-name> <portlet-class>gatein.cookbook.chapter6.StockSearchPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>Stock Search - PRP portlet</title> </portlet-info> <supported-public-render-parameter>watch_stock</supported-public-render-parameter> </portlet> <public-render-parameter> <identifier>watch_stock</identifier> <qname xmlns:gi="http://www.gatein.org/xml/ns/cookbook">gi:watch_stock</qname> </public-render-parameter></portlet-app>

15. Create web.xml in the src/main/webapp/WEB-INF folder of the project.

16. Add the following to web.xml:<?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_5.xsd" version="2.5"></web-app>

Chapter 6

177

To create the portlet that will maintain a watch list of stocks, do the following:

17. Create a new Maven project within your IDE, specifying Group ID: gatein.cookbook, Artifact ID: chapter6-prp-receiver, and Packaging: war.

18. Create a class named WatchlistPortlet that extends javax.portlet.GenericPortlet within a package named gatein.cookbook.chapter6.

19. Add a private variable to WatchlistPortlet named watchedStocks of type String[] and set it to new String[5].

20. Add a private variable to WatchlistPortlet named watchedCount of type int and set it to 0.

21. Create a method named displayWatchList within WatchlistPortlet as follows: @RenderMode(name = "view") public void displayWatchList(RenderRequest request, RenderResponse response) throws PortletException, IOException { String watchStock = (String)request.getParameter("watch_stock");

if (null != watchStock && watchStock.trim().length() > 0) { if (watchCount > 0) { boolean found = false; for (String stock : watchedStocks) { if (null != stock && stock.equals(watchStock)) { found = true; break; } } if (!found) { watchedStocks[watchCount++] = watchStock; } } else { watchedStocks[watchCount++] = watchStock; } } request.setAttribute("watchlist", watchedStocks); request.setAttribute("watchCount", watchCount); getPortletContext().getRequestDispatcher("/watchlist.jsp").include(request, response); }

22. Create watchlist.jsp in the src/main/webapp folder of the project.

Developing Portlets

178

23. Add the following content into watchlist.jsp:<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

<portlet:defineObjects/>

<div class="portlet-section-header">Stock Watchlist</div><br/><div class="portlet-section-body"><% String[] stocks = (String[])renderRequest.getAttribute("watchList"); Integer watchCount = (Integer)renderRequest.getAttribute("watchCount");%> <c:choose> <c:when test="<%= watchCount > 0 %>"> <table border="1"> <thead class="portlet-table-header"> <tr> <th>Ticker</th> </tr> </thead> <tbody class="portlet-table-body"> <% for (String stock: stocks) { if (null != stock) { %> <tr> <td align="center" class="portlet-table-text"><%= stock %></td> </tr> <% } } %> </tbody> </table> </c:when> <c:otherwise> <span>No stocks being watched at the moment.</span> </c:otherwise> </c:choose></div><br/>

24. Create portlet.xml in the src/main/webapp/WEB-INF folder of the project.

Chapter 6

179

25. Add the following content to portlet.xml:<?xml version="1.0" encoding="UTF-8"?><portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0"> <portlet> <portlet-name>WatchList-PRP</portlet-name> <portlet-class>gatein.cookbook.chapter6.WatchlistPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>WatchList - PRP portlet</title> </portlet-info> <supported-public-render-parameter>watch_stock</supported-public-render-parameter> </portlet> <public-render-parameter> <identifier>watch_stock</identifier> <qname xmlns:gi="http://www.gatein.org/xml/ns/cookbook">gi:watch_stock</qname> </public-render-parameter></portlet-app>

26. Run the following in the root of the project directory to build the web archive:>mvn clean package

27. Copy the generated web archive, chapter6-prp-1.0.0.SNAPSHOT.war, from the target folder into the deployment folder where you unpacked the GateIn installation.

28. Start the server and log in as an administrator.

29. Access the Application Registry, as seen in Chapter 2, Managing Portal Contents Using the GUI, and click on Import Applications to make our portlet available.

30. Create a new page for the StockSearch-Prp and WatchList-Prp portlets as seen in Chapter 2, Managing Portal Contents Using the GUI.

Developing Portlets

180

31. The portlet page should like the following screenshot:

32. Click on Watch! in the row for Revlon stock, and the portal page should look like the following screenshot:

Chapter 6

181

How it works...Steps 4, 5, and 6 create a String array to hold a list of stocks that are available to search for, and initialize that list during the init of the portlet. The initStockList method could retrieve the stocks from a database or any external source, but in this case it is purely hard-coded for convenience.

Step 7 sets the list of stocks that will be displayed onto the RenderRequest, before redirecting to the stockSearch.jsp page. The list of stocks set on the RenderRequest is generated in the filterStocks method in Step 8. It uses the value of ticker, set in Step 9, to reduce the number of stocks returned if there are partial matches.

Step 10 sets the stock we want to watch as a render parameter of the ActionResponse within the portlet. By default, this is only available to StockSearchPortlet, but by adding the <public-render-parameter> section to portlet.xml in Step 14 the render parameter is exposed publicly under the namespace and name specified.

Step 21 retrieves the parameter value from the request, which is available because the portlet definition specifies that it supports the Public Render Parameter details configured in Step 25.

A major restriction with using Public Render Parameters is that they are only suitable for passing string parameters between portlets.

See also f The Creating a portlet with the Portlet 2.0 Specification recipe

f The Communicating between portlets using events recipe

Communicating between portlets using events

For this recipe, we will create two portlets, each in a separate portlet application, one to search for stocks and the other to show a list of stocks in a watchlist. When a stock is retrieved in the first portlet, it will provide the option to watch that stock, and if clicked, the stock will be visible within the watchlist by firing a portlet event that the receiving portlet will act on.

Getting readyThe following are required for this recipe:

f Apache Maven

f An IDE of your choice

f GateIn-3.2.0.Final

Developing Portlets

182

How to do it...To create the portlet that will search for stocks, do the following:

1. Create a new Maven project within your IDE, specifying Group ID: gatein.cookbook, Artifact ID: chapter6-event, and Packaging: war.

2. Inside the project's pom.xml, add the following dependencies: <dependency> <groupId>javax.portlet</groupId> <artifactId>portlet-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency>

3. Create a class named StockEvent within a package called gatein.cookbook.chapter6.

4. Add the following content to it:@XmlRootElementpublic class StockEvent implements Serializable {

private static final long serialVersionUID = 4234197037147498216L;

private String ticker; private String name;

public String getTicker() { return ticker; }

public StockEvent ticker(String ticker) { this.ticker = ticker; return this; }

public String getName() { return name;

Chapter 6

183

}

public StockEvent name(String name) { this.name = name; return this; }

}

5. Create a class named StockSearchPortlet that extends javax.portlet.GenericPortlet within a package named gatein.cookbook.chapter6.

6. Add a private variable to StockSearchPortlet called availableStocks of type String[] and set it to null.

7. Create a method named init within StockSearchPortlet with the following content: @Override public void init(PortletConfig config) throws PortletException { super.init(config); initStockList(); }

8. Create a method called initStockList within StockSearchPortlet with the following content: private void initStockList() { availableStocks = new String[5]; availableStocks[0] = "IBM:International Business Machines Corp."; availableStocks[1] = "IBN:Icici Bank Limited"; availableStocks[2] = "REV:Revlon"; availableStocks[3] = "RHI:Robert Half International Inc."; availableStocks[4] = "RHT:Red Hat"; }

9. Create a method named display within StockSearchPortlet as follows: @RenderMode(name = "view") public void display(RenderRequest request, RenderResponse response) throws PortletException, IOException { request.setAttribute("stockList", filterStocks(request)); getPortletContext().getRequestDispatcher("/stockSearch.jsp").include(request, response); }

Developing Portlets

184

10. Create a method named filterStocks within StockSearchPortlet with the following content: private String[] filterStocks(RenderRequest request) { String filter = request.getParameter("ticker"); if (null != filter && filter.trim().length() > 0) { filter = filter.trim().toLowerCase(); StringBuffer filterStocks = new StringBuffer(60); boolean found = false; for (String stock : availableStocks) { if (stock.toLowerCase().startsWith(filter)) { if (found) { filterStocks.append(";"); } filterStocks.append(stock); found = true; } } return found == true ? filterStocks.toString().split(";") : null; } return availableStocks; }

11. Create a method named searchStocks within StockSearchPortlet with the following content: @ProcessAction(name = "searchStocks") public void searchStocks(ActionRequest request, ActionResponse response) throws PortletException { response.setRenderParameter("ticker", request.getParameter("ticker")); }

12. Create a method named addWatch within StockSearchPortlet with the following content: @ProcessAction(name = "watch") public void addWatch(ActionRequest request, ActionResponse response) throws PortletException { String stock = request.getParameter("stock"); StockEvent stockEvent = new StockEvent(); for (String availStock : availableStocks) { if (availStock.startsWith(stock)) { String[] split = availStock.split(":"); stockEvent.ticker(split[0]).name(split[1]); break;

Chapter 6

185

} } QName eventName = new QName("http://www.gatein.org/xml/ns/cookbook", "watchStockEvent"); response.setEvent(eventName, stockEvent); }

13. Create stockSearch.jsp in the src/main/webapp folder of the project.

14. Add the following content into stockSearch.jsp:<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

<portlet:defineObjects/>

<div class="portlet-section-header">Search Stocks</div><br/><div class="portlet-section-body"> <form action='<portlet:actionURL name="searchStocks"/>' method="post"> <span class="portlet-form-label">Ticker:</span> <input class="portlet-form-input-field" type="text" name="ticker"/> <input class="portlet-form-button" type="Submit" value="Search"/> </form> <br/><% String ticker = (String)renderRequest.getParameter("ticker"); String[] results = (String[])renderRequest.getAttribute("stockList");%> <c:choose> <c:when test="<%= results != null && results.length > 0 %>"> <table border="1"> <thead class="portlet-table-header"> <tr> <th>Ticker</th> <th>Company</th> <th></th> </tr> </thead> <tbody class="portlet-table-body"> <%

Developing Portlets

186

for (String stock: results) { if (null != stock) { String[] split = stock.split(":"); %> <tr> <td align="center" class="portlet-table-text"><%= split[0] %></td> <td align="center" class="portlet-table-text"><%= split[1] %></td> <td align="center" class="portlet-table-text"><a class="portlet-font-dim" href="<portlet:actionURL name='watch'><portlet:param name='stock' value='<%= split[0] %>' /></portlet:actionURL>">Watch!</a></td> </tr> <% } } %> </tbody> </table> </c:when> <c:otherwise> <c:if test="<%= ticker != null && ticker.trim().length() > 0 %>"> <span>No results found for ticker: <%= ticker %></span> </c:if> </c:otherwise> </c:choose></div><br/>

15. Create portlet.xml in the src/main/webapp/WEB-INF folder of the project.

16. Add the following content to portlet.xml:<?xml version="1.0" encoding="UTF-8"?><portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0"> <portlet> <portlet-name>StockSearch-Event</portlet-name> <portlet-class>gatein.cookbook.chapter6.StockSearchPortlet</portlet-class>

Chapter 6

187

<supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>Stock Search - Event portlet</title> </portlet-info> <supported-publishing-event> <qname xmlns:gi="http://www.gatein.org/xml/ns/cookbook">gi:watchStockEvent</qname> </supported-publishing-event> </portlet> <event-definition> <qname xmlns:gi="http://www.gatein.org/xml/ns/cookbook">gi:watchStockEvent</qname> <value-type>gatein.cookbook.chapter6.StockEvent</value-type> </event-definition></portlet-app>

17. Create web.xml in the src/main/webapp/WEB-INF folder of the project.

18. Add the following to web.xml:<?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_5.xsd" version="2.5"></web-app>

To create the portlet that will maintain a watch list of stocks, do the following:

19. Create a new Maven project within your IDE, specifying Group ID: gatein.cookbook, Artifact ID: chapter6-event-receiver, and Packaging: war.

20. Create a class called WatchlistPortlet that extends javax.portlet.GenericPortlet within a package called gatein.cookbook.chapter6.

21. Add a private variable to WatchlistPortlet called watchedStocks of type String[] and set it to new String[5].

22. Add a private variable to WatchlistPortlet called watchedCount of type int and set it to 0.

23. Create a method called processWatchStockEvent within WatchlistPortlet with the following content: @ProcessEvent(qname = "{http://www.gatein.org/xml/ns/cookbook}watchStockEvent") public void processWatchStockEvent(EventRequest request, EventResponse response) throws PortletException {

Developing Portlets

188

Event event = request.getEvent(); StockEvent stockEvent = (StockEvent) event.getValue(); String watchStock = stockEvent.getTicker(); if (null != watchStock && watchStock.trim().length() > 0) { if (watchCount > 0) { boolean found = false; for (String stock : watchedStocks) { if (null != stock && stock.equals(watchStock)) { found = true; break; } } if (!found) { watchedStocks[watchCount++] = watchStock + ":" + stockEvent.getName(); } } else { watchedStocks[watchCount++] = watchStock + ":" + stockEvent.getName(); } } }

24. Create a method called displayWatchList within WatchlistPortlet as follows: @RenderMode(name = "view") public void displayWatchList(RenderRequest request, RenderResponse response) throws PortletException, IOException { request.setAttribute("watchlist", watchedStocks); request.setAttribute("watchCount", watchCount); getPortletContext().getRequestDispatcher("/watchlist.jsp").include(request, response); }

25. Create watchlist.jsp in the src/main/webapp folder of the project.

26. Add the following content into watchlist.jsp:<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

<portlet:defineObjects/>

<div class="portlet-section-header">Stock Watchlist</div><br/><div class="portlet-section-body">

Chapter 6

189

<% String[] stocks = (String[])renderRequest.getAttribute("watchList"); Integer watchCount = (Integer)renderRequest.getAttribute("watchCount");%> <c:choose> <c:when test="<%= watchCount > 0 %>"> <table border="1"> <thead class="portlet-table-header"> <tr> <th>Ticker</th> <th>Company</th> </tr> </thead> <tbody class="portlet-table-body"> <% for (String stock: stocks) { if (null != stock) { String[] split = stock.split(":"); %> <tr> <td align="center" class="portlet-table-text"><%= split[0] %></td> <td align="center" class="portlet-table-text"><%= split[1] %></td> </tr> <% } } %> </tbody> </table> </c:when> <c:otherwise> <span>No stocks being watched at the moment.</span> </c:otherwise> </c:choose></div><br/>

27. Create portlet.xml in the src/main/webapp/WEB-INF folder of the project.

Developing Portlets

190

28. Add the following content to portlet.xml:<?xml version="1.0" encoding="UTF-8"?><portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0"> <portlet> <portlet-name>WatchList-Event</portlet-name> <portlet-class>gatein.cookbook.chapter6.WatchlistPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>WatchList - Event portlet</title> </portlet-info> <supported-processing-event> <qname xmlns:gi="http://www.gatein.org/xml/ns/cookbook">gi:watchStockEvent</qname> </supported-processing-event> </portlet> <event-definition> <qname xmlns:gi="http://www.gatein.org/xml/ns/cookbook">gi:watchStockEvent</qname> <value-type>gatein.cookbook.chapter6.StockEvent</value-type> </event-definition></portlet-app>

29. Run the following in the root of the project directory to build the web archive:>mvn clean package

30. Copy the generated web archive, chapter6-event-1.0.0.SNAPSHOT.war, from the target folder into the deployment folder where you unpacked the GateIn installation.

31. Start the server and log in as an administrator.

32. Access the Application Registry, as seen in Chapter 2, Managing Portal Contents Using the GUI, and click on Import Applications to make our portlet available.

33. Create a new page for the StockSearch-Event and WatchList-Event portlets as seen in Chapter 2, Managing Portal Contents Using the GUI.

Chapter 6

191

34. The portlet page should like the following screenshot:

35. Click on Watch! in the row for Robert Half International Inc. stock, and the portal page should like the following screenshot:

Developing Portlets

192

How it works...The majority of the functionality is identical to that defined in the Communicating between portlets using Public Render Parameters recipe, so in this section we will cover the differences with it.

Step 12 creates an event containing the stock ticker and name we want to watch, based on the event class from Step 4. Step 12 creates a QName object to represent the Event name before setting the QName and stock event onto the ActionResponse. Step 16 defines the Event for the portlet, such as the namespace it belongs to and the class representing the object that will be passed on the event.

In Step 4 the StockEvent class was created with the @XmlRootElement, as well as implementing Serializable, enabling the portlet container to pass the event to remote portlets.

Step 23 created a method to process the event object and add the stock to watch into the portlet's list. Step 24 shows a simpler displayWatchList method as it is no longer responsible for handling new stocks to watch, and can simply add the needed data onto the request and dispatch to the JSP.

Step 26 modifies the JSP showing the stocks being watched to now include the company name as well.

Step 28 adds information of the event into the portlet descriptor so the portlet container is aware that this portlet wants to be notified when that event is fired.

See also f The Creating a portlet with the Portlet 2.0 Specification recipe

f The Communicating between portlets using Public Render Parameters recipe

7Developing Using Components API

In this chapter we will cover:

f Getting started with WebUI

f Creating views

f Handling different skins in a portlet

f Adding the JavaScript resources to the portlet

f Handling different locales in a portlet

IntroductionGateIn provides a web framework named WebUI (Web User Interface). This framework is similar to JSF because it is based on components.

Each part of the portal can be addressed to a Java class. Some examples of components are the pagination, the navigation, the login form, the pages, the application itself, the portlets, and the gadgets. GateIn has about 400 components provided by WebUI.

Most of these components work with the JSR 286 portlet framework, so they can leverage the following features:

f Event-based flow

f Components configuration by annotation

f Groovy templates for rendering

f Built-in AJAX support

Developing Using Components API

194

Each portlet is automatically coupled with a WebUI reference, so this can be configured when you want through the XML configuration.

In this chapter, we will see some examples of customization and extension of WebUI components.

WebUI is maintained by the eXo and JBoss communities, and is an internal product of GateIn. More details about this framework can be found here: http://wiki.exoplatform.com/xwiki/bin/view/Portal/WebUI.

Getting started with WebUIIn this recipe, you will see how to add a new configuration to the portlet so that you can use the WebUI extensions.

Getting readyTo extend the portlet, you need to create a new XML configuration file for WebUI, add the path of this file in the init parameters of your portlet.xml file, and modify your portlet class.

How to do it...Here are the basic steps to create a WebUI in an existing project:

1. Create the WebUI file. In the WEB-INF folder of your web application, create a folder named /conf/portlet followed by the name of the web application, then the name of the portlet and the path /webui/configuration.xml: MyApplication.war/WEB-INF/conf/portlet/MyApplication/MyPortlet/webui/configuration.xml

Here is a basic example of configuration.xml: <webui-configuration> <ui-component-config> <type> my.sample.MyPortlet </type> <lifecycle>org.exoplatform.webui.core.lifecycle.UIApplicationLifecycle </lifecycle> </ui-component-config> <application>

Chapter 7

195

<ui-component-root> my.sample.MyPortlet </ui-component-root> <state-manager>org.exoplatform.webui.application.portlet.ParentAppStateManager </state-manager> </application></webui-configuration>

2. Add this configuration in to the portlet.xml file:<portlet-class>org.exoplatform.webui.application.portlet.PortletApplicationController</portlet-class><init-param> <name>webui.configuration</name> <value>/WEB-INF/conf/portlet/MyApplication/MyPortlet/webui/configuration.xml </value></init-param>

3. Finally, you need to extend your portlet with the org.exoplatform.webui.core.UIPortletApplication class as follows:public class MyPortlet extends UIPortletApplication {

...}

Now you are ready to work with WebUI.

How it works...The PortletApplicationController does the initial work. This class extends the JSR 286 GenericPortlet and wraps WebUI behind the configuration done in the portlet.xml.

If you decide to use WebUI, remember that your class cannot extend the javax.portlet.GenericPortlet class directly, as it is used to implement the JSR 286, and neither has the sense to implement the javax.portlet.Portlet interface. You must either use the PortletApplicationController or extend it. The reason is that the eXo framework has no concept of a portlet container. Only recently has the JBoss community added in the container, thus bringing big advantages to the portal.

Developing Using Components API

196

The PortletApplicationController passes the webui.configuration to the internal Configuration Manager of WebUI, which will process the configuration described in the configuration.xml.

In configuration.xml, we have the following two blocks:

f ui-component-config: This is used to configure the specific component. You can add several components to the application. In the example, the Configuration Manager assigns a lifecycle to the component MyPortlet. The lifecycle acts on the component and executes some of the component's operations, as will be seen in the next paragraph.

f application: This manages the whole application. In the example, the application is represented by the same component MyPortlet because MyPortlet extends the UIPortletApplication class, which is an extension of the UIContainer. It's possible to use a different application that will contain other components.

In the example, a State Manager is assigned to the application. The State Manager is mandatory in the configuration and manages the saving and the loading of the state of the components. The State Manager provides three operations. They are described in the main class, org.exoplatform.webui.application.StateManager:

abstract public class StateManager{ abstract public UIApplication restoreUIRootComponent(WebuiRequestContext context) throws Exception;

abstract public void storeUIRootComponent(WebuiRequestContext context) throws Exception;

abstract public void expire(String sessionId, WebuiApplication app) throws Exception;}

f The restore operation loads the component from a store and maintains it in memory.

f The store operation is used to save the configuration of the component along with the information of the user who reads it in the HTTP session.

f By default, the expire operation is never used, but it would be used to force the deletion of the component from the HTTP session. Only a custom State Manager can use it.

The State Manager is hierarchic. It will be used by all components and parent components. It is due to the State Manager that you will see the correct representation of the components in the page.

Chapter 7

197

There are two implementations of State Manager:

org.exoplatform.portal.application.PortalStateManager

org.exoplatform.webui.application.portlet.ParentAppStateManager

The ParentAppStateManager is a wrapper for the PortalStateManager. Always set the ParentAppStateManager method in your configuration. Since this class is a wrapper, you can use this class in GateIn 3.2, 3.3, 3.4, and so on.

There's more...You will now see some details of the base configuration seen in the preceding paragraph.

Choosing the right extension classMany components are provided by WebUI, so you will see a list of the main components to use:

f org.exoplatform.portal.webui.application.UIPortlet: Represents a portlet. It is not a JSR 286 portlet, but it provides all the features.

f org.exoplatform.portal.webui.portal.UIPortal: Represents a portal. It manages information such as default skin, public parameters, edit permissions, navigation path, and default locale.

f org.exoplatform.portal.webui.workspace.UIPortalApplication: Represents a generic application inside the portal. It manages the information of the current portal where it is installed.

f org.exoplatform.webui.core.UIPortletApplication: Represents the base portlet. It excludes many features with respect to the UIPortlet.

f org.exoplatform.webui.core.UIComponentDecorator: Represents individual components that can be integrated in the pages or in the application. Examples of extensions include the pop-up window, the page body, the graphic panel, and so on.

f org.exoplatform.webui.form.UIForm: Represents a generic form. With it, you can configure the parameters, the HTTP method, the encoding, the action, and the events.

f org.exoplatform.webui.core.UIContainer: Represents the container used by the portlet and by the other applications. Basically, it implements a tree containing other UI components represented as children. The UIForm is a container because it contains form parameters.

Developing Using Components API

198

These are the basic components in WebUI. As there are numerous components available in WebUI, it is difficult to cover all of them in this book; however, we will consider some of them. A better way to know them all is to use a Java development tool and find all extensions of the org.exoplatform.webui.core.UIComponent class. By using this method, you will find the component you need

Using the annotation instead of the configuration fileInstead of the XML file, you can use Java annotations in your class. Each XML tag in WebUI uses respective annotation. For example, instead of ui-component-config you can use the ComponentConfig annotation. This is the respective configuration:

@ComponentConfig( type = MyPortlet.class, lifecycle = UIApplicationLifecycle.class)public class MyPortlet extends UIPortletApplication {

...}

Here is an example of multiple annotated configurations:

@ComponentConfigs({ @ComponentConfig(...), @ComponentConfig(...)})

The order of execution is done as first by the annotation, and then by the XML configuration. If the XML is not present, the valid configuration will be done by the annotation. If both are present, the XML will rewrite the present annotations, or it will add new tags if present only in the annotation. It is good practice to use only one type to avoid confusion.

Choosing the right lifecycleIf you use the ui-component-config tag, you must set the lifecycle.

GateIn provides a wide set of lifecycles for your application. Basically, you have a ClassCastException if you don't set the right lifecycle, so take care when you set a lifecycle. Each lifecycle is written for the type of component that your component extends.

These are the basic methods for the lifecycle:

public class Lifecycle<E extends UIComponent>{ ... processDecode(...)

Chapter 7

199

// generic operations of decode of the outputStream

... processAction(...) // processor for the actions. For example can read the http url and pass the parameters to the action class

... processRender(...) // executes the rendering of the page, for example adding custom html div or javascript contents

... renderTemplate(...) // executes the rendering of a view loading a groovy template through a templating service

}

Each component has a proper mode to execute these operations.

In the following, you can see the relative components for the page, portal, form, portlet, application, and container. Simply choose the lifecycle according to the type of component you write:

f org.exoplatform.portal.webui.application.UIPortletLifecycle<S, C, I>

f - org.exoplatform.portal.webui.application.UIPortlet

f org.exoplatform.portal.webui.page.UIPageLifecycle

f - org.exoplatform.webui.core.UIComponent

f org.exoplatform.portal.webui.portal.UIPortalLifecycle

f - org.exoplatform.portal.webui.portal.UIPortal

f org.exoplatform.portal.webui.workspace.UIPortalApplicationLifecycle

f org.exoplatform.portal.webui.workspace.UIPortalApplication

f org.exoplatform.webui.core.UIComponentDecoratorLifecycle

f - org.exoplatform.webui.core.UIComponentDecorator

f org.exoplatform.webui.core.lifecycle.UIFormLifecycle

f - org.exoplatform.webui.form.UIForm

f org.exoplatform.webui.core.lifecycle.UIContainerLifecycle

f - org.exoplatform.webui.core.UIContainer

f org.exoplatform.webui.core.lifecycle.UIApplicationLifecycle

f - org.exoplatform.webui.core.UIPortletApplication

Developing Using Components API

200

The UIPageLifecycle provides a simple rendering for each WebUI component. It is a very generic class and it can be used by each extension of UIComponent.

Creating viewsIn the previous example, the portlet doesn't have a view, so if you install it, you will see nothing in the page of the portal. In this recipe, you will see the complete configuration and creation of the views for the portlet.

Getting readyAdd a new view for the portlet using Groovy, WebUI's preferred scripting tool. With Groovy, it is very simple to handle the WebUI components. Therefore, we need to add a new component to handle it in the view.

How to do it...Carry out the following steps:

1. Create a file MyPortlet.gtmpl inside the path MyApplication.war/groovy/MyApplication/webui/component:<%

def rcontext = _ctx.getRequestContext() ;%><div id="$uicomponent.id" class="MyPortlet"> Started!!!</div>

2. Add the Groovy template in the configuration.xml: <ui-component-config> <type> my.sample.MyPortlet </type> <lifecycle>org.exoplatform.webui.core.lifecycle.UIApplicationLifecycle </lifecycle> <template>app:/groovy/MyApplication/webui/component/MyPortlet.gtmpl </template> </ui-component-config>

Chapter 7

201

Or in the annotation:@ComponentConfig( type = MyPortlet.class, lifecycle = UIApplicationLifecycle.class, template = "app:/groovy/MyApplication/webui/component/MyPortlet.gtmpl")public class MyPortlet extends UIPortletApplication {

...}

The template tag sets the default view for the portlet. If you install the portlet in the main page under the HomePortlet, you will see something similar to the following screen:

3. Now write a more complex component. In the portlet class, add before some Component as annotation:@ComponentConfigs({ @ComponentConfig( lifecycle = UIApplicationLifecycle.class, template = " app:/groovy/MyApplication/webui/component/MyPortlet.gtmpl " ), @ComponentConfig(

type = UIPortalNavigation.class, id = "MyUISiteMap")})public class MyPortlet extends UIPortletApplication {

...}

Developing Using Components API

202

The same thing can be done through XML: <ui-component-config> <type> my.samples.MyPortlet </type> <lifecycle>org.exoplatform.webui.core.lifecycle.UIApplicationLifecycle </lifecycle> <template> app:/groovy/MyApplication/webui/component/MyPortlet.gtmpl </template> </ui-component-config> <ui-component-config id="MyUISiteMap"> <type>org.exoplatform.portal.webui.navigation.UIPortalNavigation </type> </ui-component-config>

In this example, you have two configurations: one is the example showed before, and for the second, we declare a new component, MyPortlet, through a WebUI component, the UIPortalNavigation, the component used for the navigation of the portal.

4. Now we have to add the UIPortalNavigation component as a child inside the application:public MyPortlet() throws Exception {

// Take the current PortletRequest PortletRequestContext context = (PortletRequestContext) WebuiRequestContext .getCurrentInstance(); PortletRequest prequest = context.getRequest();

/* Take the portlet preferences and find a preference called template. If it doesn't exist add the default template UISitemapTree.gtmpl. It implements a navigable tree structure */

PortletPreferences prefers = prequest.getPreferences(); String template = prefers.getValue("template", "system:/groovy/webui/core/UISitemapTree.gtmpl");

/* Add the UIPortalNavigation as a child of the

Chapter 7

203

application and set two properties */

UIPortalNavigation uiPortalNavigation = addChild( UIPortalNavigation.class, "MyUISiteMap", null);

uiPortalNavigation.setTemplate(template); uiPortalNavigation.setUseAjax(false);

/* Set in memory the informations present in the tree. In this code are memorized the first 2 levels of the tree */

uiPortalNavigation.setScope(GenericScope.treeShape(2)); }

5. Rewrite the Groovy page MyPortlet.gtmpl so that we can call the new inserted component:<% import org.exoplatform.portal.webui.navigation.UIPortalNavigation; def uiPortalNavigation = uicomponent.getChild(UIPortalNavigation.class); uiPortalNavigation.loadTreeNodes(); uicomponent.renderChildren();%>

Through the renderChildren() method, the component will be rendered for all its children. Each component will query their template and will provide their own graphic part to the page. Here is the result:

Of course, it is still a crude window. You will see how to decorate it better in the Handling different skins in a portlet recipe.

Developing Using Components API

204

How it works...The Groovy scripts are elaborated by a service, the Template Service. This service is loaded in the file $PORTAL_ROOT/WEB-INF/conf/common/common-configuration.xml:

<component> <type>org.exoplatform.groovyscript.text.TemplateService </type></component>

It is based on Groovy, a highly efficient scripting language used for the view.

As with most components, the Template Service starts during the loading of the page that uses Groovy scripts. It manages an internal cache where it preserves the bytes and compiles the pages.

The Groovy scripts can be declared with different schemas. Each schema responds to a different resource resolver. The resource resolver searches and loads the resources. Here are the available schemas:

f system: All scripts inside the portal root application. They are resolved by the org.exoplatform.resolver.ServletResourceResolver. It finds the resources in the ServletContext.

f classpath: The scripts that will be loaded through classpath. They are resolved by the org.exoplatform.resolver.ClasspathResourceResolver.

f app: All scripts inside the custom application. They are resolved by the org.exoplatform.resolver.PortletResourceResolver.

f par: The scripts that are in a portlet application. They are resolved by the org.exoplatform.resolver.PortletResourceResolver. It finds the resources in the PortletContext.

f war: All scripts inside a web application. They are resolved by the org.exoplatform.resolver.ServletResourceResolver.

f jar: All scripts inside a jar application. They are resolved by the org.exoplatform.resolver.FileResourceResolver.

f file: All scripts inside an external directory of the file system. They are resolved by the org.exoplatform.resolver.FileResourceResolver.

The Template service doesn't need configuration, unless you use different Groovy compiler settings, which you can change only by directly working on the class.

Chapter 7

205

Notate the uicomponent key in the Groovy script. It allows loading the current UI component application that you have declared in the configuration.xml:

<application> <ui-component-root> my.sample.MyPortlet </ui-component-root> ...

The other keys available for the Groovy scripts can be found in:

f orientation, dlr, isRT, isLT: These represent orientation of the text written in the Groovy template. It can be LT or RT according the nationality. For example, some nations write from right to left.

f locale: The nationality, for example en.

f nodeurl: The URL of the uicomponent represented as org.exoplatform.web.url.navigation.NodeURL instance.

f _ctx: The map containing all showed variables.

f portletContent: This can be used only if your component uses the UIPortletLifecycle. It contains the text inside the portlet. It is represented by the org.exoplatform.commons.utils.Text instance, and is very useful for AJAX or REST calls.

These variables are present in the processRender and renderTemplate methods seen in the WebUI lifecycle that you have configured in the portlet. The processRender method adds these variables in memory (except for the locale) so that they can be used in the page. The renderTemplate adds the locale to the GroovyContext and passes it to the Template Service.

If you need more details on how this is done in Groovy, you can read the official documentation at the following URL:http://groovy.codehaus.org/Documentation

There's more...In this recipe, we introduced the concept of a service. You have seen the first service in the previous chapter with the OrganizationService. All services in GateIn can be easily identified/recognized as they all end with the name Service.

All the services are declared in the portal root application, but they can be added in other portal applications. The convention for a configuration is to have the name of the service followed by –configuration.xml; for example, common-configuration.xml.

Developing Using Components API

206

The configuration system is based on Pico Container, a framework similar to Spring used to instantiate objects through the XML configuration. Details for Pico Container can be seen here: http://picocontainer.org.

A service can be loaded inside any UIComponent. For example, in MyPortlet, we can call the Template Service as follows:

PortletRequestContext context = (PortletRequestContext) WebuiRequestContext .getCurrentInstance(); ExoContainer pcontainer = context.getApplication().getApplicationServiceContainer(); TemplateService service = (TemplateService)pcontainer.getComponentInstanceOfType(TemplateService.class);

Or simply:

TemplateService templateService = this.getApplicationComponent(TemplateService.class)

Or by Groovy:

def templateService = uicomponent.getApplicationComponent(TemplateService.class);

GateIn provides many services so it is not possible to write about all of them. We will explain the main services in the next recipes for dialed argument.

Template Statistic ServiceAnother service tied to TemplateService is the TemplateStatisticService. It is used to monitor the execution time of a Groovy script. Here are the signatures of the methods:

f getTemplateList()

f getMaxTime(String id_template)

f getMinTime(String id_template)

f getExecutionCount(String id_template)

f getAverageTime(String id_template)

f getSlowestTemplates()

f getMostExecutedTemplates()

f getFastestTemplates()

See also f For more information on Configuring the Organization Service refer to Choosing the

JAAS modules recipe in Chapter 5, Securing Portal Contents

Chapter 7

207

Handling different skins in a portletEach WebUI component has the proper graphic template, JavaScript codes, and skin, so it's very simple to assign a different configuration for a single component. The same portal is managed by WebUI so it has configurable skins, JavaScript, and template too. In this recipe, you will see how to add a skin to a component inside the portal.

Getting readyYou will start with the previous example. You will add a new skin to the MyPortlet example by adding a new configuration file. The skin contains CSS files and images. For convenience, you will use the existing skin for the UISiteMap component.

How to do it...Carry out the following steps to add a new skin to the project:

1. Create the gatein-resources.xml in the folder MyApplication.war/WEB-INF/conf:<portlet-skin> <application-name> MyApplication </application-name> <portlet-name>MyPortlet</portlet-name> <skin-name>Default</skin-name> <css-path>/skin/portal/webui/component/My/DefaultStylesheet.css </css-path></portlet-skin>

This file activates a service, the Skin Service, which registers the styles for the portlet signed in the portlet-name of the application signed in the application-name. The deployment of the gatein-resources.xml generates events captured by different deployers. In the case of the skin, the GateInSkinConfigDeployer captures the events and passes the skins to the Skin Service to register.

2. It is important in this service to use the tag display-name of the web.xml file. You are forced to use the name of the application; otherwise, you will receive an exception:<display-name>MyApplication</display-name>

Developing Using Components API

208

3. You need also to add a filter in your WEB-INF/web.xml file, which interrogates the Skin Service and renders dynamically the respective CSS 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>/*</url-pattern></filter-mapping>

4. Create the /skin/portal/webui/component/My folder where we will put the style file with the images. For convenience, copy the contents of the main portal application folder: web/skin/portal/webui/component/UISiteMap/.

5. The skin is called by the views through the CSS class or ID. It is good practice to create the CSS classes with the name of the WebUI component. If you open the current DefaultStyleSheet.css, you will see the defined CSS class UISitemap. Rename all definitions with the name of your new component, My.

6. Now you need to call the CSS class in your view. For this, use a new Groovy template. The previous example used was the UISitemapTree.gtmpl template. We can rewrite it and pass it the new skin. To achieve this, create a new folder in your application using the same path of the folder as of the main portal application. In this folder, the main portal application has several groovy pages: /groovy/webui/core, and copy the existing UISitemapTree.gtmpl from the $PORTAL_ROOT/groovy/webui/core folder.

7. The only thing to do in the file is to point the main div to the new CSS class so:<div id="$uicomponent.id" class="My" > <div class="ClearFix"> <div class="CollapseAll FloatLeft" onclick="$actionCollapseAll"> <%=_ctx.appRes(siteMapPortlet.getName() + ".label.CollapseAll")%> </div> </div> <div class="SitemapContent">

Chapter 7

209

<div class="UISiteTree"> <% TreeNode treeNodeRoot = uicomponent.getTreeNodes() ;%> <% renderNodes(treeNodeRoot,nodeURL,useAJAX); %> </div> </div></div>

8. In the previous example, you have called this Groovy script dynamically from the constructor of the portlet. Actually, it calls the script from the system path, so the new script doesn't override it. To allow the override, you have to change the schema from system to app in the following way:

� app:/groovy/webui/core/UISitemapTree.gtmpl instead of system:/groovy/webui/core/UISitemapTree.gtmpl.

Here is the result:

The remaining steps will allow you to modify the skin from the web console.

9. Click on the GateIn logo button and click on Change Skin.

Developing Using Components API

210

10. From the resultant window, select one of the two available skins; for example, we will select the Simple skin.

This is the result:

How it works...A new configuration file is shown here, the gatein-resources.xml. The following screenshot shows the root configuration:

Three Deployers catch this file:

f org.exoplatform.web.application.javascript.JavascriptConfigDeployer: This reads the configuration of the JavaScript. It will be exposed in the next recipe.

f org.exoplatform.portal.resource.GateInSkinConfigDeployer: This reads the configuration of the skin.

f org.exoplatform.portal.resource.GateInSkinConfigRemoval: Removes the configuration of the skin from the context

Chapter 7

211

GateInSkinConfigDeployer takes the skin configuration and processes it. If the validation is ok, it passes the skin to the Skin Service.

The Skin Service is provided by the org.exoplatform.portal.resource.SkinService class. It maintains all the skin configurations in a registry, categorized by the skin ID.

There are two types of skins in the default portal: the Portal skin and the Portlet skin. The only difference between them is that the Portlet skin is categorized by application ID and portlet ID. You can set a skin for a portlet or for the whole application.

The Portal skin is categorized by a portal ID. It makes no sense for a portlet to use a Portal skin because the graphic is completely different, but if you would do it, you simply need to declare the Portal skin as Portlet skin in the gatein-resources.xml.

The ResourceRequestFilter has a big role. This filter receives the URL of the resources requested by pages and renders them. The resources can be CSS, images, or JavaScript. For the rendering of the CSS, the renderCSS method of the Skin Service is used.

The URL of the CSS resource is calculated through the parameters declared in the skin configuration of the gatein-resources.xml in the following way:

application-name + css-path

Moreover, orientation is added in the CSS, –lt or –rt according the used language. For the example, the URL of the skin is:

/MyApplication/skin/portal/webui/component/My/DefaultStylesheet-lt.css

The skin-name tag in the gatein-resources.xml is very important. It links the portlet to the Portal skin. It's important to know the Portal skin when you add a new Portlet skin. If the skin-name is not an existing Portal skin, you won't see the results declared in your custom Portlet skin.

The list of the available Portal skins can be found in the WEB-INF/gatein-resources.xml file of the applications. Each application can rewrite them or add new skins through a new gatein-resources.xml file. You have two Portal skins:

eXoResources/WEB-INF/gatein-resources.xml:

<portal-skin> <skin-name>Default</skin-name> <css-path>/skin/Stylesheet.css</css-path> <css-priority>0</css-priority></portal-skin>

Developing Using Components API

212

gatein-sample-skin/WEB-INF/gatein-resources.xml:

<portal-skin> <skin-name>SimpleSkin</skin-name> <css-path>/skin/Stylesheet.css</css-path> <css-priority>0</css-priority></portal-skin>

There's more...Here we extend some discussions from earlier.

DeployersThe deployers in GateIn are event listeners. They extend the org.gatein.wci.WebAppListener interface as follows:

public interface WebAppListener{

/** * Signal a web application event to the listener. * * @param event the web application event */ void onEvent(WebAppEvent event);}

The received events can be:

f ADDED: The new resource is added

f REMOVED: The resource is undeployed

The WCI passes the events to the deployers.

The other deployer is the org.gatein.pc.portlet.impl.deployment.PortletApplicationDeployer. All portal applications are intercepted by this deployer and installed by assigning a lifecycle.

The window styleAnother part of the gatein-resources.xml is the Window Style. It represents the graphic assigned to the windows of the portal. If you go to the Edit Page window and select a portlet and click on the Edit Portlet button, you can see on the left the default Window Styles available for the portlet:

Chapter 7

213

The configuration and the use are very similar to the skin. GateIn supports many Window Styles configured in the exoResources application in the file exoResources/WEB-INF/gatein-resources.xml. Here is one example of the Simple window style:

<window-style> <style-name>Simple</style-name> <style-theme> <theme-name>SimpleBlue</theme-name> </style-theme> <style-theme> <theme-name>SimpleViolet</theme-name> </style-theme> <style-theme> <theme-name>SimpleOrange</theme-name> </style-theme> <style-theme> <theme-name>SimplePink</theme-name> </style-theme> <style-theme> <theme-name>SimpleGreen</theme-name> </style-theme></window-style>

If you open exoResources/skin/Stylesheet.css, there is an import of the images folder of the Window Styles:

@import url(PortletThemes/Stylesheet.css);

Developing Using Components API

214

Each Window Style contains a set of themes that can be chosen by the web console for a single window. Here is an example of a CSS class declaration of the SimpleBlue theme in the PortletThemes/Stylesheet.css:

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

The styles take part in the decoration process. By default, no window uses the styles.

The configuration of the decoration can be done through a web console or through an XML file with the pages.xml file. In this configuration, you can declare all the pages of the portal and its properties so they will be created automatically on the first boot of the portal. See the properties of the home page in the configuration of the root portal in the $ROOT_PORTAL/WEB-INF/conf/portal/portal/classic/pages.xml:

<page> <name>homepage</name> <title>Home Page</title> ... <portlet-application> <portlet> <application-ref>web</application-ref> <portlet-ref>HomePagePortlet</portlet-ref> ... </portlet> <title>Home Page portlet</title> <access-permissions>Everyone</access-permissions> <show-info-bar>false</show-info-bar> <show-application-state>false</show-application-state> <show-application-mode>false</show-application-mode> </portlet-application> </page>

This file creates a page and associates an existing portlet to it.

We have three properties here:

f show-info-bar: Shows the base window.

f show-application-state: Basically, this shows the buttons for minimizing and maximizing the portlet.

f show-application-mode: Shows the list of modes for the portlet, for example, the help page, edit page, and view page. Click inside to select the preferred mode.

If you set the three properties to true, you will see the default theme on the portlet.

Chapter 7

215

The default theme is declared directly in the Stylesheet.css file through the section DefaultTheme. There is no way to choose the theme through XML.

Carry out the following steps to choose it through the web console:

1. Choose a page, for example the SiteMap, and select Edit Page as shown in the following screenshot:

2. Click on Edit Portlet:

Developing Using Components API

216

3. On the Portlet Setting tab, flag all three checkboxes so you can see the whole window:

4. In Decoration Themes, choose the window style, for example Mac Style.

Chapter 7

217

5. Click on Save and Close and then on the Finish button.

Here is the result:

Notate the portlet modes in the pop-up and the two buttons for minimize and maximize at the right.

See also f The Adding the JavaScript resources to the portlet recipe

Developing Using Components API

218

Adding the JavaScript resources to the portlet

In this recipe, you will add the JavaScript code to add functionalities to a portlet. You will see how to assign JavaScript functions to a portlet.

Getting readyLook at the previous example. If you click on an empty node of the tree after the second level, for example, on the left of the Application Registry node, you will see that nothing happens. Remember that you have configured, in the Creating views recipe, the in-memory state for a maximum of two levels. Therefore, you need to add the functions to collapse and update the tree of the pages for the levels that are not in memory.

How to do it...1. First, declare the JavaScript module in the gatein-resources.xml:

<javascript> <param> <js-module>eXo.webui.Mycomponent</js-module> <js-path> /javascript/eXo/webui/Mycomponent.js </js-path> </param></javascript>

This module will be registered in the portal page before the rendering operation.

2. Add the JavaScript file in the declared path MyApplication.war/javascript/exo/webui/Mycomponent.js. Here is a basic example:Mycomponent.prototype.updateTreeNode = function (nodeToUpdate, getNodeURL) { ...};eXo.webui.Mycomponent = new Mycomponent();

The JavaScript updateTreeNode function is triggered when you click on a node of the tree because it is started by the Groovy page seen before, UISitemapTree.gtmpl.

In this method, you have access to all eXo scripts. eXo counts about 200 scripts. Useful utilities include the operations for the keyboard, the pop-ups, the upload utilities, the tool bar, the management of the contents, and drag-and-drop. You are free to use eXo JavaScripts or some other framework, such as prototype or jquery.

Chapter 7

219

As for the CSS, if it is still not done, you need to activate the filter for the resources, as shown in the Handling different skins in the portlet recipe.

3. Now you can add the following code to your portlet class (my.sample.Myportlet in the Getting started with WebUI recipe) or Groovy script of your portlet:PortalRequestContext pcontext = Util.getPortalRequestContext(); JavascriptManager jsmanager = pcontext.getJavascriptManager();jsmanager.importJavascript('eXo.webui.JSPHelloUser') ;

It will load the functions on the page.

How it works...JavascriptConfigDeployer waits for the JavaScript module declared in the gatein-service.xml when the application is deployed.

It passes the module to the JavascriptConfigService that registers it in an internal cache.

This service maintains the following information about the modules:

f portal-name: The name of the portal where the JavaScript is loaded. The WebUI framework always declares the JavaScript at the portal level. The JavaScripts are always declared in the head of the page.

f js-path: The path of the file where the JavaScript code is located.

f js-priority: The priority of loading the module. If 0, it will be loaded last, with higher numbers loaded before lower numbers.

The org.exoplatform.web.application.JavascriptManager is the interface that interacts with the scripts. Here are the main operations:

f importJavascript (module_id): Imports the module inside the Manager Service. It can be called at any part of the portal, passing the ID of the module configured in the gatein-resources.xml.

f writeJavascript (Writer writer): This is called by GateIn to append the requests of the JavaScript modules. They are written at the end of the page. The page that calls this method is UIPortalApplicationChildren.gtmpl. It is inside the $PORTAL_ROOT/groovy/portal/webui/workspace folder.

Developing Using Components API

220

f writeCustomizedOnLoadScript (Writer writer): This is called by an internal Groovy page of GateIn. The UIPortalApplicationChildren.gtmpl is used to append the custom JavaScript calls. Some pages would add an onLoad function passing a parameter. GateIn offers this method to use the classic onload event in a page. This eXo method allows to order the onload functions in the pages. Here is an example to import JavaScript code:javascriptmanager.addCustomizedOnLoadScript( 'eXo.portal.UIAdminToolbar.onLoad( "' + uicomponent.id + '" );');

They are written at the end of the page too.

In the example seen earlier, you have these declarations as the last result:

<script type="text/javascript" src="/MyApplication/javascript/eXo/webui/Mycomponent.js"></script>...eXo.require(', eXo.webui.Mycomponent, ');

The UIApplication.gtmpl page loads the first row. This page is located in the $PORTAL_ROOT/groovy/portal/webui/workspace folder; it represents the default layout of the portal. It calls the Javascript Config Service and takes all the available JS paths at the head of the page.

The eXo.require row is written by the writeJavascript method. This is a JavaScript function used to find the JS modules that are not declared in the gatein-resources.xml file. It automatically searches for the JS modules in the eXoResources path, and if it finds the file .js with the same name, it is cached on the client side. An example is the eXo.portal.UIAdminToolbar module.

Handling different locales in a portletAccording to the JSR 286, Portlet Specification, each portlet manages the internationalization, also called i18n. In this recipe, you will see what WebUI adds to the i18n of a portlet and how the services work.

Getting readyYou need Version 3.2.0 of GateIn working on Tomcat or on JBOSS, the sample application, and two Resource Bundles to test the localization.

Chapter 7

221

How to do it...Start to add an internationalization sample to an existing project:

1. Create two Resource Bundles in your sample application. The conventional name of the folder for the Resource Bundle of a portlet is: $APPLICATION_NAME/WEB-INF/classes/locale/portlet/$APPLICATION_NAME/$PORTLET_NAME_xx.properties.

Write a double APPLICATION_NAME because you can write a Resource Bundle for an application and store it in another application. An example is the PORTAL_ROOT application that stores all Resource Bundles for other applications.

XX represents one of the names of the locales available in $PORTAL_ROOT/WEB-INF/conf/common/locales-config.xml.

2. Add to the MyPortlet_en.properties file in the MyApplication/WEB-INF/classes/locale/portlet/MyApplication folder:word1=Testword2=Test2

3. Add also to the MyApplication/ WEB-INF/classes/locale/portlet/MyApplication/MyPortlet_it.properties file:

word1=Provaword2=Prova2

4. Add the chosen locale, in this case the Italian language, and the name of the Resource Bundle file in portlet.xml:<resource-bundle> locale.portlet.MyApplication.MyPortlet</resource-bundle>

We can call the Resource Bundle from the portlet:import java.util.Locale;import java.util.ResourceBundle;import org.exoplatform.webui.application.*;import org.exoplatform.webui.application.portlet.*;...public class MyPortlet extends UIPortletApplication {

public MyPortlet() { ... PortletRequestContext context = (PortletRequestContext) WebuiRequestContext .getCurrentInstance(); Locale locale = context.getLocale();

Developing Using Components API

222

ResourceBundle resourceBundle = context.getApplicationResourceBundle(); String word = resourceBundle.getString("word1"); ... }}

Alternatively, we can call the Resource Bundle from the Groovy script as follows:<%= _ctx.appRes("word1"); %>

Now you need to test the Resource Bundle, changing the language for the demo user.

5. Log in as root and go to Users and groups management:

6. Edit the demo user information. Select Italian from the Language drop-down for the demo user as shown in the following screenshot:

Chapter 7

223

7. Now sign off, sign in as demo user, and go to the custom portlet where you have added the row in the Groovy script. You will see the value Prova.

How it works...All supported languages are declared in the locales-config.xml file inside $PORTAL_ROOT/WEB-INF/conf/common.

The Locale Service represented by the org.exoplatform.services.resources.impl.LocaleConfigServiceImpl class registers this information in memory when the portal starts.

This is the configuration of the Locale Service:

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

This is the English configuration in the locales-config.xml:

<locale-config> <locale>en</locale> <output-encoding>UTF-8</output-encoding> <input-encoding>UTF-8</input-encoding> <description> Default configuration for english locale </description></locale-config>

The first locale in the file is automatically the default, unless it is configured manually.

Developing Using Components API

224

When a page loads initially, the portal finds and registers the property lang of the HttpServletRequest as locale client-side and the locale server-side in the Portal Context. The default server-side in the Portal Context is the English.

The page requests the default locale from the Portal Context, and interrogates the Locale Config Service and sets all the locale properties for the page.

A Localization Lifecycle starts when the HttpRequest is processed. This is a listener configured in the file $PORTAL_ROOT/WEB-INF/webui-configuration.xml:

<application-lifecycle-listeners> ... <listener>org.exoplatform.portal.application.localization.LocalizationLifecycle </listener> </application-lifecycle-listeners>

Here are the events, which are managed by the Localization Lifecycle from the org.exoplatform.web.application.ApplicationLifecycle interface:

public void onInit(Application app)public void onStartRequest(Application app, E context)public void onFailRequest(Application app, E context, RequestFailure failureType)public void onEndRequest(Application app, E context)public void onDestroy(Application app)

Here is the code from the org.exoplatform.web.application.ApplicationRequestPhaseLifecycle interface:

/** * Perform any processing required at the beginning of {@link Phase#ACTION} or {@link Phase#RENDER} phase. * @param app Application * @param context current RequestContext * @param phase starting phase */ public void onStartRequestPhase(Application app, E context, Phase phase);

/** * Perform any processing required at the end of {@link Phase#ACTION} or {@link Phase#RENDER} phase. * @param app Application * @param context current RequestContext * @param phase ending phase */ public void onEndRequestPhase(Application app, E context, Phase phase);

Chapter 7

225

The Localization Lifecycle creates LocalizationContext containing all local information from the browser, the request, the session, the portal, and the user profile.

The Localization Lifecycle will calculate the current locale through a policy mechanism.

The org.exoplatform.portal.application.localization.DefaultLocalePolicyService class represents the default locale policy.

It calculates the current locale according to whether the user is registered or anonymous. It decides whether to give priority to the session, to the request, or to the portal configuration locale.

As the first step, it verifies if a registered or anonymous user is logged on. If there is a registered user, it searches the current locale in the cookies, in the session, and then in the request. If the user is anonymous, the control is done only in the request.

The policy is customizable. Here is the default configuration taken from the $PORTAL_ROOT/WEB-INF/conf/portal/web-configuration.xml file:

<component> <key> org.exoplatform.services.resources.LocalePolicy </key> <type>org.exoplatform.portal.application.localization.DefaultLocalePolicyService </type></component>

Once the default locale is calculated, the Resource Bundles will always use it for the rendering of the words.

There's more...See now how the Resource Bundle works.

Resource Bundle ServiceThe Resource Bundle is a Java object (java.util.ResourceBundle) used to register localized phrases and words callable through keys. GateIn provides a Resource Bundle Service that is represented by the class org.exoplatform.services.resources.impl.BaseResourceBundleService. It is tied to the Locale Service because each Resource Bundle has the proper path according to the used locale.

Developing Using Components API

226

It registers in memory a set of files ending with the notation –xx, where xx represents the locale declared in the local-config tag of the locales-config.xml. The list of files that are loaded in memory can be found in the configuration of the Resource Bundle Service, in the same common-configuration.xml:

<component> <key>org.exoplatform.services.resources.ResourceBundleService </key> <type> org.exoplatform.services.resources.impl.SimpleResourceBundleService </type> <value>locale.portal.expression</value> <value>locale.portal.services</value> ...

Each string present in the value tag represents a folder. For example, in the folder $PORTAL_ROOT/WEB-INF/classes/locale/portal, you have a set of Resource Bundles as:

All Resource Bundles are cached through an internal mechanism provided by eXo.

A part of the expression_it.properties:

word.cancel=Annullaword.category=Categoriaword.change=Cambiaword.city=Citt\u00E0word.close=Chiudiword.comment=Commentoword.content=Contenutoword.country=Nazione

8Migrating from

Existing Portals

In this chapter, we will cover:

f Migrating a transactional portlet

f Migrating an authenticated portlet

f Migrating a portlet that uses JCR

f Importing a skin from an existing website

IntroductionDue to the JCP (Java Community Process) specifications, mainly JSR 286, JSF, and the 329 Portlet Bridge, a portlet can be moved to different portals and application servers.

Each portal has its own extensions in order to deliver better features and greater performance.

In this chapter, we will look at the process through which portlets can be migrated, using simple examples.

Remember, in order for an enterprise application to be highly portable it is important to use Java APIs defined by the Java specifications. Only when you are sure that your application will never be migrated should you decide to use the extensions.

Migrating from Existing Portals

228

Migrating a transactional portletTransactions are an important part of applications. The standard specification for transactions in Java EE is JTA (Java Transaction API). All full-profile Java EE-certified servers implement it. Tomcat and Jetty are not fully Java enterprise certified, so they don't strictly implement JTA. Because of this, GateIn is bundled with a transaction service that loads the JBoss transaction manager that is bundled in GateIn. All portals need a transaction service, and this can be called in different manners.

Getting readyIn this recipe, you will see how to migrate a transactional portlet from JBoss Portal to GateIn.

JBoss Portal is the old portal of the JBoss community distributed by Red Hat until EPP (Enterprise Portal Platform) 4, before the union with the eXo Portal. GateIn can be considered as the new version of JBoss Portal because it integrates the portlet container of JBoss Portal, but the portlet extensions and the administration tools are completely new.

How to do it...1. JBoss Portal uses an extension file where the transactional mode for a portlet is

declared. Here is an example:<?xml version="1.0" standalone="yes"?><!DOCTYPE portlet-app PUBLIC "-//&PRODUCT;//DTD JBoss Portlet 2.6//EN" "http://www.jboss.org/portal/dtd/jboss-portlet_2_6.dtd"><portlet-app> <portlet> <portlet-name>MyPortlet</portlet-name> <trans-attribute>Required</trans-attribute> </portlet></portlet-app>

2. In GateIn, the transaction mode is not declarative. It must therefore be put in the code of the portlet. Here is an example:import javax.transaction.SystemException;import javax.transaction.Transaction;import javax.transaction.TransactionManager;import javax.transaction.TransactionRequiredException;

import org.exoplatform.container.ExoContainer;

Chapter 8

229

import org.exoplatform.portal.application.PortalRequestContext;import org.exoplatform.webui.application.WebuiRequestContext;import org.jboss.cache.transaction.TransactionManagerLookup;...@Overrideprotected void doDispatch(RenderRequest request, RenderResponse response) throws PortletException, IOException { PortalRequestContext context = (PortalRequestContext) WebuiRequestContext.getCurrentInstance(); ExoContainer pcontainer = context.getApplication() .getApplicationServiceContainer(); TransactionManagerLookup tsLookup = (TransactionManagerLookup) pcontainer .getComponentInstanceOfType(TransactionManagerLookup.class); TransactionManager tManager; Transaction transaction = null;

try { tManager = tsLookup.getTransactionManager(); transaction = tManager.getTransaction(); if (transaction == null) throw new TransactionRequiredException(); super.doDispatch(request, response); transaction.commit(); } catch (Exception e1) { try { if (transaction != null) transaction.rollback(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (SystemException e) { e.printStackTrace(); }}

In this case, we implement a transaction of type Required. The code will throw an exception if the transaction is null.

The Required type is an attribute declared in the JTA specifications. If it is set and the current transaction doesn't exist, then the transaction manager will throw an exception. More information on JTA attributes is available on the Oracle page at the following URL:http://www.oracle.com/technetwork/java/javaee/jta/index.html

Migrating from Existing Portals

230

How it works...In the portal/WEB-INF/conf/common/common-configuration.xml file, you can see the configuration of the transaction service. There are two services:

f org.jboss.cache.transaction.TransactionManagerLookup: Used to get the current Transaction Manager through the getTransactionManager() method

f org.exoplatform.services.transaction.TransactionService: Manages the JTA Transaction Manager. Usually, the client uses it to get UserTransaction to manage the transactions. Here is an example of UserTransaction:TransactionService ts = (TransactionService) pcontainer .getComponentInstanceOfType(TransactionService.class);UserTransaction ut = ts.getUserTransaction();try { ut.begin(); ... ut.commit();} catch (Exception e1) { try { ut.rollback(); } catch (Exception e) { e.printStackTrace(); }}

In the configuration of the service, you can set the timeout of the transaction. If not set, the timeout is 60 seconds, after which the transaction expires. The configured default is 300 seconds.

Arjuna implements the Transaction Manager. Arjuna is an open source company that developed the Transaction Manager and donated it to JBoss Application Server. If you use JBoss, you can configure the Transaction Manager in the files transaction-jboss-beans.xml and transaction-service.xml in the folder $JBOSS_HOME/server/$CONFIG/deploy.

If you use Tomcat or Jetty, no customization is possible, so always remember to take care regarding the requirements for your application.

Chapter 8

231

Migrating an authenticated portletAuthentication is a standard in the portlet specification. Accordingly, migrating is very simple if the standard APIs are used. In other cases, use the extended features provided by the products. Each Portal Product provides its own extensions. In this recipe, you will see an example of the migration of a portlet written with Liferay.

Getting readyThis is the Liferay portlet code to to be migrated:

try {  User currentUser = PortalUtil.getUser(request);

} catch (PortalException e) {  // something went wrong, we should handle it} catch (SystemException e) {  // something went terribly wrong, we should handle it}// the roles

List<Role> roles = currentUser.getRoles();// other users

List<User> liferayUsers = UserLocalServiceUtil.getUserByEmailAddress(currentUser.getCompanyId(), "[email protected]");

// ... rest of the code

This portlet takes the information of the current user through a utility class, PortalUtil.

It gets a User object and with it takes the information of the roles for this user.

It then requests information from the other available users of the same group. Let's see how to migrate this code.

How to do it...This is the new code in GateIn:

...import org.exoplatform.services.organization.UserHandler; import org.exoplatform.services.organization.GroupHandler;import org.exoplatform.services.organization.OrganizationService;import org.exoplatform.services.organization.Query;import org.exoplatform.services.organization.User;...

// Get the Organization Service

Migrating from Existing Portals

232

PortalRequestContext context = (PortalRequestContext) WebuiRequestContextget.CurrentInstance();ExoContainer pcontainer = context.getApplication().getApplicationServiceContainer();OrganizationService securityService = (OrganizationService) pcontainer.getComponentInstanceOfType( OrganizationService.class);

try { // Get the handlers for users and groups by the Organization Service UserHandler userHandler = securityService.getUserHandler(); GroupHandler groupHandler = securityService.getGroupHandler(); // Get the current user String currentUser = request.getRemoteUser(); User user = userHandler.findUserByName(currentUser); // Get the roles for the current user Collection roles = groupHandler.findGroupsOfUser(currentUser); System.out.println(roles); // Get all users with the same email Query query = new Query(); query.setEmail(user.getEmail()); ListAccess<User> users = userHandler.findUsersByQuery(query); System.out.println(users);} catch (Exception e) { e.printStackTrace();}

How it works...We have used the Organization Service, shown in Chapter 7, Developing Using Components API. The JSR 289 specifications define two methods to retrieve information about the current user through the javax.portlet.PortletRequest interface. Of course, they offer limited functionality compared to the features of the Organization Service, but it's good idea to keep them in mind.

request.getRemoteUser()

Chapter 8

233

The preceding line of code returns the ID of the current user as a String. It represents the userName attribute of the org.exoplatform.services.organization.User object.

request.isUserInRole(String group)

The preceding line of code returns a Boolean verifying if the current user is in the requested group. The parameter is the group ID and it represents the groupName attribute of the org.exoplatform.services.organization.Group object.

The Query object is an implementation by eXo. By default, it can interrogate only the five main fields of the User: user name, first name, last name, e-mail, and the last login date.

There's more...If you need to interrogate other fields, customization is needed. The following paragraph shows how to interrogate a different field.

Interrogating other fields of the userAdd these updates to your current project:

1. Add a new Query class that extends the org.exoplatform.services.organization.Query class:package my.extension;

import org.exoplatform.services.organization.Query;

public class MyQuery extends Query {

public MyQuery(String organization_) { this.organization_ = organization_; } private String organization_;

public String getOrganization() { return organization_; }

public void setOrganization(String s) { this.organization_ = s; }}

This class will interrogate the organizationId of the user.

Migrating from Existing Portals

234

2. Add a new User class in the configuration of the Organization Service that extends the org.exoplatform.services.organization.OrganizationConfig.User class adding the organizationId field:package my.extension;

import org.exoplatform.services.organization.OrganizationConfig.User;

public class MyUser extends User {

private String organizationId;

public MyUser() { }

public String getOrganizationId() { return organizationId; }

public void setOrganizationId(String organizationId) { this.organizationId = organizationId; } }

3. Modify the User configuration in organization-configuration.xml in the portal/WEB-INF/conf/organization folder by adding the new implementation and the new organizationId field:...<field name="user"> <collection type="java.util.ArrayList"> <value> <object type="my.extension.MyUser"> <field name="userName"> <string>root</string></field> <field name="password"> <string>gtn</string></field> <field name="firstName"> <string>Root</string></field> <field name="lastName"> <string>Root</string></field>

Chapter 8

235

<field name="email"> <string>root@localhost</string></field> <field name="organizationId"> <string>my organization</string></field>...

4. Now you need to extend the UserHandler. Here is an example:package my.extension;

import org.exoplatform.commons.utils.ListAccess;import org.exoplatform.services.organization.Query;import org.exoplatform.services.organization.User;import org.exoplatform.services.organization.idm.IDMUserListAccess;import org.exoplatform.services.organization.idm.PicketLinkIDMOrganizationServiceImpl;import org.exoplatform.services.organization.idm.PicketLinkIDMService;import org.exoplatform.services.organization.idm.Tools;import org.exoplatform.services.organization.idm.UserDAOImpl;import org.gatein.common.logging.LogLevel;import org.gatein.common.logging.Logger;import org.gatein.common.logging.LoggerFactory;import org.picketlink.idm.api.query.UserQueryBuilder;

public class MyUserDAOImpl extends UserDAOImpl {

private static Logger log = LoggerFactory.getLogger(MyUserDAOImpl.class);

private final PicketLinkIDMService service_;

public MyUserDAOImpl( PicketLinkIDMOrganizationServiceImpl orgService, PicketLinkIDMService idmService) throws Exception { super(orgService, idmService); this.service_ = idmService; }

@Override public ListAccess<User> findUsersByQuery(Query q) throws Exception { if (log.isTraceEnabled()) { Tools.logMethodIn(log, LogLevel.TRACE,

Migrating from Existing Portals

236

"findUsersByQuery", new Object[] { "q", q }); }

IDMUserListAccess list;

getOrgService().flush();

UserQueryBuilder qb = service_.getIdentitySession() .createUserQueryBuilder();

MyQuery myq = (MyQuery)q; if (myq.getOrganization() != null) { qb.attributeValuesFilter( MyUserDAOImpl.USER_ORGANIZATION_ID, new String[] { myq.getOrganization() } ); } list = new IDMUserListAccess(qb, 20, false); return list; }

}

5. Now extend the default Organization Service, the IDM Service:package my.extension;

import org.exoplatform.container.xml.InitParams;import org.exoplatform.services.organization.idm.PicketLinkIDMOrganizationServiceImpl;import org.exoplatform.services.organization.idm.PicketLinkIDMService;

public class MyOrganizationService extends PicketLinkIDMOrganizationServiceImpl {

public MyOrganizationService(InitParams params, PicketLinkIDMService idmService) throws Exception { super(params, idmService); userDAO_ = new MyUserDAOImpl(this, idmService);

}}

Chapter 8

237

6. Update the OrganizationService configuration in the organization-configuration.xml file:... <component> <key>org.exoplatform.services.organization.OrganizationService </key> <type>my.extension.MyOrganizationService<type>...

7. Now in the portlet code for GateIn that you have seen before, simply change the query implementation to give the following:... // Get all users with the same emailMyQuery query = new MyQuery("my organization id");ListAccess<User> users = userHandler.findUsersByQuery(query);System.out.println(users);

This will return the root user.

See also f Chapter 5, Securing Portal Contents

f Chapter 7, Developing Using Components API

Migrating a portlet that uses JCRJCR is a standard specification for content management implemented by ECM (Enterprise Content Management) systems. GateIn is an ECM. By default, many parts of the portal are managed in a JCR repository, for example, the web resources (JavaScript and CSS), the portlet, the pages' metadata, and the resource bundles. Each portlet can call an internal or external JCR repository and handle the contents. In this recipe, you will see a simple example of the migration of a portlet with JCR and how use JCR in this context.

Getting readyThe following is an example of a portlet that inserts a new node and prints out all the nodes. Here is the code:

import javax.jcr.AccessDeniedException;import javax.jcr.Node;import javax.jcr.Repository;

Migrating from Existing Portals

238

import javax.jcr.RepositoryException;import javax.jcr.Session;import javax.jcr.NodeIterator;

...

@Overridepublic void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException { ... Session session = (Session)repository.login(); Node root = (Node)session.getRootNode(); Node newNode = (Node)root.addNode("my new node"); newNode.setProperty("my property", "my value"); session.save(); printAllNodes(root, ""); session.logout();}

public void printAllNodes(Node root, String space) throws RepositoryException { NodeIterator nodeIterator = (NodeIterator)root.getNodes(); while (nodeIterator.hasNext()) { Node node = (Node)nodeIterator.nextNode(); System.out.println(space+node.getName()); printAllNodes(node, space+" "); } }

How to do it...All the objects that you see in this code—the Repository, Node, NodeIterator—are standard JCR APIs. The standard confers big advantages to migrations. All you need to do is call the javax.jcr.Repository class. Each portal has its own mode to call the Repository. GateIn, as for all components and services, calls the JCR Repository through the eXo Container:

PortalRequestContext context = (PortalRequestContext) WebuiRequestContext .getCurrentInstance();ExoContainer pcontainer = context.getApplication() .getApplicationServiceContainer();RepositoryService repositoryService = (RepositoryService) pcontainer

Chapter 8

239

.getComponentInstanceOfType(RepositoryService.class);Repository repository = (Repository)repositoryService .getRepository("repository");

The same thing can be done in a groovy script as follows:

<% def repositoryService = uicomponent.getApplicationComponent(RepositoryService.class);%>

How it works...GateIn integrates a JCR Repository donated by eXo to the JBoss community, eXo JCR.

eXo JCR implements the JCR specifications 1.0. Its architecture is similar to GateIn. PicoContainer hosts the services and the components are configured by XML.

The configuration of the repository can be found in the portal/WEB-INF/conf/jcr folder. Here are the main XML configuration files:

f component-plugins-configuration.xml: Used to create the default users in the JCR path /User for the workspace portal-system. Note how the users are also considered JCR nodes.

f jcr-configuration.xml: Contains the services for to the JCR Repository. Here the list:

� org.exoplatform.services.jcr.config.RepositoryServiceConfiguration: The configuration of the JCR repository. It delegates to the next repository-configuration.xml file.

� org.exoplatform.services.jcr.RepositoryService: The service seen in the previous example, callable by the client to execute the JCR operations

� org.exoplatform.services.jcr.ext.distribution.DataDistributionManager: A utility to perform massive read and write operations in an efficient manner; see the next paragraph for details.

� org.exoplatform.services.jcr.ext.hierarchy.NodeHierarchyCreator: This service is used to initialize the JCR workspaces. It is used in component-plugins-configuration.xml to create the default users and in jcr-configuration.xml to set the default permissions for the gadgets.

� org.exoplatform.services.jcr.webdav.WebDavServiceImpl: The webdav service. Through it, you can connect to GateIn with a client and manage the JCR repository. You will see an example below.

Migrating from Existing Portals

240

f repository-configuration.xml: The eXo JCR configuration. It creates three workspaces connectable remotely through webdav:

� portal-system: The main workspace containing all the information of the portal. It can be called by connecting to http://localhost:8080/rest/private/jcr/repository/portal-system

� portal-work: Contains information about password reminders and authentication gadget tokens. It can be called connecting by to http://localhost:8080/rest/private/jcr/repository/portal-work

� system: Contains the system nodes created by eXo JCR where all nodes are based. No need to interact with them unless debugging or developing. It can be called by connecting to http://localhost:8080/rest/private/jcr/repository/system

Each of these workspaces can be configured as follows:

f Storage: Can be set through the filesystem or database. The default is hsqldb. Initializer. Here, you can add plugins to initialize the nodes.

f Cache: The configuration of the caching of the nodes. GateIn uses JBoss cache 3.2.6 for the caching and clustering.

f Lock Manager: Decides when to lock a node and when to release it. It is tied to JBoss cache and is used by the transaction manager.

f Indexing: In this part, you configure the property of the query handler based on Lucene as the directory of the indexes and the caching mode for the JCR query.

Lucene is the main open source indexing product. Many open source products use it. It provides good performance for query execution. For details on Lucene, see the official site: http://lucene.apache.org

There's more...Here are two important digressions on eXo JCR:

Data distribution managerThe data distribution manager is used to distribute the data over the JCR in a smart fashion.

It can ensure that read and write performance is optimal, especially if you have many nodes to store, instead of storing them yourself under the same parent node (which can affect the performance if you have many nodes to store). We simply delegate data access and storage to the DataDistributionType corresponding to the expected mode that will store the data for you in an optimized and reliable way.

Chapter 8

241

The following code is an example of how it can be used:

// Get the data distribution corresponding to the readable modeDataDistributionType type = manager .getDataDistributionType(DataDistributionMode.READABLE);

// Get or create the node corresponding to "john.smith"Node node = type.getOrCreateDataNode(parentNode, "john.smith");

Clustered nodesJBoss Cache is an open source framework used for distributed and transactional caching. GateIn uses it for clustering. Basically, there are two configurations:

f JCR caching: The major part of the resources is managed by JCR. JBoss Cache is integrated in to eXo JCR, so the configuration of the cluster is done in a declarative manner. There are two different configurations, one for clustering mode and one for local mode. The difference is that the local mode only manages the local caching of the data, so they are not replicated. Here are the main configuration files that can be found in the portal/WEB-INF/conf/jcr/jbosscache folder:

� config.xml: Creates the partition Defaultpartition-jcr where the data will be replicated

� lock-config.xml: Creates the partition Defaultpartition-jcrlock used to maintain the information on the locking of the nodes. When a node is called, a value is replicated in this partition, so that all the nodes know it is in use and the transaction will be managed with the current locking mechanism.

By default, the cache used in cache mode is local. To set cluster mode go to conf/gatein/configuration.properties and set the property gatein.jcr.config.type=cluster instead of local.

f Caching for resources outside of JCR: A very similar configuration is used for data not contained in the JCR Repository. Each portlet can manage its own data and replicate them in a very simple mode. Here is an example of a portlet that replicates a string value:import org.exoplatform.services.cache.CacheService;import org.exoplatform.services.cache.ExoCache;

...

CacheService cacheService = (CacheService) pcontainer .getComponentInstanceOfType(CacheService.class);ExoCache<String, String> cache =

Migrating from Existing Portals

242

cacheService.getCacheInstance("/production");cache.put("mynewkey", "mynewvalue");

f And here is an example of a value exported from the cache:...String value = cache.get("mynewkey");...

f The default partition used is DefaultPartition-gatein and the configuration files are in the portal/WEB-INF/conf/jbosscache folder divided into local and cluster mode as for the JCR configuration.

Webdav serviceWebdav (Web-based Distributed Authoring and Versioning) is a protocol composed by a set of HTTP instructions that allows working on documents remotely.

You can connect to webdav URLs through a web browser. Here is an example of a webdav connection in Mac OS X.

Go to Go | Connect to Servers as shown in the following screenshot:

Chapter 8

243

Log in as any GateIn user:

Enter the webdav path:

Migrating from Existing Portals

244

You are now connected. From here, you can add a new node:

Importing a skin from an existing websiteA good feature of GateIn is that the skin is divided in modules. You can therefore update the skin of just a part of the site, for example the toolbar, the breadcrumbs, the logo, or the footer. The build of the skin is highly complex, so in this recipe, we will describe how to create a skin and how to take an existing skin from a site and import it. We will use the Google toolbar as an example.

Getting readyA skin is a graphic work, so you will need a graphic tool. The open source software program GIMP is recommended. As an example, we will use the theme of iGoogle simply by connecting to http://www.google.com.

Chapter 8

245

GIMP is the GNU Image Manipulation Program. It is a freely distributed piece of software for tasks such as photo retouching, image composition, and image authoring. It works on many operating systems, and in many languages. For more information visit http://www.gimp.org

How to do it...Follow these steps to create a new toolbar to add in your portal:

1. The first step is to create the skin. The skin can be a Maven project or simply a resource folder to put in the deploy directory of the application server as WAR file. This is the scheme of the application:

2. The web.xml file is needed for GateIn applications:<web-app xmlns="http://java.sun.com/xml/ns/j2ee" 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.4"> <display-name>google-skin</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>

<servlet> <servlet-name>GateInServlet</servlet-name> <servlet-class>org.gatein.wci.api.GateInServlet</servlet-class> <load-on-startup>0</load-on-startup>

Migrating from Existing Portals

246

</servlet> <servlet-mapping> <servlet-name>GateInServlet</servlet-name> <url-pattern>/gateinservlet</url-pattern> </servlet-mapping> </web-app>

3. Here is gatein-resources.xml: … <portal-skin> <skin-name>GoogleSkin</skin-name> <css-path>/skin/Stylesheet.css</css-path> <css-priority>0</css-priority> </portal-skin> <portlet-skin> <application-name>web</application-name> <portlet-name>BannerPortlet</portlet-name> <skin-name>GoogleSkin</skin-name> <css-path> /skin/webPortlet/webui/component/ UIBannerPortlet/Stylesheet.css </css-path> </portlet-skin> …

4. Add the /skin/Stylesheet.css:@import url(/eXoResources/skin/DefaultSkin/webui/component/Stylesheet.css);@import url(/eXoResources/skin/PortletThemes/Stylesheet.css);@import url(GoogleSkin/portal/webui/component/UIPortalApplicationSkin.css);

5. Add /skin/GoogleSkin/portal/webui/component/UIPortalApplicationSkin.css:@import url(/eXoResources/skin/DefaultSkin/portal/webui/component/control/Stylesheet.css);@import url(/eXoResources/skin/DefaultSkin/portal/webui/component/customization/Stylesheet.css);@import url(/eXoResources/skin/DefaultSkin/portal/webui/component/view/Stylesheet.css);

@import url(view/Stylesheet.css);

body {

Chapter 8

247

background: #eeeeee; margin: 0px; padding: 0px; font-family: Tahoma,Verdana, Arial, Helvetica, sans-serif; font-size: 11px; overflow-x: hidden;}

a { text-decoration: none; color: black;}

6. Create the directory /skin/GoogleSkin/portal/webui/component/view/UIToolbarContainer/background.

This directory contains the images used for the UIToolbarContainer, the tool bar that we are customizing.

7. Move the following three images from /eXoResources/skin/DefaultSkin/portal/webui/component/view/UIToolbarContainer/background to a new folder:

� ToolbarContainer.jpg: Contains all the images used by the toolbar.

� UserIcon.png: An image for the logged-in user.

� GateinLogo.jpg: The button used to change the language and the skin. Google doesn't support these features in the toolbar so you will see nothing if this is omitted.

Google shows the personal user image while GateIn, by default, is configured with an image that represents all users, so the UserIcon.png will not be modified.

8. Modify ToolbarContainer.jpg using GIMP, the image-manipulation tool. In the end, the result will be something like the following screenshot:

Migrating from Existing Portals

248

Google has only one image in the toolbar, so you must delete the other images shown above to make it similar. The Google image can be taken from: http://ssl.gstatic.com/gb/images/h_bedf916a.png

The technique used by the DefaultSkin of GateIn is called layering. This technique adds all the images of a skin inside one image. In the end, you call only the image that is cached in the portal

9. Now connect to www.google.com and take a screenshot of the website as shown in the following screenshot, whatever program your operating system provides with this functionality.

This will be used as the cover when you will choose the skin in GateIn. Name it GoogleTheme.jpg.

10. Go to /eXoResources/skin/DefaultSkin/portal/webui/component/customization/UIChangeSkinForm and create a new image named GoogleSkinForm.jpg. This image must be divided in 4 parts of the same dimension, with respect to the ChangeSkinForm.jpg file but with a different theme. Using GIMP, you must substitute the four skins with the image created earlier. Here is an example of the two images: ChangeSkinForm.jpg on the left and GoogleSkinForm.jpg on the right:

Chapter 8

249

11. Now create the style of the GoogleSkinImage cover in the UIChangeSkinForm page using the file /eXoResources/skin/DefaultSkin/portal/webui/component/customization/UIChangeSkinForm/Stylesheet.css, and add it under the old cover, SimpleSkinImage:.UIChangeSkinForm .UIItemSelector .TemplateContainer .SimpleSkinImage { margin: auto; width: 329px; height: 204px; background: url('background/ChangeSkinForm.jpg') no-repeat center -615px;

Migrating from Existing Portals

250

cursor: pointer ; }

.UIChangeSkinForm .UIItemSelector .TemplateContainer

.GoogleSkinImage { margin: auto; width: 329px; height: 204px; background: url('background/GoogleSkinForm.jpg') no-repeat center -615px; cursor: pointer ; }

12. Now connect to GateIn and change the skin. In the change skin form, you will see the newly created skin:

13. Now click on Apply and you will see the following result:

Chapter 8

251

How it works...We created a theme similar to iGoogle. The world of web graphics is too complex to demand a plugged import of a theme. It is important to study all the relevant components of the portal and the related CSS files before organizing a migration.

See also f The Handling different skins in a portlet recipe in Chapter 7, Developing Using

Components API

9Managing Gadgets

In this chapter we will cover:

f Importing existing gadgets

f Removing gadgets

f Creating gadgets

f Changing the category of a gadget

f Resizing gadgets

f Making the gadget a Portlet

f Setting user preferences

IntroductionGadgets are usually simple applications written in JavaScript that can be imported as windows. Their best feature is portability: they can be deployed through the Web in any container implementing the gadget specs. Another advantage is their simplicity of development. Google provides OpenSocial, which is an API for gadgets to interact with social network platforms. In this chapter, you will see different operations on Google Gadgets and details on how GateIn implements this specification.

To learn more about Google Gadgets, the official documentation is a good starting point. Go here to see it: http://developers.google.com/gadgets

Managing Gadgets

254

Importing existing gadgetsIn this recipe, you will start with an example where you import a Google Gadget in GateIn; by the end you'll have learnt more on the implementation and best practices for the importing procedure.

Getting readyIn this example, you will import, test, and resolve migration problems with the YouTube gadget present in the gadgets list.

How to do it...Follow these steps to import the YouTube gadget in your portal:

1. Connect to: http://www.google.it/ig/directory?synd=open. The page will show a list of the available gadgets:

Chapter 9

255

2. As you can see in the preceding screenshot, at the right there is a very wide range of applications to choose from. Click on the button below the YouTube gadget. You will see a window where you can configure the size and the color of the window:

3. Click now on the link View source and copy the address of the gadget code as shown in the following screenshot:

Managing Gadgets

256

4. Now log onto GateIn, go to the dashboard, and click on the Add Gadgets link inside the page. The Gadget box will appear:

5. Enter the URL you copied in step 3 and click on the + button. You will see this new window:

Chapter 9

257

It is now ready to use. You can maximize or minimize it depending on the window size or select a movie that will be shown in the window.

How it works...The Gadget box is an instrument provided by the dashboard application. The dashboard application is represented by a WEBUI portlet, the org.exoplatform.dashboard.webui.component.UIDashboardPortlet. This portlet has a child, the org.exoplatform.dashboard.webui.component.UIAddGadgetForm component, which executes the operations for the creation of the gadget.

Each time you click on the + button of the Gadget box, this component creates a further two components:

f org.exoplatform.portal.webui.application.UIGadget: this is added to the org.exoplatform.dashboard.webui.component.UIDashboardContainer. The UIDashboardContainer is the internal part of the UIDashboardPortlet that contains all the gadgets that the user chooses in their own dashboard.

The goal of the UIGadget is to provide the action and rendering operations to show the gadget in the dashboard. When the UIGadget is instanced, a storage name is created so it can be registered in the JCR repository.

f org.exoplatform.application.gadget.Gadget: this contains all the metadata, such as name and URL. It is registered in the gadget Registry Service. The UIGadget component queries the Gadget Registry Service to get all the information about the gadget.

All information about gadgets is registered in the JCR repository. You can see them by connecting through WEBDAV using this URL:

http://localhost:8080/rest/private/jcr/repository/portal-system/production/app:gadgets

Here is an example of a list of gadgets through the browser:

f app:Calculator

f app:Calendar

f app:Currency

f app:gadget-1270571586

f app:OAUTH

f app:rssAggregator

f app:ServicesManagement

f app:SiteExportImport

f app:Todo

Managing Gadgets

258

There's more...Now you can see some more details of the services available for the management of gadgets.

Gadget Registry ServiceThe Gadget Registry Service used for the management of gadgets. Here is a list of the main operations:

f deploy(Iterable<GadgetImporter> gadgets): Deploys a list of gadgets

f getGadget(String name): Returns the chosen gadget

f getAllGadgets: Returns all gadgets present in the registry

f saveGadget(Gadget gadget): Registers the new gadget or updates a chosen gadget

f removeGadget(String name): Removes the chosen gadget from the registry

f getGadgetUrl(String name): Returns the URL of the XML file for the chosen gadget name

The Gadget Registry Service can be called through an API. Here is an example:

import org.exoplatform.container.ExoContainer;import org.exoplatform.webui.application.WebuiRequestContext;import org.exoplatform.application.gadget.GadgetRegistryService

...

PortalRequestContext context = (PortalRequestContext) WebuiRequestContext.getCurrentInstance();ExoContainer pcontainer = context.getApplication() .getApplicationServiceContainer();GadgetRegistryService registryService = (GadgetRegistryService) pcontainer .getComponentInstanceOfType(GadgetRegistryService.class);

It is configured in the portal/WEB-INF/conf/portal/application-registry-configuration.xml. Here is the configuration:

<component> <key>org.exoplatform.application.gadget.GadgetRegistryService</key> <type>org.exoplatform.application.gadget.impl.GadgetRegistryServiceImpl</type> <init-params> <value-param> <name>gadgets.country</name>

Chapter 9

259

<description>US</description> <value>US</value> </value-param> <value-param> <name>gadgets.language</name> <description>en</description> <value>en</value> </value-param> <value-param> <name>gadgets.moduleId</name> <description>0</description> <value>0</value> </value-param> <value-param> <name>gadgets.hostName</name> <description>Gadget server url</description> <value>eXoGadgetServer/gadgets</value> </value-param> <properties-param> <name>developerInfo</name> <description>The group that is allowed to develop gadgets</description> <property name="developer.group" value="/platform/administrators"/> </properties-param> </init-params> </component>

There are default values used to query the gadgets. Gadgets.country and gadgets.language are used to query the metadata of the gadget. The main field is the gadgets.hostname. It connects the application to a Gadget Server to execute the queries. Here is a brief look at the Gadget Server.

Gadget ServerThrough the Gadget Server you can query all metadata of the gadget. The value of the gadgets.hostname field allows for the creation of the Gadget Server URL. Usually, it is something like:

http://localhost:8080/eXoGadgetServer/gadgets/api/rpc

In the previous example, when you click on the + button, the UIAddGadgetForm, to obtain a gadget, the form makes a JSON call to this URL by passing the following parameters:

[{method:"gadgets.metadata", id:"test", params: {ids:["http://hosting.gmodules.com/ig/gadgets/file/100080069921643878012/youtube.xml"], container:"default", language:"en", country:"US", view:"home"}}]

Managing Gadgets

260

The Gadget Server queries the XML file registered in JCR and it returns all the metadata of the gadget. It returns a JSON string that will be elaborated as a map of fields.

Removing gadgetsThis recipe will guide you through the process of removing gadgets from the dashboard.

Getting readyYou need only to click on the X button of the window.

How to do it...Here are the steps to follow for removing a gadget:

1. If your gadget is in the dashboard, simply click on the X of the window as shown in the following screenshot:

Chapter 9

261

2. It will then prompt you to confirm your choice. Click on the OK button as shown in the following screenshot:

3. If you want to completely delete the Gadget, simply go to the Application Registry of the gadget and click on the trash image of the chosen gadget on the left as shown in the following screenshot:

Managing Gadgets

262

4. If you want to only delete the declaration, so that the gadget remains inside the repository, but cannot be called, go to the voice Category and click on the trash image of the chosen gadget on the left as shown in the following screenshot:

If you remove a gadget imported on a user page, the user will see automatically this advice:

How it works...When you click on the X button, an event listener of the UIDashboard container, the org.exoplatform.dashboard.webui.component.UIDashboardContainer .DeleteGadgetActionListener, is activated. It queries the JCR Repository and verifies if there is a child in the UIDashboardContainer; if yes it deletes it.

When the user enters the portal for the first time and goes to the dashboard page, a set of pages is created in this JCR path: portal-system/production/mop:workspace/mop:usersites/mop:root/mop:rootpage/mop:children/mop:pages/mop:children/mop:Tab_Default.

The mop:Tab_Default node represents the dashboard page. If you go inside mop:Tab_Default/mop:rootcomponent through webdav, (see R for webdav) you will note that a node named with UUID containing three other nodes is also named with the UUID. Those represent the three spaces of the dashboard page where the gadgets can be inserted.

Chapter 9

263

One of these three spaces will contain the ID of the gadget inside the property mop:contentid of the mop:customization node. When you click on the X button, the information in the space will be removed.

Here are the properties of the mop:customization created while importing the YouTube gadget:

------------------------------------------------------ jcr:primaryType - mop:workspaceclone ------------------------------------------------------ jcr:uuid - 648e567dc0a8b21c00e159111e665b63 ------------------------------------------------------ mop:contentid - gadget-1270571586 ------------------------------------------------------

Here is an example of a pre-installed gadget in the portal, for example the Calculator gadget:

------------------------------------------------------ jcr:primaryType - mop:workspaceclone ------------------------------------------------------ jcr:uuid - 5ccdfaeac0a8b21c2658e94580d1467d ------------------------------------------------------ mop:contentid - Calculator ------------------------------------------------------ mop:mimetype - application/gadget

Though it is different from the YouTube gadget, the mop:contentid is predefined because it is taken from the name field of gadget.xml:

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

In the YouTube gadget, this value is auto generated. You will see how to create this file in the next recipe: Creating gadgets.

When you remove a gadget from the Application Registry, a delete event is sent to the org.exoplatform.applicationregistry.webui.component.UIGadgetManagement. RemoveGadgetActionListener. This listener works in two phases:

f It searches for the gadget in the Gadget Registry Service and removes it.

f It searches the associated Gadget Wrapper Portlet and deletes it. The Gadget Wrapper Portlet will be seen in the recipe Making the gadget a Portlet

Managing Gadgets

264

See also f The Migrating a portlet that uses JCR recipe in Chapter 8, Migrating from

Existing Portals

f The Making the gadget a Portlet recipe

Creating gadgetsThe creation of a gadget is very simple. This recipe will walk you through the process of creating and deploying a gadget.

Getting readyYou need to create a sample bundle where you will add and deploy your gadget. For example, we will create a Skype Talk client where a user can log into the portal and chat with friends through a customized window. The steps will therefore be:

f Creating a pom.xml file for a new Maven project

f Creating a web.xml file , which is mandatory for deploying a gadget project

f Creating a gadget.xml file where the gadgets are declared

f Creating the source directories and the code

How to do it...Go through the following steps to create the new Skype gadget:

1. Add a very basic pom.xml file in your new Maven project as follows:<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>my</groupId> <artifactId>skypeGadget</artifactId> <version>1.1.0-GA</version> <packaging>war</packaging> <name>GateIn Portal Skype Gadget Application</name>

<build> <finalName>skypeGadget</finalName> </build></project>

Chapter 9

265

2. Add a very basic web.xml file in the src/main/webapp/WEB-INF directory of your project:<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app> <display-name>skypeGadget</display-name></web-app>

3. Finally, add the following content in the gadget.xml file of your src/main/webapp/WEB-INF directory:<gadgets xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gatein.org/xml/ns/gatein_objects_1_2 http://www.gatein.org/xml/ns/gadgets_1_0" xmlns="http://www.gatein.org/xml/ns/gadgets_1_0"> <gadget name="SkypeTalk"> <path>/gadgets/SkypeTalk/SkypeTalk.xml</path> </gadget></gadgets>

In this example, only one Gadget is declared, you can add multiple gadgets in the portlet.xml file and set separated configuration files.

4. Create the folder for the source.

If you created a Maven project through Eclipse, we will, by the end of the project, have the following directory/folder structure (see Chapter 1, Getting Started for more details on Maven and Eclipse):

Managing Gadgets

266

5. Now look at the details of the gadgets. Create SkypeTalk.xml in the SkypeTalk folder as declared in the gadget.xml file:<?xml version="1.0" encoding="UTF-8" ?><Module> <ModulePrefs author="Cookbook Developer" title="Skype Talk" directory_title="Skype Talk" title_url="http://www.vige.it" description="Speak with your friends on Skype." thumbnail="images/SkypeTalk.gif" height="227"> <Locale lang="ar" language_direction="rtl"/> </ModulePrefs> <Content type="html"> <![CDATA[ ... ]]> </Content></Module>

The XML file declares an image to put be in the SkypeTalk/images folder. It will be used in the administration portlets as metadata for the presentation.

Now you need the JavaScript code. Of course, the more complex the application is, the bigger the JavaScript code will be. Luckily, Skype provides JavaScript to use on their website, so you can take it from there.

6. Connect to: http://www.skype.com/intl/en-us/tell-a-friend/get-a-skype-button, and the following form will open:

Chapter 9

267

7. Put your Skype username and choose a button in the window. You will end up with this code:<script type="text/javascript" src="http://download.skype.com/share/skypebuttons/js/skypeCheck.js"></script><a href="skype:yourusername?call"><img src="http://download.skype.com/share/skypebuttons/buttons/call_green_white_153x63.png" style="border: none;" width="153" height="63" alt="Skype Me™!" /></a>

8. Put all the preceding code in the CDATA tag of SkypeTalk.xml.

9. Now the project is ready. Create the target war with the Maven command:mvn install

10. Deploy it in the application server.

11. Go to the Application Registry and click on the Import Applications button. The new gadget will be added:

Managing Gadgets

268

12. Go to the dashboard to insert the gadget in the page. Open the Gadget box by clicking on the Add Gadgets link and choose the Skype Talk gadget. This will be the result:

You have seen the creation of the gadget through a project. There is, however, an alternative method for creating gadgets. The steps required are as follows:

13. Go to the Application Registry and click on the Gadget button at the top right:

Chapter 9

269

14. Click on Create a new gadget in the second toolbar of the Application Registry. You will see a smart form as shown in the following screenshot:

15. Type the title of the gadget in the Name field, for example Skype Talk, and in the Source field, the content of SkypeTalk.xml.

16. Click on the Save button; the gadget is ready. The gadget, as for the previous example, will be deployed automatically in the JCR repository.

How it works...When you deploy an application gadget, the Gadget Deployer is called. It is an event listener that catches all applications containing the WEB-INF/gadget.xml file. The deploy process is divided into three phases:

1. Validation of gadget.xml.

2. Creation of the importers according to the type of gadget. They can be remote or local.

3. The gadget is passed to the Gadget Registry Service, which registers the gadget in the registry and executes the importers to register the metadata in the JCR Repository.

The configuration of the deployer can be found in the portal/WEB-INF/conf/portal/application-registry-configuration.xml file:

<component> <key>org.exoplatform.application.gadget.GadgetDeployer</key> <type> org.exoplatform.application.gadget.GadgetDeployer </type></component>

Managing Gadgets

270

There's more...You have seen in this recipe how to create local gadgets. You can also import remote gadgets. The only difference is that the resources will not be registered in the internal repository, but will be called remotely. Here is an example of a remote YouTube gadget declaration through the gadget.xml file:

<gadget name="RemoteYouTubeGadget"> <url>http://hosting.gmodules.com/ig/gadgets/file/1000800699 21643878012/youtube.xml</url> </gadget>

The same thing can be done through the web console, by simply clicking on the Add a remote gadget button shown in the preceding screenshot and choosing the gadget XML URL.

Note that, if your gadget is local, many resources of the Google Gadgets are called remotely because the imported code present in the Content field of the gadget module descriptor contains remote calls. Consider rewriting that code if you want to avoid remote calls in your page.

See also f Chapter 1, Getting Started

f The Migrating a portlet that uses JCR in Chapter 8, Migrating from Existing Portals

Changing the category of a gadgetThis recipe will guide you through the process of modifying the default category of an existing gadget.

Getting readyPrepare to use the web console, XML configuration, and a few lines of Java code.

Chapter 9

271

How to do it...Follow these to change the category of a gadget:

1. If you created the gadget from the web console, you can choose the category, instead of using the default category with name Gadgets. Simply select the created gadget from the Application Registry. You will see a red link inside the description:

2. Click on the red link shown above to change the category. You will see the category selection form:

3. To change the category in the configuration, you need to write to the portal/WEB-INF/conf/portal/application-registry-configuration.xml and manually add the new category and a definition of the application for the created gadget. Here is an example that adds the Skype Talk gadget definition in the Application Registry Service:<object-param> <name>GadgetsMine</name> <description>GadgetsMine</description>

Managing Gadgets

272

<object type="org.exoplatform.application.registry.ApplicationCategory"> <field name="name"> <string>GadgetsMine</string> </field> <field name="displayName"> <string>GadgetsMine</string> </field> <field name="description"> <string>GadgetsMine</string> </field> <field name="accessPermissions"> <collection type="java.util.ArrayList" item-type="java.lang.String"> <value> <string>*:/platform/users</string> </value> </collection> </field> <field name="applications"> <collection type="java.util.ArrayList"> <value> <object type="org.exoplatform.application.registry.Application"> <field name="categoryName"> <string>GadgetsMine</string> </field> <field name="applicationName"> <string>SkypeTalk</string> </field> <field name="displayName"> <string>Skype Talk</string> </field> <field name="description"> <string>Skype Talk Gadget, easily manage and track your daily to-do list.</string> </field> <field name="type"> <string>gadget</string> </field> <field name="contentId"> <string>SkypeTalk</string> </field> <field name="accessPermissions">

Chapter 9

273

<collection type="java.util.ArrayList" item-type="java.lang.String"> <value> <string>*:/platform/users</string> </value> </collection> </field> </object> </value> </collection> </field> </object></object-param>

4. This code will create a new definition of the Skype Talk gadget that points to the new category GadgetsMine. You therefore now have two definitions for the Skype Talk gadget. Remove the old declaration as shown in the earlier recipe, Removing gadgets.

The categories defined in the Application Registry Service configuration are created only when you start GateIn for the first time, and when the JCR repository is empty. Pay attention to this feature, because if the repository is just created, you can only modify the categories by using the API. Here is an example of code that modifies a category with an API:

import org.exoplatform.application.registry.Application;import org.exoplatform.application.registry.ApplicationCategory;import org.exoplatform.application.registry.ApplicationRegistryService;

...

PortalRequestContext context = (PortalRequestContext) WebuiRequestContext.getCurrentInstance();ExoContainer pcontainer = context.getApplication().getApplicationServiceContainer();

ApplicationRegistryService applicationRegistryService = (ApplicationRegistryService) pcontainer.getComponentInstanceOfType(ApplicationRegistryService.class);

try { if (applicationRegistryService .getApplication("GadgetsMine/SkypeTalk") == null) { ApplicationCategory applicationCategory = new ApplicationCategory(); applicationCategory.setName("GadgetsMine"); applicationCategory.setDisplayName("GadgetsMine");

Managing Gadgets

274

Application application = applicationRegistryService .getApplication("Gadgets/SkypeTalk"); applicationRegistryService .save(applicationCategory, application); }} catch (Exception e) { e.printStackTrace();}

This code can be put in a new WEBUI component of the web application or in a simple servlet filter. For WEBUI, see Chapter 7, Developing Using Components API for more details.

How it works...When you click on the Import Applications button of the Application Registry, the default category for the gadgets is applied, so all gadgets that are still not installed, usually those inside an application and deployed, will take that category. It's the code in the org.exoplatform.application.registry.impl.ApplicationRegistryServiceImpl class that is executed:

public void importExoGadgets() throws Exception { ContentRegistry registry = getContentRegistry();

// ExoContainer container = ExoContainerContext.getCurrentContainer(); GadgetRegistryService gadgetService = (GadgetRegistryService)container.getComponentInstanceOfType(GadgetRegistryService.class); List<Gadget> eXoGadgets = gadgetService.getAllGadgets();

// if (eXoGadgets != null) { ArrayList<String> permissions = new ArrayList<String>(); permissions.add(UserACL.EVERYONE); String categoryName = "Gadgets"; // CategoryDefinition category = registry.getCategory(categoryName); if (category == null) { category = registry.createCategory(categoryName);

Chapter 9

275

Resizing gadgetsFor the purpose of this recipe, we will be using the configuration example to resize the gadget.

Getting readyModify the SkypeTalk.xml file of the previous example and add the height field, or use the web console.

How to do it...Here is an example of the configuration:

<ModulePrefs author="Cookbook Developer" title="Skype Talk" directory_title="Skype Talk" title_url="http://www.vige.it" description="Speak with your friends on Skype." thumbnail="images/SkypeTalk.gif" height="24">

The following steps show how to do it with the web console:

1. Go to the Application Registry, click on the Gadgets button, and select the Skype Talk gadget as shown in the following screenshot:

Managing Gadgets

276

2. Set the height to 62 and click on the Save button.

3. Go to the dashboard. You will see the result as follows:

By using the gadget, you can only configure the height because the layout and the skin manage all other settings. See Chapter 7, Developing Using Components API for more details. If you need to modify the width too, you could also consider transforming your Gadget into a portlet. In the next recipe, you will see how to do it.

How it works...As a portal is a combination of windows in a page, only the layout can decide the width of a portlet. GateIn provides a wide series of containers that are simple to install. You can select your container directly from the Edit Page link of the Site Editor or through the pages.xml as a portlet. The definitions of the layouts are in the groovy file: portal/WEB-INF/conf/uiconf/portal/webui/container/ContainerConfigOption.groovy. Here is an example of a two-columns container:

...column.addSelectItemOption(new SelectItemOption("twoColumns", "<container template=\"system:/groovy/portal/webui/container/UITableColumnContainer.gtmpl\">" + " <factory-id>TableColumnContainer</factory-id>" + " <container template=\"system:/groovy/portal/webui/container/UIColumnContainer.gtmpl\"><factory-id>ColumnContainer</factory-id></container>" + " <container template=\"system:/groovy/portal/webui/container/UIColumnContainer.gtmpl\"><factory-id>ColumnContainer</factory-id></container>" + "</container>", "TwoColumnContainerLayout")) ;

Chapter 9

277

Making the gadget a portletThe gadgets you have seen in previous recipes can be used only in the dashboard. It could be useful to manage the gadget as a portlet so that you can use all the portlet features, such as the security and event management, and manage them in any page of the portal.

Getting readyYou need the Gadget Wrapper Portlet. With it, you can add the gadget to any page of the portal and use the Portlet preferences for the configuration.

How to do it...Follow these steps to make a gadget a portlet:

1. Log in to GateIn and click on the Edit Page link of the home page as shown in the following screenshot:

2. Now move the Skype Talk gadget in the page as shown in the following screenshot:

Managing Gadgets

278

3. You will note that a Gadget Wrapper Portlet is created. Now edit the portlet by clicking on the pencil (at the top) in the created window. The Portlet Setting window will appear as shown in the following screenshot:

4. This new Gadget Wrapper Portlet instance now contains the gadget, and the gadget assumes the same properties of a portlet. As an example, resize the window again by choosing your size, and click on Save And Close button. That's it!

5. If you want to transform it by XML configuration, you need to add the portlet.xml file in the web application. Add the file inside the WEB-INF folder. Here is the code:<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> <description xml:lang="EN">Skype Talk Portlet Portlet</description> <portlet-name>SkypeTalkPortlet</portlet-name> <display-name>Skype Talk Portlet</display-name> <portlet-class>org.exoplatform.webui.application.portlet.PortletApplicationController </portlet-class> <init-param>

Chapter 9

279

<name>webui.configuration</name> <value>/WEB-INF/conf/portlet/skypetalk/SkypeTalkPortlet/webui/configuration.xml</value> </init-param> <supports> <mime-type>text/html</mime-type> </supports> <supported-locale>en</supported-locale> <resource-bundle>locale.portlet.gadget.SkypeTalkPortlet </resource-bundle> <portlet-info> <title>Skype Gadget Portlet</title> <short-title>Skype Gadget Portlet</short-title> <keywords>SkypeTalk</keywords> </portlet-info> <portlet-preferences> <preference> <name>url</name> <value>local://SkypeTalk</value> </preference> </portlet-preferences> </portlet></portlet-app>

6. This file needs a WEBUI configuration.xml file to be put inside the WEB-INF/conf/portlet/skypetalk/SkypeTalkPortlet/webui folder:<webui-configuration> <application> <ui-component-root>org.exoplatform.gadget.webui.component.UIGadgetPortlet </ui-component-root> <state-manager>org.exoplatform.webui.application.portlet.ParentAppStateManager </state-manager> <application-lifecycle-listeners> </application-lifecycle-listeners> </application></webui-configuration>

Managing Gadgets

280

7. This file uses the features of the UIGadgetPortlet class declared in the dashboard application. So in your pom.xml add the dependency for this component: <dependencies> <dependency> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.portlet.dashboard</artifactId> <type>war</type> <version>3.2.0-GA</version> </dependency> </dependencies>

8. Redeploy the Skype Talk application. Now you are ready to use the new portlet.

Setting user preferencesIt is possible to set a gadget's preferences, so any user can customize his/her own gadget and configure their own preferences. In this recipe, you will set the preferences of the Skype ID account of the Skype Talk gadget.

Getting readyTo set user preferences, you only need to modify the Gadget XML descriptor, in the example the SkypeTalk.xml, or by the Application Registry Administration console seen in the previous recipes of the chapter. Add the new parameter, and insert it in the view. The details are as follows.

How to do it...Follow these steps to create the user preferences for the gadget:

1. Open the SkypeTalk.xml file or go in the Application Registry and add the following code:<?xml version="1.0" encoding="UTF-8" ?><Module> <ModulePrefs author="Cookbook Developer" title="Skype Talk" directory_title="Skype Talk" title_url="http://www.vige.it" description="Speak with your friends on Skype." thumbnail="images/SkypeTalk.gif" height="24"> <Require feature="setprefs" /> <Locale lang="ar" language_direction="rtl" />

Chapter 9

281

</ModulePrefs> <UserPref name="skypeid" display_name="Insert the Skype id" default_value="myaccount" /> <Content type="html"> <![CDATA[ <script type="text/javascript"> var prefs = new _IG_Prefs(__MODULE_ID__); var theURL = 'skype:'+prefs.getString('skypeid')+'?call'; </script> <script type="text/javascript" src="http://download.skype.com/share/skypebuttons/js/skypeCheck.js"></script> <body> <a href="javascript:document.location=theURL"> <img src="http://download.skype.com/share/skypebuttons/buttons/call_green_white_153x63.png" style="border: none;" width="153" height="63" alt="Skype Me™!" /> </a></body> ]]> </Content></Module>

2. Inspect your gadget. You will notice a new pencil button in the title bar. Click on the button and the new user preferences form will appear.

3. Now you can change the Skype ID to your contact.

Remember, if you modify the XML file, you should redeploy the application. If you use the web console, you don't need to do anything else because the updates are automatically registered.

Managing Gadgets

282

How it works...The Google OpenSocial specifications describe different features that can be used, but GateIn implements only some of them. Moreover, the Require feature tag is optional, because the features are called directly with a simple mechanism of calling a function declared in the Content tag of the gadget XML descriptor. You have seen the setprefs feature in this recipe. This feature and the other gadget features are described in three JavaScript files inside the eXoResources/javascript/eXo/gadget folder.

You can connect to these three URLs to see the provided gadget features:

f http://localhost:8080/eXoResources/javascript/eXo/gadget/Gadgets.js

f http://localhost:8080/eXoResources/javascript/eXo/gadget/UIGadget.js

f http://localhost:8080/eXoResources/javascript/eXo/gadget/ExoBasedUserPrefStore.js

When you click on the pencil button on the top right of the portlet, the handleOpenUserPrefsDialog function of the Gadgets.js is called. This file is in the directory eXoResources/javascript/eXo/gadget. Here is the code:

gadgets.IfrGadget.prototype.handleOpenUserPrefsDialog = function() { if (this.userPrefsDialogContentLoaded) { this.showUserPrefsDialog(); } else { var gadget = this; var igCallbackName = 'ig_callback_' + this.id; gadget.userPrefsDialogContentLoaded = true; this.generateForm(gadget, this.getUserPrefsParams()); gadget.showUserPrefsDialog(); }};

It generates an automatic form taking the skypeid as parameter and passing it to the _IG_Prefs object.

Chapter 9

283

When you click on the Save button, the method handleSaveUserPrefs is called:

gadgets.IfrGadget.prototype.handleSaveUserPrefs = function() { this.hideUserPrefsDialog();

var prefs = {}; var numFields = document.getElementById('m_' + this.id + '_numfields').value; for (var i = 0; i < numFields; i++) { var input = document.getElementById('m_' + this.id + '_' + i); if (input.type != 'hidden') { var userPrefNamePrefix = 'm_' + this.id + '_up_'; var userPrefName = input.name.substring(userPrefNamePrefix.length); var userPrefValue = input.value; if(input.type == 'checkbox') userPrefValue = input.checked ? "true" : "false"; prefs[userPrefName] = userPrefValue; } } this.setUserPrefs(prefs); this.refresh();};

It simply fills in the user preferences on the client side.

10Frameworks in a Portal

In this chapter, we will cover:

f Creating a JSF 2 portlet

f Using jQuery in a portlet

f Using portlet events in JSF 2

f Creating a RichFaces 4 portlet

IntroductionIn this chapter, will cover creating portlets with various frameworks, with the focus on Java Server Faces 2, to provide examples of the ease with which these frameworks can be used to develop portlets. Using frameworks, such as JSF 2, does require the inclusion of a Portlet Bridge, but this is achieved without noticeable complexity for a developer.

A Portlet Bridge adds functionality to a portlet to handle the lifecycle differences between the Portal and Servlet environments. How a Portlet Bridge works is specified within JSR-301 and JSR-329, though these only cover JSF 1.0 and 1.2. There is currently no JSR for Portlet Bridge and JSF 2 interoperability, but those on the expert group are working together to define a common API.

Creating a JSF 2 portletIn this recipe, will create a JSF 2 portlet that creates a new stock purchase request. The portlet will also utilize JSR-303 for validating the form data before submission to the server.

Frameworks in a Portal

286

Getting readyThe following are required for this recipe:

f Apache Maven

f An IDE of your choice

f GateIn-3.2.0.Final-jbossas7-preview

How to do it...To create a portlet that uses JSF 2:

1. Create a new Maven project within your IDE, specifying Group Id: gatein.cookbook, Artifact Id: chapter10-jsf, and Packaging: war.

2. Inside the project's pom.xml, add the following dependencies: <dependency> <groupId>javax.faces</groupId> <artifactId>jsf-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.portlet</groupId> <artifactId>portlet-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.portletbridge</groupId> <artifactId>portletbridge-api</artifactId> <version>3.1.0.Alpha1</version> </dependency> <dependency> <groupId>org.jboss.portletbridge</groupId> <artifactId>portletbridge-impl</artifactId> <version>3.1.0.Alpha1</version> <scope>runtime</scope> </dependency>

Chapter 10

287

3. Create an enum named Status within a package named gatein.cookbook.chapter10 with the following content:public enum Status {

WAITING("Waiting"), COMPLETE("Complete"), CANCELLED("Cancelled");

private String name;

Status(String name) { this.name = name; }

public String getName() { return this.name; }}

4. Create an enum named TradeInstructionType within a package named gatein.cookbook.chapter10 with the following content:public enum TradeInstructionType {

MARKET("Market"), LIMIT("Limit"), STOP_LOSS("Stop Loss"), TRAILING_STOP("Trailing Stop"), GOOD_TILL_CANCELLED("Good Till Cancelled"), DAY_ORDER("Day Order");

private String name;

TradeInstructionType(String name) { this.name = name; }

public String getName() { return this.name; }}

Frameworks in a Portal

288

5. Create an enum named OrderType within a package named gatein.cookbook.chapter10 with the following content:public enum OrderType {

BUY("Buy"), SELL("Sell");

private String name;

OrderType(String name) { this.name = name; }

public String getName() { return this.name; }

}

6. Create a class named OrderManager within a package named gatein.cookbook.chapter10 with the following content:@ManagedBean@ApplicationScopedpublic class OrderManager implements Serializable {

private static final long serialVersionUID = -5776345927432051634L;

Set<Order> orders = new HashSet<Order>();

public void addOrder(Order order) { orders.add(order); }

public Set<Order> getOrders() { return orders; }

public void setOrders(Set<Order> orders) { this.orders = orders; }}

Chapter 10

289

7. Create a class named Order within a package named gatein.cookbook.chapter10 with the following content:@ManagedBean@RequestScopedpublic class Order implements Serializable {

private static final long serialVersionUID = -1343926602596381203L;

@ManagedProperty(value = "#{orderManager}") private OrderManager orderManager;

public Order() { this.status = Status.WAITING; this.entryDate = new Date(); }

private OrderType type;

private TradeInstructionType tradeInstruction;

private String accountNumber;

private Integer numberStocks;

private Date entryDate;

private Status status;}

8. Add the following getter and setter methods to Order: public OrderType getType() { return type; }

public void setType(OrderType type) { this.type = type; }

public TradeInstructionType getTradeInstruction() { return tradeInstruction; }

Frameworks in a Portal

290

public void setTradeInstruction(TradeInstructionType tradeInstruction) { this.tradeInstruction = tradeInstruction; }

public String getAccountNumber() { return accountNumber; }

public void setAccountNumber(String accountNumber) { this.accountNumber = accountNumber; }

public Integer getNumberStocks() { return numberStocks; }

public void setNumberStocks(Integer numberStocks) { this.numberStocks = numberStocks; }

public Date getEntryDate() { return entryDate; }

public void setEntryDate(Date entryDate) { this.entryDate = entryDate; }

public Status getStatus() { return status; }

public void setStatus(Status status) { this.status = status; }

9. Add the following methods to Order: public SelectItem[] getOrderTypeValues() { SelectItem[] items = new SelectItem[OrderType.values().length]; int i=0; for (OrderType type : OrderType.values()) { items[i++] = new SelectItem(type, type.getName());

Chapter 10

291

} return items; }

public SelectItem[] getTradeInstructionValues() { SelectItem[] items = new SelectItem[TradeInstructionType.values().length]; int i=0; for (TradeInstructionType type : TradeInstructionType.values()) { items[i++] = new SelectItem(type, type.getName()); } return items; }

public SelectItem[] getStatusValues() { SelectItem[] items = new SelectItem[Status.values().length]; int i=0; for (Status status : Status.values()) { items[i++] = new SelectItem(status, status.getName()); } return items; }

10. Add the following methods to Order: public void setOrderManager(OrderManager orderManager) { this.orderManager = orderManager; }

public String create() { orderManager.addOrder(this); return "orderSuccess"; }

11. Create layout.xhtml in the src/main/webapp/templates folder of the project.

12. Add the following content into layout.xhtml:<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">

<h:head>

Frameworks in a Portal

292

</h:head>

<h:body>

<div id="header"> <ui:insert name="header"> <h2>New Stock Purchase</h2> </ui:insert> </div>

<div id="content"> <ui:insert name="content"> </ui:insert> </div>

</h:body></f:view>

13. Create newOrder.xhtml in the src/main/webapp folder of the project.

14. Add the following content into newOrder.xhtml:<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">

<h:body> <ui:composition template="/templates/layout.xhtml"> <ui:define name="content"> <h:form> <h:panelGrid columns="2"> <h:outputLabel value="Order Type" for="orderType" /> <h:selectOneMenu id="orderType" value="#{order.type}" required="true"> <f:selectItems value="#{order.orderTypeValues}"/> </h:selectOneMenu>

<h:outputLabel value="Trade Instruction" for="tradeInstructionType" /> <h:selectOneMenu id="tradeInstructionType" value="#{order.tradeInstruction}" required="true"> <f:selectItems value="#{order.tradeInstructionValues}"/> </h:selectOneMenu>

Chapter 10

293

<h:outputLabel value="Account Number" for="acctNum" /> <h:inputText id="acctNum" value="#{order.accountNumber}" />

<h:outputLabel value="How many Stocks?" for="stocks" /> <h:inputText id="stocks" value="#{order.numberStocks}" /> </h:panelGrid> <h:commandButton action="#{order.create}" value="Create Order" /> </h:form> </ui:define> </ui:composition> </h:body></f:view>

15. Create orderSuccess.xhtml in the src/main/webapp folder of the project.

16. Add the following content into orderSuccess.xhtml:<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">

<h:body> <ui:composition template="/templates/layout.xhtml"> <ui:define name="header"> <h3>Order Success!</h3> </ui:define>

<ui:define name="content"> <h5>Congratulations, your order was submitted for processing</h5> <h:form> <h:commandButton action="newOrder" value="Place another order" /> </h:form> </ui:define> </ui:composition> </h:body></f:view>

Frameworks in a Portal

294

17. Create portlet.xml in the src/main/webapp/WEB-INF folder of the project.

18. Add the following content to portlet.xml:<?xml version="1.0" encoding="UTF-8"?><portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0">

<portlet> <portlet-name>Order-JSF</portlet-name> <portlet-class>javax.portlet.faces.GenericFacesPortlet</portlet-class> <init-param> <name>javax.portlet.faces.defaultViewId.view</name> <value>/newOrder.xhtml</value> </init-param> <supports> <mime-type>text/html</mime-type> <portlet-mode>VIEW</portlet-mode> </supports> <portlet-info> <title>Order JSF Portlet</title> </portlet-info> </portlet></portlet-app>

For an existing JSF 2 application that needs to be run as a portlet, the first, and often only, step is to add a portlet.xml file into the WEB-INF folder of the project for it to be recognized as a portlet.

19. Create web.xml in the src/main/webapp/WEB-INF folder of the project.

20. Add the following content to web.xml:<?xml version="1.0" encoding="UTF-8"?><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>chapter10-jsf</display-name>

<context-param>

Chapter 10

295

<param-name>javax.portlet.faces.renderPolicy</param-name> <param-value>ALWAYS_DELEGATE</param-value> </context-param> <context-param> <param-name>javax.faces.FACELETS_VIEW_MAPPINGS</param-name> <param-value>*.xhtml</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>

21. Create faces-config.xml in the src/main/webapp/WEB-INF folder of the project.

22. Add the following content to faces-config.xml:<?xml version="1.0" encoding="UTF-8"?><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_1.xsd" version="2.1">

</faces-config>

23. Run the following command in the root of the project directory to build the web archive:>mvn clean package

24. Copy the generated web archive, chapter10-jsf-1.0.0.SNAPSHOT.war, from the target folder into the deployment folder of where you unpacked the GateIn installation.

25. Start the server and log in as an administrator.

26. Click through the top-menu navigation with the following path: Group | Administration | Application Registry.

Frameworks in a Portal

296

27. Click on Import Applications to make the new portlet available.

28. Create a new page in the Site area of the portal and add the Order-JSF portlet to it. The Order-JSF portlet can be found under the Chapter10-jsf category.

29. Click on the Finish icon to save the page.

30. The portlet page should look like the following screenshot:

31. Enter some data into the form and click on Create Order, and the portal page will redirect you to the Order Success! page.

32. Clicking on Place another order will return the user to a blank order form.

How it works...Step 2 adds the JBoss Portlet Bridge dependency to the project, which enables JSF 2 to work in a portal environment.

At the time of writing, the latest release of JBoss Portlet Bridge for JSF 2 is 3.1.0.Alpha1. Please check http://www.jboss.org/portletbridge for the latest available version.

Steps 3, 4, and 5 create Java enumerations for properties of an order where we have a set of valid values.

Chapter 10

297

Step 6 creates OrderManager, which uses the JSF @ManagedBean annotation to specify it is a JSF backing bean, and it is also marked with @ApplicationScoped to signify that all users will see the same instance of this class. It holds a Set of orders that have been entered through the form.

Steps 7, 8, and 9 create the Order bean, which holds the details of an order. Step 7 specifies that the bean is @RequestScoped, which means there will be a new instance created for each page request. Step 7 also sets the @ManagedProperty annotation to indicate to JSF that there is a separate backing bean with the name of orderManager that should be set onto this property.

Step 9 defines some methods to convert the enum values into SelectItems for use in the JSF form. The methods are called from the <f:selectItems> tag in Step 14.

Step 10 specifies the setter associated with the @ManagedProperty, which is required for the JSF injection to work correctly. It also adds a create method to be called when the form is submitted, with the returned string representing the name of the page to navigate to next.

Steps 11 and 12 create a basic Facelet template for the portlet.

Steps 13 and 14 create the order form page in JSF. It consists of tags to specify different types of input elements, such as text and select lists. The action on the h:commandButton is set to call Order.create() when clicked, represented through the expression language names.

Steps 15 and 16 create the success page that the user is redirected to once the order has been recorded.

Steps 17 and 18 define the metadata for the portlet. The class that the portlet uses is GenericFacesPortlet, which is part of JBoss Portlet Bridge, so there is no need to create a portlet to use JSF. The init-param is important as it informs the JBoss Portlet Bridge which JSF page to display for the view Portlet Mode.

Steps 19 and 20 create the web.xml of the project. It defines the FacesServlet for JSF, determines which file suffixes are supported, and ensures that the JBoss Portlet Bridge should always delegate to JSF to render the content of the page.

See also f The Using portlet events in JSF 2 recipe

f The Creating a RichFaces 4 portlet recipe

Frameworks in a Portal

298

Using jQuery in a portletIn this recipe, we will create a stock watch list that utilizes jQuery to retrieve updated stock prices without refreshing the page.

Getting readyThe following are required for this recipe:

f Apache Maven

f An IDE of your choice

f GateIn-3.2.0.Final-jbossas7-preview

How to do it...To create a portlet that uses jQuery for Ajax calls:

1. Create a new Maven project within your IDE, specifying Group Id: gatein.cookbook, Artifact Id: chapter10-jquery, and Packaging: war.

2. Inside the project's pom.xml, add the following dependency: <dependency> <groupId>javax.portlet</groupId> <artifactId>portlet-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency>

3. Create a class named WatchlistPortlet that extends javax.portlet.GenericPortlet within a package named gatein.cookbook.chapter10.

4. Add a private variable to WatchlistPortlet called watchedStocks of type String[] and set it to null.

5. Add a private variable to WatchlistPortlet called watchedStockPrice of type BigDecimal[] and set it to null.

Chapter 10

299

6. Create a method named init within WatchlistPortlet with the following content: @Override public void init() throws PortletException { initStockList(); initStockPrices(); }

7. Create a method named initStockList within WatchlistPortlet with the following content: private void initStockList() { watchedStocks = new String[3];

watchedStocks[0] = "IBM:International Business Machines Corp."; watchedStocks[1] = "REV:Revlon"; watchedStocks[2] = "RHT:Red Hat"; }

8. Create a method named initStockPrices within WatchlistPortlet with the following content: private void initStockPrices() { watchedStockPrice = new BigDecimal[3];

watchedStockPrice[0] = new BigDecimal(32.10).setScale(2, RoundingMode.HALF_UP); watchedStockPrice[1] = new BigDecimal(12.54).setScale(2, RoundingMode.HALF_UP); watchedStockPrice[2] = new BigDecimal(57.01).setScale(2, RoundingMode.HALF_UP); }

9. Download jQuery production version and save the file to the src/main/webapp/js folder of the project.

10. Create a method named display within WatchlistPortlet as follows: @RenderMode(name = "view") public void display(RenderRequest request, RenderResponse response) throws PortletException, IOException { request.setAttribute("watchList", watchedStocks); request.setAttribute("prices", watchedStockPrice); getPortletContext().getRequestDispatcher("/watchlist.jsp").include(request, response); }

Frameworks in a Portal

300

11. Create a method named updateStockPrice within WatchlistPortlet as follows: protected String updateStockPrice(int index) { Float increment = new Random(watchedStockPrice[index].longValue()).nextFloat(); BigDecimal newValue = watchedStockPrice[index].add(new BigDecimal(increment.doubleValue())); newValue = newValue.setScale(2, RoundingMode.HALF_UP); watchedStockPrice[index] = newValue; return newValue.toString(); }

12. Create a method named serveResource within WatchlistPortlet as follows: @Override public void serveResource(ResourceRequest request, ResourceResponse response) throws PortletException, IOException { String resourceID = request.getResourceID(); PrintWriter writer = response.getWriter();

if ("refreshPrice".equalsIgnoreCase(resourceID)) { int index = Integer.parseInt(request.getParameter("stock")); writer.write(updateStockPrice(index)); }

writer.flush(); writer.close(); }

13. Create watchlist.jsp in the src/main/webapp folder of the project.

14. Add the following content into watchlist.jsp:<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %><%@ page import="java.math.BigDecimal" %>

<portlet:defineObjects/>

<div class="portlet-section-header">Stock Watchlist</div><br/><div class="portlet-section-body"><% String[] stocks = (String[])renderRequest.getAttribute("watchList");

Chapter 10

301

BigDecimal[] prices = (BigDecimal[])renderRequest.getAttribute("prices");%> <table border="1"> <thead class="portlet-table-header"> <tr> <th>Ticker</th> <th>Company</th> <th>Stock Price</th> <th></th> </tr> </thead> <tbody class="portlet-table-body"> <% int count = 0; for (String stock: stocks) { if (null != stock) { String[] split = stock.split(":"); %> <portlet:resourceURL var="refreshPriceUrl" id="refreshPrice"> <portlet:param name="stock" value="<%= Integer.toString(count) %>" /> </portlet:resourceURL>

<tr> <td align="center" class="portlet-table-text"><%= split[0] %></td> <td align="center" class="portlet-table-text"><%= split[1] %></td> <td align="center" class="portlet-table-text"><span id="<portlet:namespace/>price<%= count %>"><%= prices[count] %></span></td> <td align="center" class="portlet-table-text"><a class="portlet-font-dim" href="#" onclick="<portlet:namespace/>updatePrice('${refreshPriceUrl}', <%= count++ %>);">Refresh Price</a></td> </tr> <% } } %> </tbody> </table></div>

Frameworks in a Portal

302

<br/><script type="text/javascript" src="<%= renderRequest.getContextPath() %>/js/jquery-1.7.2.min.js"></script><script type='text/javascript'> function <portlet:namespace/>updatePrice(updatePriceUrl, index) { $.ajax( { type: "POST", url: updatePriceUrl, cache: false, success: function (data) { $('#<portlet:namespace/>price'+index).html(data); } } ); }</script>

15. Create portlet.xml in the src/main/webapp/WEB-INF folder of the project.

16. Add the following content to portlet.xml:<?xml version="1.0" encoding="UTF-8"?><portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0"> <portlet> <portlet-name>WatchList-jQuery</portlet-name> <portlet-class>gatein.cookbook.chapter10.WatchlistPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>WatchList - jQuery portlet</title> </portlet-info> </portlet></portlet-app>

Chapter 10

303

17. Create web.xml in the src/main/webapp/WEB-INF folder of the project.

18. Add the following to web.xml:<?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_5.xsd" version="2.5"></web-app>

19. Run the following command in the root of the project directory to build the web archive:>mvn clean package

20. Copy the generated web archive, chapter10-jquery-1.0.0.SNAPSHOT.war, from the target folder into the deployment folder of where you unpacked the GateIn installation.

21. Start the server and log in as an administrator.

22. Click on the top-menu navigation with the following path: Group | Administration | Application Registry.

23. Click on Import Applications to make the new portlet available.

24. Create a new page in the Site area of the portal and add the Watchlist-jQuery portlet to it. The Watchlist-jQuery portlet can be found under the Chapter10-jquery category.

25. Click on the Finish icon to save the page.

26. The portlet page should look like the following screenshot:

Frameworks in a Portal

304

27. Click on Refresh Price a few times on a couple of the stocks, and the portal page will show updated prices like the following screenshot:

How it works...Steps 7 and 8 initialize the array of stocks that are being watched and what the initial price for each of them is when the portlet is initialized at startup.

Step 10 handles the RenderRequest for the portlet, by setting the array of stocks and their prices as request attributes before redirecting the request to the JSP page for display.

Step 11 defines updateStockPrice, a method for incrementing the stock price by a random amount. This is easily modified to call a service to retrieve stock prices from a live system.

Step 12 defines serveResource, which is where the Ajax request is handled by the portlet. The resource ID is checked to make sure the ResourceRequest received by the portlet is the correct one, though in practice this check is more applicable when a single portlet handles many Ajax requests through the serveResource method. A PrintWriter is retrieved from the ResourceResponse for writing the result of the request back to the browser. The stock request parameter is retrieved and used as the index for calling updateStockPrice.

Step 14 creates the watchlist.jsp, which handles all the portlet content display. It uses the <portlet:resourceURL> tag to create a self-referencing URL for retrieving resources from the portlet. The id attribute on the tag is set as the resource ID on the ResourceRequest, so the value must match what we are expecting within serveResource, otherwise it won't successfully retrieve any content.

Within the stock table, a link is created that uses the onclick event to trigger a JavaScript call to updatePrice, using the resource URL that was created earlier.

At the bottom of watchlist.jsp, the jQuery JavaScript library is loaded and the updatePrice method is defined. It instructs jQuery to make an Ajax call using the URL passed to it, and to update the HTML element with the id of #<portlet:namespace/>price'+index with the data that was returned in the response. The call is specified as non-cacheable as there is no need to cache the stock price, because it is not a static resource.

Chapter 10

305

In the JSP, <portlet:namespace/> was used to generate an ID unique to this portlet to prevent ID and JavaScript name clashes with other portlets that are on the same page.

In Step 27, when Refresh Price is clicked one or more times, the entire portal page and portlet are not re-rendered at any point; it is only the single piece of HTML content that is replaced by Ajax. This happens as calling serveResource does not result in a new RenderRequest being issued by the portal container.

The serveResource method does not support coordination through events or through shared render parameters. It cannot set new render parameters, portlet mode, or window state, nor can it issue redirects or modify the application-scoped session state to prevent an inconsistent user experience.

Using portlet events in JSF 2Update the JSF 2 order portlet from the first recipe to send an event when an order is saved, and create a new portlet to display a list of active orders.

Getting readyThe following are required for this recipe:

f Apache Maven

f An IDE of your choice

f GateIn-3.2.0.Final-jbossas7-preview

f Order-JSF project from Creating a JSF 2 portlet

How to do it...To modify the existing order portlet to fire an event:

1. Inside the project's pom.xml, add the following dependency: <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <scope>provided</scope> <version>2.1</version> </dependency>

Frameworks in a Portal

306

2. Create a class named OrderEvent within a package named gatein.cookbook.chapter10 with the following content:@XmlRootElementpublic class OrderEvent implements Serializable {

private Order order;

public static final QName QNAME = new QName("urn:chapter10:jsf:order:event", "OrderEvent");

public OrderEvent(Order order) { this.order = order; }

public Order getOrder() { return order; }

}

3. Modify the create method of Order by adding the following code before return "orderSuccess": Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse(); if (response instanceof StateAwareResponse) { StateAwareResponse stateResponse = (StateAwareResponse) response; stateResponse.setEvent(OrderEvent.QNAME, new OrderEvent(this)); }

4. Add the following into portlet.xml just before </portlet>: <supported-publishing-event> <qname xmlns:jsf="urn:chapter10:jsf:order:event">jsf:OrderEvent</qname> </supported-publishing-event>

5. Add the following into portlet.xml just before </portlet-app>: <event-definition> <qname xmlns:jsf="urn:chapter10:jsf:order:event">jsf:OrderEvent</qname> <value-type>gatein.cookbook.chapter10.OrderEvent</value-type> </event-definition>

Chapter 10

307

To create a new portlet listing the active orders:

1. Create a class named OrderManagerEvent within a package named gatein.cookbook.chapter10 with the following content:@ManagedBean@ApplicationScopedpublic class OrderManagerEvent implements Serializable {

private static final long serialVersionUID = -5776345927432051634L;

Set<Order> orders = new HashSet<Order>();

public void addOrder(Order order) { orders.add(order); }

public Set<Order> getOrders() { return orders; }

public void setOrders(Set<Order> orders) { this.orders = orders; }}

2. Create a class named OrderEventHandler within a package named gatein.cookbook.chapter10 with the following content:public class OrderEventHandler implements BridgeEventHandler {

public EventNavigationResult handleEvent(FacesContext context, Event event) { ELResolver facesResolver = context.getELContext().getELResolver(); OrderManagerEvent mgrBean = (OrderManagerEvent) facesResolver.getValue(context.getELContext(), null, "orderManagerEvent"); mgrBean.addOrder(((OrderEvent)event.getValue()).getOrder()); return new EventNavigationResult("/", "/orderList.xhtml"); }}

Frameworks in a Portal

308

3. Create a class named OrderList within a package named gatein.cookbook.chapter10 with the following content:@ManagedBean@RequestScopedpublic class OrderList {

@ManagedProperty(value = "#{orderManagerEvent}") private OrderManagerEvent orderManager;

public void setOrderManager(OrderManagerEvent orderManager) { this.orderManager = orderManager; }

public List<Order> getActiveOrders() { List<Order> active = new ArrayList<Order>(); for (Order order : orderManager.getOrders()) { if (order.getStatus().equals(Status.WAITING)) { active.add(order); } } return active; }}

4. Create orderList.xhtml in the src/main/webapp folder of the project.

5. Add the following content into orderList.xhtml:<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">

<h:body> <ui:composition template="/templates/layout.xhtml"> <ui:define name="header"> <h3>Active Orders</h3> </ui:define>

<ui:define name="content"> <h:outputText value="There are currently no active orders" rendered="#{empty orderList.activeOrders}" />

Chapter 10

309

<h:dataTable value="#{orderList.activeOrders}" var="ord" rendered="#{not empty orderList.activeOrders}" border="1" style="text-align:center">

<h:column> <f:facet name="header">Order Type</f:facet> #{ord.type} </h:column> <h:column> <f:facet name="header">Trade Instruction</f:facet> #{ord.tradeInstruction} </h:column> <h:column> <f:facet name="header">Account Number</f:facet> #{ord.accountNumber} </h:column> <h:column> <f:facet name="header">Number of Stocks</f:facet> #{ord.numberStocks} </h:column> <h:column> <f:facet name="header">Date Entered</f:facet> <h:outputText value="#{ord.entryDate}"> <f:convertDateTime pattern="hh:mm:ssa d-M-yyyy" /> </h:outputText> </h:column> <h:column> <f:facet name="header">Status</f:facet> #{ord.status} </h:column> </h:dataTable> </ui:define> </ui:composition></h:body></f:view>

6. Add the following into portlet.xml just before </portlet-app>: <portlet> <portlet-name>OrderList-JSF</portlet-name> <portlet-class>javax.portlet.faces.GenericFacesPortlet</portlet-class> <init-param> <name>javax.portlet.faces.defaultViewId.view</name> <value>/orderList.xhtml</value>

Frameworks in a Portal

310

</init-param> <init-param> <name>javax.portlet.faces.bridgeEventHandler</name> <value>gatein.cookbook.chapter10.OrderEventHandler</value> </init-param> <supports> <mime-type>text/html</mime-type> <portlet-mode>VIEW</portlet-mode> </supports> <portlet-info> <title>Order List JSF Portlet</title> </portlet-info> <supported-processing-event> <qname xmlns:jsf="urn:chapter10:jsf:order:event">jsf:OrderEvent</qname> </supported-processing-event> </portlet>

7. Add the following into web.xml: <context-param> <param-name>javax.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE</param-name> <param-value>true</param-value> </context-param>

8. Run the following command in the root of the project directory to build the web archive:>mvn clean package

9. Copy the generated web archive, chapter10-jsf-1.0.0.SNAPSHOT.war, from the target folder into the deployment folder of where you unpacked the GateIn installation.

10. Start the server and log in as an administrator.

11. Click on the top-menu navigation and use the following path: Group | Administration | Application Registry.

12. Click on Import Applications to make the new portlet available.

13. Access the portal page created in the Creating a JSF 2 portlet recipe.

14. Click on Site Editor | Edit Page and add the OrderList-JSF portlet to it below the Order-JSF portlet. The OrderList-JSF portlet can be found under the Chapter10-jsf category.

15. Click the Finish icon to save the page.

Chapter 10

311

16. The portlet page should look like the following screenshot:

17. Enter some data into the form and click on Create Order, and the portal page will redirect you to the Order Success! page while also loading the order into the Active Orders section of the page.

How it works...The following step details relate to modifying the existing order portlet.

Step 2 created an event class that can be passed between the portlets, defining the namespace the event will reside in.

Step 3 modified the create method, called during the process of saving an order, to set the OrderEvent onto the response.

Steps 4 and 5 updated the portlet.xml to include the definition of the OrderEvent and define that as the order portlet

The following step details are for the new order list portlet.

Step 1 creates a new @ApplicationScoped bean to hold the orders that are received from the event.

Frameworks in a Portal

312

Step 2 creates an event handler to process the incoming events. It uses the JSF resolver to retrieve the bean created in Step 1 and call addOrder on it passing the event Order.

Step 3 creates a @RequestScoped bean to retrieve the active orders from the @ApplicationScoped bean for the page.

Steps 4 and 5 create the page to display the list of active orders. At its simplest, there are outputText and dataTable components, with differing rendered attributes so that when there are no orders, a piece of text is displayed, but when there are orders, a table is displayed. The <f:facet> is present within <h:column> to define a column heading for display as if the code read <th>.

Step 6 defines the new portlet in portlet.xml and informs the portlet container that it can process the OrderEvent.

Step 7 informs JSF that any use of <f:convertDateTime>, such as in Step 5, should use the timezone of the machine instead of UTC for displaying time.

See also f The Creating a JSF 2 portlet recipe

f The Creating a RichFaces 4 portlet recipe

Creating a RichFaces 4 portletThis recipe shows how to create a new order list portlet, like the one from Using portlet events in JSF 2, but using components from RichFaces 4.

Getting readyThe following are required for this recipe:

f Apache Maven

f An IDE of your choice

f GateIn-3.2.0.Final-jbossas7-preview

f Order-JSF project from Creating a JSF 2 portlet and Using portlet events in JSF 2

Chapter 10

313

How to do it...To create a RichFaces portlet for displaying the active orders:

1. Inside the project's pom.xml, add the following dependency management section: <dependencyManagement> <dependencies> <dependency> <groupId>org.richfaces</groupId> <artifactId>richfaces-bom</artifactId> <version>4.2.2.Final</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement>

At the time of writing, the latest release of RichFaces 4 for JSF 2 is 4.2.2.Final. Please check http://www.jboss.org/richfaces for the latest available version.

2. Inside the project's pom.xml, add the following dependencies: <dependency> <groupId>org.richfaces.ui</groupId> <artifactId>richfaces-components-ui</artifactId> <exclusions> <exclusion> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.richfaces.core</groupId> <artifactId>richfaces-core-impl</artifactId> <exclusions> <exclusion> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> </exclusion> </exclusions> </dependency>

Frameworks in a Portal

314

3. Inside the project's pom.xml, add the following: <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <archive> <manifestEntries> <Dependencies>com.google.guava</Dependencies> </manifestEntries> </archive> </configuration> </plugin> </plugins> </build>

4. Create rfOrderList.xhtml in the src/main/webapp folder of the project.

5. Add the following content into rfOrderList.xhtml:<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" xmlns:rich="http://richfaces.org/rich" xmlns:a4j="http://richfaces.org/a4j">

<h:body> <ui:composition template="/templates/layout.xhtml"> <ui:define name="header"> </ui:define>

<ui:define name="content"> <h:outputText value="There are currently no active orders" rendered="#{empty orderList.activeOrders}" />

<rich:dataTable value="#{orderList.activeOrders}" var="ord" rendered="#{not empty orderList.activeOrders}" border="1" style="text-align:center">

<f:facet name="header"> <h:outputText value="Active Orders" /> </f:facet> <rich:column> <f:facet name="header">Order Type</f:facet>

Chapter 10

315

#{ord.type} </rich:column> <rich:column> <f:facet name="header">Trade Instruction</f:facet> #{ord.tradeInstruction} </rich:column> <rich:column> <f:facet name="header">Account Number</f:facet> #{ord.accountNumber} </rich:column> <rich:column> <f:facet name="header">Number of Stocks</f:facet> #{ord.numberStocks} </rich:column> <rich:column> <f:facet name="header">Date Entered</f:facet> <h:outputText value="#{ord.entryDate}"> <f:convertDateTime pattern="hh:mm:ssa d-M-yyyy" /> </h:outputText> </rich:column> <rich:column> <f:facet name="header">Status</f:facet> #{ord.status} </rich:column> </rich:dataTable> </ui:define> </ui:composition></h:body></f:view>

6. Add the following into portlet.xml just before </portlet-app>: <portlet> <portlet-name>OrderList-RichFaces</portlet-name> <portlet-class>javax.portlet.faces.GenericFacesPortlet</portlet-class> <init-param> <name>javax.portlet.faces.defaultViewId.view</name> <value>/rfOrderList.xhtml</value> </init-param> <init-param> <name>javax.portlet.faces.bridgeEventHandler</name> <value>gatein.cookbook.chapter10.OrderEventHandler</value> </init-param> <supports>

Frameworks in a Portal

316

<mime-type>text/html</mime-type> <portlet-mode>VIEW</portlet-mode> </supports> <portlet-info> <title>Order List RichFaces Portlet</title> </portlet-info> <supported-processing-event> <qname xmlns:jsf="urn:chapter10:jsf:order:event">jsf:OrderEvent</qname> </supported-processing-event> </portlet>

7. Run the following command in the root of the project directory to build the web archive:>mvn clean package

8. Copy the generated web archive, chapter10-jsf-1.0.0.SNAPSHOT.war, from the target folder into the deployment folder of where you unpacked the GateIn installation.

9. Start the server and log on as an administrator.

10. Click on the top-menu navigation by following this path: Group | Administration | Application Registry.

11. Click on Import Applications to make the new portlet available.

12. Access the portal page created in Creating a JSF 2 portlet.

13. Click on Site Editor | Edit Page and add the OrderList-RichFaces portlet to it below the OrderList-JSF portlet. The OrderList-RichFaces portlet can be found under the Chapter10-jsf category.

14. Click on the Finish icon to save the page.

15. Enter some data into the form and click on Create Order, and the portal page will redirect you to the Order Success! page while also loading the order into the OrderList-JSF and OrderList-RichFaces portlets on the page.

16. The portal page should look like the following screenshot:

Chapter 10

317

How it works...Steps 1 and 2 add the dependencies required for RichFaces 4. Step 1 enables Maven to retrieve all the correct versions of dependencies, as they are declared in an artifact that only contains dependency information. Step 2 excludes the guava dependency from the WEB-INF/lib of the build, as it is referenced as a JBoss AS7 manifest dependency in Step 3.

Steps 4 and 5 create the RichFaces page to display the list of orders. The change from Step 5 of the Using portlet events in JSF 2 recipe is switching from <h:dataTable> to <rich:dataTable> and from <h:column> to <rich:column>.

Step 6 defines the new portlet in portlet.xml, specifying the default view to be rfOrderList.xhtml.

Step 16 shows on a single portal page the styling differences, without making additional style modifications, between plain JSF 2 and RichFaces portlets.

See also f The Creating a JSF 2 portlet recipe

f The The Using portlet events in JSF 2 recipe

11Managing Portal Resources with

the Management Component

In this chapter, we will cover:

f Deploying the CLI to GateIn

f Retrieving a managed resource with RESTful Interface

f Exporting a portal page with the CLI

f Removing a portal page from the navigation

f Using the default portal as the foundation for a new portal site

IntroductionThe management component enables portal resources to be managed over common interfaces such as RESTful Service interfaces, Command Line Interfaces (CLI), and portlets.

Management extensions are able to register resources and operations through a set of APIs that can then be exposed over the common interfaces. Additional interfaces added to a management extension are available over common interfaces without further effort.

Managing Portal Resources with the Management Component

320

Deploying the CLI to GateInIn this recipe, we will deploy the GateIn management CLI to a GateIn instance, and then verify that the deployment was successful.

Getting readyThe following is required for this recipe:

f GateIn-3.2.0.Final

How to do it...To deploy the GateIn management CLI:

1. Go to https://repository.jboss.org/nexus/content/groups/public/org/gatein/management/gatein-management-cli/1.0.1-GA/ and download gatein-management-cli-1.0.1-GA.war.

2. Copy it into the standalone/deployments folder of the GateIn installation and start the server.

3. Open an SSH connection to the CLI by entering the following command:> ssh -p 2000 root@localhost

4. Enter the password for root when requested. With the standard GateIn install, the password is gtn.

5. The initial output from the CLI shell will be similar to the following screenshot:

Chapter 11

321

6. At the shell prompt, enter the following to connect to the management console:> mgmt connect

7. Enter the password for root user again.

8. At the shell prompt, enter the following to list all the nodes at the current location:> ls

9. The CLI shell will be similar to the following screenshot:

How it works...Step 6 connects the user to the management CLI of portal, which is the default portal container on GateIn. If you wanted to connect to a different portal deployed to GateIn, just pass the name of the portal as a -c argument in the mgmt connect command.

Step 8 demonstrates one of the commands available from the portal CLI. In this case, ls will list all the management extension components that are present in the portal. If ls were executed from within the mop folder, the result would be a list of JCR nodes at that level.

There's more...Now we show you how to build the CLI with the latest source available.

Building the CLI WAR from sourceTo take advantage of the latest updates to the GateIn management component CLI, it may sometimes be necessary to build it from source.

There are two options for retrieving the source for building:

f Use a GIT client to clone the repository at git://github.com/gatein/gatein-management.git

f Download the latest source as a ZIP from https://github.com/gatein/gatein-management/zipball/master

Managing Portal Resources with the Management Component

322

Building the source is then a matter of either going to the location where GIT cloned the repository to, or where the source ZIP was unpacked, and entering the following command on a command line:

> mvn clean install

For Maven to find any JBoss dependencies that are required to build gatein-management, follow the instructions on https://community.jboss.org/wiki/MavenSettings to add the JBoss repository.

The WAR file required to be deployed is then located in the cli/target folder.

See also f The Retrieving a managed resource with RESTful Interface recipe

Retrieving a managed resource with RESTful Interface

In this recipe, we will retrieve the managed resource for the portal sitemap using RESTful services within a browser.

Getting readyThe following is required for this recipe:

f GateIn-3.2.0.Final

How to do it...To retrieve the sitemap managed resource:

1. Start the server.

2. Open a browser and go to http://localhost:8080/rest/private/managed-components?format=xml

http://localhost:8080/rest/private/managed-components is equivalent to http://localhost:8080/rest/private/managed-components?op=read-resource

Chapter 11

323

3. The browser will display a page that looks similar to the following screenshot:

4. Navigate to the URL specified in the href link under the mop child by copying it into the address bar of the browser.

5. Navigate to the URL specified in the href link under the portalsites child by copying it into the address bar of the browser.

6. Navigate to the URL specified in the href link under the classic child by copying it into the address bar of the browser.

7. Navigate to the URL specified in the href link under the pages child by copying it into the address bar of the browser.

8. Navigate to the URL specified in the href link under the sitemap child by copying it into the address bar of the browser.

9. The browser will display a page that looks similar to the following screenshot:

Managing Portal Resources with the Management Component

324

How it works...Step 2 requests the top-most level of the REST entry point for managed components. The format=xml URL parameter simply asks the data to be returned in an XML format instead of the default, JSON, which makes it easier to visualize the structured content within a browser. Step 2 is also the equivalent to Step 8 in Deploying the CLI to GateIn.

Steps 4, 5, 6, 7, and 8 navigate through a child node of each managed resource in turn, until the sitemap is reached. It is also possible to directly navigate to a managed resource through REST, if the resource hierarchy is known.

See also f The Deploying the CLI to GateIn recipe

Exporting a portal page with the CLIIn this recipe, we will export the sitemap page managed resource from the portal using the CLI.

Getting readyThe following are required for this recipe:

f GateIn-3.2.0.Final

f Deploying the CLI to GateIn recipe from this chapter to have been completed

How to do it...To export the sitemap page from the portal:

1. Start the server.

2. Open an SSH connection to the CLI by entering the following command:> ssh -p 2000 root@localhost

3. Enter the password for root when requested. With the standard GateIn install, the password is gtn.

4. At the shell prompt, enter the following:> mgmt connect

Chapter 11

325

5. Enter the password for root user again.

6. At the shell prompt, enter the following:> export --file /tmp --filter site-name:classic;page-name:sitemap;nav-uri:sitemap /mop

When using the CLI, all directories and files referenced in import or export commands are references to the GateIn server system, and not the system in which the ssh command was issued from.

7. Opening the ZIP file that was created in /tmp with a filename of mop_timestamp.zip will show the following contents:

How it works...Step 6 executes the export command on the mop managed resource, with filters on the site-name, page-name, and nav-uri. Filtering on site-name will restrict the contents of the ZIP file to the site specified, in this case classic. Filtering on page-name and nav-uri with the value sitemap will restrict the content of pages.xml and navigation.xml to those entries related to the sitemap pages and navigation configuration.

There's more...Now we will see how to achieve the same outcome using the RESTful interface.

Exporting a portal page with RESTful InterfaceThe CLI commands used in this recipe to export a ZIP file that contains the sitemap page and navigation are equivalent to the following REST URLs:

f http://localhost:8080/rest/private/managed-components/mop/portalsites/classic.zip?filter=page-name:sitemap;nav-uri:sitemap

f http://localhost:8080/rest/private/managed-components/mop.zip?filter=site-name:classic;page-name:sitemap;nav-uri:sitemap

See also f The Removing a portal page from the navigation recipe

Managing Portal Resources with the Management Component

326

Removing a portal page from the navigationIn this recipe, we will export the sitemap configuration from the portal and update the navigation.xml file to hide the page. Once updated, the ZIP will be imported back into the portal to overwrite the existing configuration for the sitemap.

Getting readyThe following are required for this recipe:

f GateIn-3.2.0.Final

f Deploying the CLI to GateIn recipe from this chapter to have been completed

How to do it...To remove the sitemap page from the navigation:

1. Start the server.

2. Open an SSH connection to the CLI by entering the following command:> ssh -p 2000 root@localhost

3. Enter the password for root when requested. With the standard GateIn install, the password is gtn.

4. At the shell prompt, enter the following:> mgmt connect

5. Enter the password for root user again.

6. At the shell prompt, enter the following:> export --file /tmp/mop.zip --filter site-name:classic;page-name:sitemap;nav-uri:sitemap /mop

7. Open the ZIP file that was created above.

8. Edit navigation.xml in the portal/classic folder of the ZIP file by changing DISPLAY to HIDDEN in the <visibility> tag of the sitemap navigation element.

9. Go to http://localhost:8080/portal to see the SiteMap menu option next to Home on the homepage, as shown in the following screenshot:

Chapter 11

327

10. At the shell prompt, enter the following:> import --file /tmp/mop.zip /mop

11. Refresh the browser to reload the homepage of the portal and notice that the SiteMap menu option is no longer present next to Home, as seen in the following screenshot:

How it works...Step 6 exports the configuration from the classic portal, applying filters to only retrieve the sitemap page and navigation. It was not strictly necessary to filter for sitemap, but it makes it simpler to find the entry you want to edit and it also prevents inadvertently editing other navigation nodes or pages and potentially corrupting the portal.

Step 8 edits the navigation.xml to mark the SiteMap as HIDDEN.

Step 10 imports the mop.zip content back into the portal. As an import-mode was not specified, it uses the default of merge, which updates existing data that already exists and creates it when it doesn't. If the import-mode was set to conserve or insert in our recipe, then no change would have been applied to the portal.

There's more...Now we'll show some other ways the navigation of the portal can be modified.

Changing the navigation pathInstead of removing the SiteMap from the navigation, we could move it to beneath the Home menu.

To move the SiteMap menu item:

1. Connect to the CLI and at the shell prompt enter:> export --file /tmp/mop.zip --filter site-name:classic /mop

2. Open mop.zip and edit navigation.xml.

Managing Portal Resources with the Management Component

328

3. Move the <node> containing the sitemap to before </node> of the homepage node. It should now look as follows: <page-nodes> <node> <name>home</name> <label xml:lang="en">Home</label> <visibility>DISPLAYED</visibility> <page-reference>portal::classic::homepage</page-reference> <node> <name>sitemap</name> <label xml:lang="en">SiteMap</label> <visibility>DISPLAYED</visibility> <page-reference>portal::classic::sitemap</page-reference> </node> </node>

In the above navigation.xml snippet, the tags for non-English languages and additional navigation nodes have been removed for brevity. Removing them from the actual file would result in them being removed from the portal.

4. Save the changes and update mop.zip.

5. At the shell prompt enter:> import --file /tmp/mop.zip --importMode overwrite /mop

Note the use of the overwrite importMode. This ensures that the existing sitemap node at the same level as home is removed. Without the overwrite setting, the portal would have two sitemap menu options.

6. Go to http://localhost:8080/portal, or refresh the home page if you are already there. It should now appear as the following screenshot:

Chapter 11

329

Modifying the navigation node labelIt is also possible to edit the labels of a navigation node, and therefore change the name that is visible to the user within the portal.

To modify the navigation node label:

1. Connect to the CLI and at the shell prompt enter:> export --file /tmp/mop.zip --filter site-name:classic /mop

2. Open mop.zip and edit navigation.xml.

3. Find the sitemap <node> section and set the value within <label xml:lang="en"> to Page List.

Note that it would also be necessary to modify the label value for all languages that are present within the <node> definition. In this case, for brevity, only the English language label was modified.

4. Save the changes and update mop.zip.

5. At the shell prompt enter:> import --file /tmp/mop.zip --importMode overwrite /mop

6. Go to http://localhost:8080/portal. It should now appear as the following screenshot:

See also f The Exporting a portal page with the CLI recipe

Using the default portal as the foundation for a new portal site

In this recipe, we will the existing default portal site, referred to as classic, and modify the configuration files to rename the portal before importing it into GateIn.

Managing Portal Resources with the Management Component

330

Getting readyThe following are required for this recipe:

f GateIn-3.2.0.Final

f Deploying the CLI to GateIn recipe from this chapter to have been completed

How to do it...To replicate the classic portal:

1. Start the server.

2. Open an SSH connection to the CLI by entering the following command:> ssh -p 2000 root@localhost

3. Enter the password for root when requested. With the standard GateIn install, the password is gtn.

4. At the shell prompt, enter the following command:> mgmt connect

5. Enter the password for root user again.

6. At the shell prompt, enter the following command:> export --file /tmp/portal.zip --filter site-name:classic /mop

7. Open portal.zip, which was created above.

8. Rename the portal/classic folder to portal/testing.

9. Edit portal.xml by changing <portal-name> to testing, <label> to Testing and <description> to GateIn Testing.

10. Edit navigation.xml by replacing all references to the classic portal name with testing, including the <page-reference> nodes and the labels of samplePage and groupnavigation changed to Sample Page and Group Navigation respectively.

11. At the shell prompt, enter the following:> import --file /tmp/portal.zip /mop

Chapter 11

331

12. Go to http://localhost:8080/portal/testing and the new portal will appear as shown in the following screenshot:

How it works...Step 6 exports the entire classic portal site from GateIn, including portal configuration, pages, and navigation nodes.

The output from Step 6 can also be used as a way to migrate portal page content and structure from one environment to another. With the CLI import command, you can replace the environment's content with the newly exported ZIP file. Note that there are many other facets to a full migration, such as portlet applications and any portal customization.

Steps 8, 9, and 10 modify the configuration that was exported from the classic portal, and rename it to testing. Renaming the portal will create a new portal in GateIn as part of the import process, accomplished in Step 11.

12Managing Documents

Using External ECM Systems

In this chapter we will discuss the following topics:

f Creating a portlet to integrate a CMIS repository

f Creating a portlet to integrate JBoss ModeShape

f Creating a portlet to integrate Apache JackRabbit

f Creating a portlet to integrate Alfresco using Spring WebScripts

IntroductionIn this last chapter, we will consider some examples of how we can integrate GateIn with our existing Enterprise Content Management (ECM) system.

An ECM system is a class of an application that allows you to manage critical contents and documents for a company. Typically, this means that you need to take care of the contents using specific workflows and security policies.

The main benefit of the adoption of an ECM system is that you are able to manage structured and unstructured information related to any domain for creating and searching any type of content (file, records, and so on). You can also use it to preserve contents that need to be archived.

Managing Documents Using External ECM Systems

334

The definition of ECM has changed over the years for the concept to remain up-to-date with the latest changes introduced by the market and the new requirements relating to user experience and collaboration features.

This type of system allows for the definition of business rules for transforming and changing the status of processes depending on external or internal notifications, and for monitoring the lifecycle of contents.

For more information about ECM systems, please visit http://en.wikipedia.org/wiki/Enterprise_content_management

Creating a portlet to integrate a CMIS repository

In this recipe, we will describe how to implement standard portlets to integrate GateIn with an existing CMIS-compliant repository for creating and searching documents.

CMIS stands for Content Management Interoperability Services. CMIS is the latest standard arising from the Organization for the Advancement of Structured Information Standards (OASIS) consortium to account for different integration scenarios among ECM vendors.

CMIS could be considered as the SQL language dedicated to repositories. Now, for the first time, there is a unique standard to exchange contents between ECM systems and client applications; it is also independent from the programming language because it is defined with two protocol bindings: REST and SOAP.

The technical committee that wrote this specification includes people behind ECM vendors such as Alfresco, Microsoft, SAP, OpenText, Adobe, IBM, Nuxeo, and so on.

For more information about CMIS, please seehttp://www.oasis-open.org/committees/cmis

The CMIS implementation that we will use in this recipe is based on the Java language. We will use the OpenCMIS library provided by the reference implementation of the CMIS protocol of the Apache Software Foundation: the Apache Chemistry project.

More details about all the supported CMIS clients available in the Apache Chemistry project can be found at the following URL:http://chemistry.apache.org/

Chapter 12

335

Getting readyThe following are required for this recipe:

f Maven

f An IDE of your choice

f GateIn-3.2.0.Final-jbossas7-preview

How to do it...Let's start implementing a standard upload portlet for creating new documents in any CMIS-compliant repository:

1. Create a new Maven project within your IDE, specifying Group ID: com.packtpub.gatein.cookbook.chapter12, Artifact ID: gatein-cmis-portlets, and Packaging: war.

2. Inside the project's pom.xml, add the following dependencies: <dependencies> <dependency> <groupId>javax.portlet</groupId> <artifactId>portlet-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.chemistry.opencmis</groupId> <artifactId>chemistry-opencmis-client-impl </artifactId> <version>0.7.0</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.2.1</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.commons</groupId>

Managing Documents Using External ECM Systems

336

<artifactId>commons-lang3</artifactId> <version>3.0</version> </dependency> </dependencies>

3. Add the following to a new file in the WEB-INF folder named web.xml:<?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_3_0.xsd" version="2.5"></web-app>

4. Create a class named CmisUploadPortlet that extends javax.portlet.GenericPortlet within a package named com.packtpub.gatein.cookbook.ecm.cmis.

5. Add the following portlet definition with all the portlet preferences in your portlet.xml:<portlet> <portlet-name>CmisUpload</portlet-name> <portlet-class>com.packtpub.gatein.cookbook.ecm.cmis.CmisUploadPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> </supports> <portlet-info> <title>CMIS Upload Portlet</title> </portlet-info> <portlet-preferences> <preference> <name>endpoint</name> <read-only>false</read-only> </preference> <preference> <name>username</name> <read-only>false</read-only> </preference> <preference> <name>password</name> <read-only>false</read-only> </preference> <preference>

Chapter 12

337

<name>repositoryId</name> <read-only>false</read-only> </preference> <preference> <name>binding</name> <read-only>false</read-only> </preference> </portlet-preferences> </portlet>

6. We want to manage four main actions: two for the CMIS configuration and two for the upload. This means that you need to implement a display method similar to the following:@RenderMode(name = "view") public void display(RenderRequest request, RenderResponse response) throws PortletException, IOException { response.setContentType("text/html"); String forward = request.getParameter(CmisPortletConstants.FORWARD_PARAM); String forwardJsp = StringUtils.EMPTY; if(StringUtils.isEmpty(forward) || forward.equals(CmisPortletConstants.UPLOAD_VIEW)){ forwardJsp = CmisPortletConstants.UPLOAD_FORM_JSP_PATH; } else if(forward.equals(CmisPortletConstants.EDIT_OK_VIEW)){ forwardJsp = CmisPortletConstants.CONFIG_OK_JSP_PATH; request.setAttribute(CmisClientConfig.ATTRIBUTE_NAME, cmisClientConfig); } else if(forward.equals(CmisPortletConstants.UPLOAD_OK_VIEW)){ forwardJsp = CmisPortletConstants.UPLOAD_OK_JSP_PATH; String downloadUrl = request.getParameter(CmisPortletConstants.UPLOAD_OK_DOWNLOAD_URL_PARAM); request.setAttribute(CmisPortletConstants.UPLOAD_OK_DOWNLOAD_URL_PARAM, downloadUrl); request.setAttribute(CmisPortletConstants.DOC_PARAM, currentDocument); } else if(forward.equals(CmisPortletConstants.EDIT_VIEW)){ forwardJsp = CmisPortletConstants.CONFIG_FORM_JSP_PATH; request.setAttribute(CmisClientConfig.ATTRIBUTE_NAME, cmisClientConfig); } else if(forward.equals(CmisPortletConstants.ERROR_VIEW)){ forwardJsp = CmisPortletConstants.ERROR_JSP_PATH;

Managing Documents Using External ECM Systems

338

String errorMessage = (String) request.getParameter(CmisPortletConstants.ERROR_MESSAGE_PARAM); request.setAttribute(CmisPortletConstants.ERROR_MESSAGE_PARAM, errorMessage); } PortletRequestDispatcher requestDispacther = getPortletContext() .getRequestDispatcher(forwardJsp); requestDispacther.include(request, response); }

7. Once added to a page, the upload portlet will show an HTML form based on a Configure CMIS server button and the upload form.

8. During the first step of the web flow, the user has to provide the CMIS configuration. The editAction will be used to change the CMIS parameters that will be stored as portlet preferences in the portal. Accordingly, the feature behind the button Configure CMIS server will store preferences as user-specific information and so the next time the CMIS settings will be taken from the portal without the user needing to retype all the parameters. The following code excerpt details the method:@ProcessAction(name = "editAction") public void editAction(ActionRequest request, ActionResponse response) throws PortletException { String endpoint = request.getParameter(CmisPortletConstants.ENDPOINT_PARAM); String username = request.getParameter(CmisPortletConstants.USERNAME_PARAM); String password = request.getParameter(CmisPortletConstants.PASSWORD_PARAM); String repositoryId = request.getParameter(CmisPortletConstants.REPO_ID_PARAM); String binding = request.getParameter(CmisPortletConstants.BINDING_PARAM); //configure the CMIS client cmisClientConfig.setEndpoint(endpoint); cmisClientConfig.setUsername(username); cmisClientConfig.setPassword(password); cmisClientConfig.setRepositoryId(repositoryId); cmisClientConfig.setBinding(binding); PortletPreferences prefs = request.getPreferences(); prefs.setValue(CmisPortletConstants.ENDPOINT_PARAM, endpoint);

Chapter 12

339

prefs.setValue(CmisPortletConstants.USERNAME_PARAM, username); prefs.setValue(CmisPortletConstants.PASSWORD_PARAM, password); prefs.setValue(CmisPortletConstants.REPO_ID_PARAM, repositoryId); prefs.setValue(CmisPortletConstants.BINDING_PARAM, binding); try { prefs.store(); } catch (IOException e) { response.setRenderParameter(CmisPortletConstants.ERROR_MESSAGE_PARAM, e.getMessage()); response.setRenderParameter(CmisPortletConstants.FORWARD_PARAM, CmisPortletConstants.ERROR_VIEW); } //setup CMIS client try { session = CmisUtils.getCmisSession(cmisClientConfig); request.setAttribute(CmisClientConfig.ATTRIBUTE_NAME, cmisClientConfig); response.setRenderParameter(CmisPortletConstants.FORWARD_PARAM, CmisPortletConstants.EDIT_OK_VIEW); } catch (RuntimeException e) { response.setRenderParameter(CmisPortletConstants.ERROR_MESSAGE_PARAM, e.getMessage()); response.setRenderParameter(CmisPortletConstants.FORWARD_PARAM, CmisPortletConstants.ERROR_VIEW); } }

9. All the CMIS-specific operations can be encapsulated inside a utility class named CmisUtils. The implementation of getCmisSession(config) consists of the typical creation of the repository session. It can be based on either the REST or the SOAP binding, depending on the value of the binding parameter:public static Session getCmisSession(CmisClientConfig cmisClientConfig) throws RuntimeException{ try { SessionFactory factory = SessionFactoryImpl.newInstance(); Map<String, String> parameters = new HashMap<String, String>(); // Create a session parameters.clear();

// user credentials

Managing Documents Using External ECM Systems

340

parameters.put(SessionParameter.USER, cmisClientConfig.getUsername()); parameters.put(SessionParameter.PASSWORD, cmisClientConfig.getPassword()); // connection settings if(cmisClientConfig.getBinding().equals(BINDING_ATOM_VALUE)){ //AtomPub protocol parameters.put(SessionParameter.ATOMPUB_URL, cmisClientConfig.getEndpoint()); parameters.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value()); } else if(cmisClientConfig.getBinding().equals(BINDING_SOAP_VALUE)){ String endpoint = cmisClientConfig.getEndpoint(); //Web Services - SOAP - protocol parameters.put(SessionParameter.BINDING_TYPE, BindingType.WEBSERVICES.value()); parameters.put(SessionParameter.WEBSERVICES_ACL_SERVICE, endpoint+"/ACLService?wsdl"); parameters.put(SessionParameter.WEBSERVICES_DISCOVERY_SERVICE, endpoint+"/DiscoveryService?wsdl"); parameters.put(SessionParameter.WEBSERVICES_MULTIFILING_SERVICE, endpoint+"/MultiFilingService?wsdl"); parameters.put(SessionParameter.WEBSERVICES_NAVIGATION_SERVICE, endpoint+"/NavigationService?wsdl"); parameters.put(SessionParameter.WEBSERVICES_OBJECT_SERVICE, endpoint+"/ObjectService?wsdl"); parameters.put(SessionParameter.WEBSERVICES_POLICY_SERVICE, endpoint+"/PolicyService?wsdl"); parameters.put(SessionParameter.WEBSERVICES_RELATIONSHIP_SERVICE, endpoint+"/RelationshipService?wsdl"); parameters.put(SessionParameter.WEBSERVICES_REPOSITORY_SERVICE, endpoint+"/RepositoryService?wsdl"); parameters.put(SessionParameter.WEBSERVICES_VERSIONING_SERVICE, endpoint+"/VersioningService?wsdl"); } // create session if (StringUtils.isEmpty(cmisClientConfig.getRepositoryId())) { // get a session from the first CMIS repository exposed List<Repository> repos = null; try { repos = factory.getRepositories(parameters);

Chapter 12

341

return repos.get(0).createSession(); } catch (Exception e) { throw new RuntimeException("Error during the creation of the CMIS session", e); } } else { // get a session from a specific repository parameters.put(SessionParameter.REPOSITORY_ID, cmisClientConfig.getRepositoryId()); try { return factory.createSession(parameters); } catch (Exception e) { throw new RuntimeException("Error during the creation of the CMIS session", e); } }

} catch (Throwable e) { throw new RuntimeException("Error during the creation of the CMIS session", e); } }

10. Finally, once the CMIS session is configured and correctly instantiated, we can implement the upload feature with a specific uploadAction:@ProcessAction(name = "uploadAction") public void uploadAction(ActionRequest request, ActionResponse response) throws PortletException { //retrieve a CMIS session if(session==null){ cmisClientConfig = CmisUtils.getConfigFromPrefs(request); if(cmisClientConfig.isEmpty()){ editFormAction(request, response); } else { session = CmisUtils.getCmisSession(cmisClientConfig); } } else { //get content information from the upload form DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();

Managing Documents Using External ECM Systems

342

PortletFileUpload portletFileUpload = new PortletFileUpload( diskFileItemFactory); try { List fileItemList = portletFileUpload.parseRequest(request); Iterator fileIt = fileItemList.iterator(); Document document = null; while (fileIt.hasNext()) { FileItem fileItem = (FileItem) fileIt.next(); if(!fileItem.isFormField()){ document = CmisUtils.createDocument(session, fileItem); break; } } currentDocument = document; response.setRenderParameter(CmisPortletConstants.FORWARD_PARAM, CmisPortletConstants.UPLOAD_OK_VIEW); if(document!=null){ String downloadUrl = CmisUtils.getDocumentURL(session, document); response.setRenderParameter(CmisPortletConstants.UPLOAD_OK_DOWNLOAD_URL_PARAM, downloadUrl); } } catch (Exception e){ response.setRenderParameter(CmisPortletConstants.ERROR_MESSAGE_PARAM, e.getMessage()); response.setRenderParameter(CmisPortletConstants.FORWARD_PARAM, CmisPortletConstants.ERROR_VIEW); } response.setRenderParameter( CmisPortletConstants.FORWARD_PARAM, CmisPortletConstants.UPLOAD_OK_VIEW); } }

11. The creation of a new document is based on the OpenCMIS library using the createDocument(session,fileItem) method; it will return a Document instance for the new content:public static Document createDocument(Session session, FileItem fileItem) throws IOException {

Chapter 12

343

String fileName = fileItem.getName(); Map<String, Object> properties = new HashMap<String, Object>(); properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document"); properties.put(PropertyIds.NAME, fileName);

// content InputStream is = fileItem.getInputStream(); String mimetype = fileItem.getContentType(); ContentStream contentStream = new ContentStreamImpl(fileName, BigInteger.valueOf(fileItem.getSize()), mimetype, is);

// create a major version Folder parent = session.getRootFolder(); Document document = parent.createDocument(properties, contentStream, VersioningState.MAJOR); return document; }

Notice that you need to provide a parent in order to create any new document, because in the CMIS model, the new content will be a child of an existing parent.

12. The new document created in the CMIS repository will be presented in the JSP template with some details showing (path, name, ID, base type, and the download URL):<%@page import="com.packtpub.gatein.cookbook.ecm.cmis.CmisPortletConstants"%><%@page import="org.apache.chemistry.opencmis.client.api.Document"%><%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%><%Document document = (Document) request.getAttribute(CmisPortletConstants.DOC_PARAM);String downloadUrl = (String) request.getAttribute(CmisPortletConstants.UPLOAD_OK_DOWNLOAD_URL_PARAM);%><div class="portlet-section-header"><%if(document!=null){%>CMIS - Document uploaded correctly</div><br /><div class="portlet-section-body">

Managing Documents Using External ECM Systems

344

<p>The new document was uploaded correctly, here the details:</p> <p>Path: <%=document.getPaths().get(0)%></p> <p>Name: <%=document.getName()%></p> <p>Id: <%=document.getId()%></p> <p>Base type: <%=document.getBaseType().getQueryName()%></p> <p><a style="text-decoration: underline;" href="<%=downloadUrl%>" target="_blank">Download URL</a></p><%} else {%>CMIS - Error during document upload</div><br /><div class="portlet-section-body"><%}%><form action="<portlet:actionURL name="uploadFormAction"/>" method="POST"> <table> <tr> <td><input type="submit" name="submit" value="Upload another document" /></td> </tr> </table> </form></div>

How it works...The CMIS API is based on a set of services exposed by the CMIS repository. We have implemented the upload feature using a CMIS client. As you now know, in order to get a CMIS session, you need to provide the same information stored by the POJO named CmisClientConfig:

f Endpoint of the repository: This is the HTTP URL of the CMIS endpoint for the specific binding.

f Username: This is the username of the user session that you want to use.

f Password: This is the password for creating the user session.

f Repository ID (optional): This is the identifier of one of the repositories available from the CMIS endpoint. If not provided, the CMIS server will return the first repository available.

f Binding: This is used to specify the protocol (REST or SOAP).

Chapter 12

345

The domain model of CMIS is based on these basic objects:

f Document: This type of content can contain metadata and content streams

f Folder: This is a container for multiple documents

f Relationship: This manages the association between a target and a source document

f Policy: This helps to manage administrative policies for retention and similar needs

There's more...Now let's look at how to implement a full text search portlet for your portal in order to provide an advanced search feature against a CMIS repository.

Searching documentsThe search portlet that we are going to implement is based on the same CMIS configuration feature that we have described in the previous upload portlet, so we will describe only the search action.

The search portlet is based on a starting view based on an HTML form for inserting the keyword that will be used for the full text search against the CMIS repository:

1. First, we need to get the keyword parameter, check the CMIS session, and execute the search as follows:@ProcessAction(name = "searchResultsAction") public void searchResultsAction(ActionRequest request, ActionResponse response) throws PortletException { //get the keyword parameter String keyword = (String) request.getParameter(CmisPortletConstants.SEARCH_KEYWORD_PARAM); //check the CMIS session, if session is null we need to forward to the editAction if(session==null){ cmisClientConfig = CmisUtils.getConfigFromPrefs(request); if(cmisClientConfig.isEmpty()){ editFormAction(request, response); } else { session = CmisUtils.getCmisSession(cmisClientConfig); } }

Managing Documents Using External ECM Systems

346

//execute the search against the repository if(session!=null){ searchResults = CmisUtils.fullTextSearch(session, keyword); request.setAttribute(CmisPortletConstants.SEARCH_RESULTS_PARAM, searchResults); response.setRenderParameter(CmisPortletConstants.SEARCH_KEYWORD_PARAM, keyword); response.setRenderParameter(CmisPortletConstants.FORWARD_PARAM, CmisPortletConstants.SEARCH_RESULTS_VIEW); } if(StringUtils.isEmpty(keyword)){ response.setRenderParameter(CmisPortletConstants.ERROR_MESSAGE_PARAM, "Please insert a keyword to execute the full text search"); response.setRenderParameter(CmisPortletConstants.FORWARD_PARAM, CmisPortletConstants.ERROR_VIEW); } }

2. The encapsulated method fullTextSearch for executing the search is based on the query method exposed by the OpenCMIS API:public static List<DocumentVO> fullTextSearch(Session session, String keyword){ String statement = StringUtils.replace(CMIS_FULL_TEXT_QUERY, REPLACER, keyword); ItemIterable<QueryResult> searchResults = session.query(statement, false); List<DocumentVO> results = new ArrayList<DocumentVO>(); for(QueryResult hit: searchResults) { String name = hit.getPropertyById(PropertyIds.NAME).getValues().get(0).toString(); String objectId = hit.getPropertyById(PropertyIds.OBJECT_ID).getValues().get(0).toString(); Document document = (Document) session.getObject(objectId); String url = CmisUtils.getDocumentURL(session, document); DocumentVO documentVo = new DocumentVO(); documentVo.setName(name); documentVo.setUrl(url); results.add(documentVo); } return results; }

Chapter 12

347

3. The CMIS_FULL_TEXT_QUERY is based on this CMIS SQL statement:SELECT cmis:objectId, cmis:name FROM cmis:document WHERE CONTAINS('keyword')

4. Finally, all the results in the searchResults attribute can be presented in the JSP template iterating each element in a similar way:<%@page import="com.packtpub.gatein.cookbook.ecm.cmis.vo.DocumentVO"%><%@page import="java.util.List"%><%@page import="com.packtpub.gatein.cookbook.ecm.cmis.CmisPortletConstants"%><%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %><%String keyword = (String) request.getAttribute(CmisPortletConstants.SEARCH_KEYWORD_PARAM);List<DocumentVO> searchResults = (List<DocumentVO>) request.getAttribute(CmisPortletConstants.SEARCH_RESULTS_PARAM);%><div class="portlet-section-header">CMIS - Search results</div><br/><div class="portlet-section-body"><%if(searchResults!=null && keyword != null){%><p>Results for the keyword: <strong><%=keyword%></strong></p><p>Click on an item to download the document</p><form action="<portlet:actionURL name="searchFormAction" />" method="POST"> <table id="results"><%for(DocumentVO hit: searchResults) {%> <tr> <td><a style="text-decoration: underline;" href="<%=hit.getUrl()%>" target="_blank"><%=hit.getName()%></a></td> </tr>

<%}%> </table></form></div>

Managing Documents Using External ECM Systems

348

See also f The Creating a portlet to integrate JBoss ModeShape

f The Creating a portlet to integrate Apache JackRabbit

f The Creating a portlet to integrate Alfresco using Spring WebScripts recipe

Creating a portlet to integrate JBoss ModeShape

In this recipe, we will see how GateIn communicates with another ECM repository, JBoss ModeShape.

ModeShape is a JCR implementation provided by the JBoss Community. The main feature that makes it stand out is the repository federation.

The federation provides a single JCR interface for accessing and searching contents coming from different backend systems. You might think of a ModeShape repository containing information from a relational database, a filesystem, and perhaps even another Java content repository, for instance, Hippo CMS 7's content repository.

In this recipe, you will see some examples of a portlet that communicates with a ModeShape endpoint.

Getting readyTo follow this recipe, you will need:

f Maven 3.x

f A GateIn 3.2.0 installation (JBoss or Tomcat)

f ModeShape 2.8.1

ModeShape will be installed automatically through Maven. We will create two portlets, with one making a local connection and the other connecting remotely remotely to the repository to access the content.

Chapter 12

349

How to do it...In this sample, you will learn how to bootstrap a ModeShape repository at runtime using Maven. Let's look at the required steps:

1. Create the Maven pom.xml file as follows:<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>my.gatein</groupId> <artifactId>ModeshapePortlet</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version>

<dependencies> <dependency> <groupId>javax.portlet</groupId> <artifactId>portlet-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.jcr</groupId> <artifactId>jcr</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>org.modeshape</groupId> <artifactId>modeshape-jcr</artifactId> <version>2.8.1.Final</version> </dependency> <dependency> <groupId>org.modeshape</groupId> <artifactId>modeshape-cnd</artifactId> <version>2.8.1.Final</version> </dependency> <dependency> <groupId>org.modeshape</groupId> <artifactId>modeshape-connector-store-jpa</artifactId> <version>2.8.1.Final</version> </dependency> <dependency>

Managing Documents Using External ECM Systems

350

<groupId>org.modeshape</groupId> <artifactId>modeshape-connector-filesystem</artifactId> <version>2.8.1.Final</version> </dependency> <dependency> <groupId>org.modeshape</groupId> <artifactId>modeshape-web-jcr-rest-client</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.5.8</version> </dependency> </dependencies></project>

2. In portlet.xml, declare the two portlets as follows:<?xml version="1.0" encoding="UTF-8"?><portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"> <portlet> <portlet-name>LocalModeshapePortlet</portlet-name> <portlet-class>my.gatein.LocalModeshapePortlet</portlet-class> <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>Modeshape Local Portlet</title> </portlet-info> </portlet> <portlet> <portlet-name>RemoteModeshapePortlet</portlet-name> <portlet-class>my.gatein.RemoteModeshapePortlet</portlet-class> <supports> <mime-type>text/html</mime-type>

Chapter 12

351

<portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> <portlet-mode>help</portlet-mode> </supports> <portlet-info> <title>Modeshape Remote Portlet</title> </portlet-info> </portlet></portlet-app>

3. For the local connection, create a simple class my.gatein.LocalModeshapePortlet. In the following sample, you will create a new repository session in the processAction method:import org.modeshape.jcr.JcrRepositoryFactory;import java.util.ServiceLoader;import javax.portlet.*;import javax.jcr.*;...

@Override public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException {

Properties parameters = new Properties(); parameters.put("org.modeshape.jcr.URL", "file:/configRepository.xml?repositoryName=Cars"); Repository repository = null;

for (RepositoryFactory factory : ServiceLoader .load(RepositoryFactory.class)) { if (factory instanceof JcrRepositoryFactory) try { repository = factory.getRepository(parameters); Session session = (Session) repository.login("workspace1"); Node root = (Node) session.getRootNode(); printAllNodes(root, "");

} catch (RepositoryException e) { e.printStackTrace(); } } }

Managing Documents Using External ECM Systems

352

4. Put the XML configuration files of ModeShape in the src/main/resources folder of your Maven project. The example used is a repository for vehicles; it can be found in the ModeShape 2.8.1 source or by connecting directly to a code search engine. You can download grepcode from this URL: http://grepcode.com/snapshot/repository.jboss.org/nexus/content/repositories/releases/org.modeshape.examples/modeshape-example-repositories/2.8.1.Final/

Copy the following required configuration files:

� configRepository.xml: This is the main configuration file. It declares a federated vehicles repository composed of cars represented by an in-memory repository, airplanes represented by a JPA repository, and UFOs represented by a file-system repository.

� cars.cnd: This is the descriptor for the cars. It declares the structure of a JCR car node.

� aircraft.cnd: This is the descriptor for the airplanes. It declares the structure of a JCR aircraft node.

5. Now let us consider the Remote portlet. This time you don't need the configuration files because we expect the ModeShape repository to be installed remotely. The only difference in the local portlet is the actionProcess method of the my.gatein.RemoteModeshapePortlet:import javax.portlet.*;import javax.jcr.*;import org.modeshape.web.jcr.rest.client.IRestClient;import org.modeshape.web.jcr.rest.client.domain.QueryRow;import org.modeshape.web.jcr.rest.client.domain.Repository;import org.modeshape.web.jcr.rest.client.domain.Server;import org.modeshape.web.jcr.rest.client.domain.Workspace;import org.modeshape.web.jcr.rest.client.json.JsonRestClient;...

@Override public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException {

Server server = new Server(REMOTE_SERVER_URL, "username", "password"); Repository repository = new Repository("Cars", server); Workspace workspace = new Workspace("workspace1", repository);

IRestClient restClient = new JsonRestClient(); try {

Chapter 12

353

List<QueryRow> rows = restClient.query(workspace, "xpath", "//*"); ... } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }

6. The following code can be used to get nodes from the repository: public void printAllNodes(Node root, String space) throws RepositoryException { NodeIterator nodeIterator = (NodeIterator) root.getNodes(); while (nodeIterator.hasNext()) { Node node = (Node) nodeIterator.nextNode(); System.out.println(space + node); System.out.println(); PropertyIterator pi = node.getProperties(); while (pi.hasNext()) { Property property = pi.nextProperty(); System.out .println(space + "------------------------------------------------------"); System.out.print(space + property.getName() + " - "); try { System.out.println(property.getValue().getString()); } catch (ValueFormatException e) { for (Value value : property.getValues()) System.out.print(value.getString() + " - "); System.out.println(); } } printAllNodes(node, space + " "); } }

7. Build the Maven project and install the created WAR and portlets files in your portal as seen in the previous chapters.

8. Click on the Local Controller ModeShape link shown in the body of the window of the Local ModeShape Portlet. You will see in the log the complete list of the nodes of the workspace1 workspace.

Managing Documents Using External ECM Systems

354

How it works...In the pom.xml file, we declare enough libraries to execute an interaction with ModeShape. Of course, ModeShape provides several other plugins that we don't need to show in this recipe.

You can see the standard portlet and JCR API libraries needed for the creation of the portlet and the interaction with a common JCR repository.

The modeshape-jcr project is the core JCR implementation. The modeshape-cnd reads the cnd configuration files used to represent the JCR nodes structure.

If you look at the configRepository.xml file, you will see the declaration of a federated repository named Vehicles:

<mode:source jcr:name="Vehicles"> <mode:classname>org.modeshape.graph.connector.federation.FederatedRepositorySource</mode:classname> <mode:workspaces> <mode:workspace jcr:name="virtual"> <mode:projections> <!-- Project the 'Cars' content, starting with the '/Cars' node. --> <mode:projection jcr:name="Cars projection" mode:source="Cars" mode:workspaceName="workspace1"> <mode:projectionRules>/Vehicles/Cars => /Cars</mode:projectionRules> </mode:projection> <!-- Project the 'Aircraft' content, starting with the '/Aircraft' node. --> <mode:projection jcr:name="Aircarft projection" mode:source="Aircraft" mode:workspaceName="workspace2"> <mode:projectionRules>/Vehicles/Aircraft => /Aircraft</mode:projectionRules> </mode:projection> <!-- Project the 'UFOs' content, starting with the root node. --> <mode:projection jcr:name="UFO projection" mode:source="UFOs" mode:workspaceName="workspace1"> <mode:projectionRules>/Vehicles/UFOs => /</mode:projectionRules> </mode:projection></mode:projections> </mode:workspace> </mode:workspaces></mode:source>

Chapter 12

355

The Vehicles repository virtualizes all the three repositories: Cars, Aircrafts, and UFOs. The UFOs repository maintains the references in the filesystem through the modeshape-connector-filesystem project. The Aircraft repository needs the modeshape-connector-jpa project to be restored through the JPA framework.

The slf4j-log4j12 library is required for ModeShape. Always use the current version used by GateIn, 1.5.8, to avoid confusion between the versions.

In this sample, we are using JCR 2.0 API instead of the 1.0 version used by eXo JCR. This version allows us to use the javax.jcr.RepositoryFactory class that can be discovered as a service by the java.util.ServiceLoader class of the Java 6 API.

The ServiceLoader is a utility class provided by Java for loading services as an extension mechanism. A service is a set of interfaces and concrete classes that you need to add features to the application in a clean and transparent way. This approach allows you to extend any Java application dropping new resources in an external path/package. These external services will be registered as components. For GateIn, each extension must implement the interface org.gatein.management.spi.ManagementExtension, and GateIn will look up for extensions under the path /META-INF/services.For more information about ServiceLoader, please visit http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html.

This is because the file META-INF/services/javax.jcr.RepositoryFactory is set in the modeshape-jcr library, containing the implementation row:

org.modeshape.jcr.JcrRepositoryFactory # ModeShape JCR Implementation

In this manner, if you need to use different JCR repositories, you can use the same client to find them and filter them using similar code:

if (factory instanceof JcrRepositoryFactory) ...else ...

Through the org.modeshape.jcr.URL property passed to the JCR factory.getRepository(parameters) operation, the configuration file is read and the working repository is found thanks to the parameter value ?repositoryName=Cars.

The login operation starts the ModeShape repository. At first login, all objects are created and persisted in the filesystem or in the database according to the configuration of configRepository.xml.

Managing Documents Using External ECM Systems

356

When the thread ends, the repository also ends.

The remote connection needs the Rest API for ModeShape. This is installed through the modeshape-web-jcr-rest-client library declared in the pom.xml.

The remote connection needs a server configuration for ModeShape. It can be configured together with a webdav server. See Chapter 8, Migrating from Existing Portals for some information on webdav, or see the Red Hat doc for complete information on how to configure webdav in ModeShape:

http://docs.redhat.com/docs/en-US/JBoss_Enterprise_Data_Services/5/html/Metadata_Repository_Reference_Guide/web_access.html

See also f The Migrating a portlet that uses JCR recipe in Chapter 8, Migrating from

Existing Portals

Creating a portlet to integrate Apache JackRabbit

In this recipe, you will learn how to create two connections (local and remote) to JackRabbit by implementing two portlets.

Apache Jackrabbit is an implementation of the JSR-283, JCR 2.0 Specification provided by the Apache Software Foundation. More information about this project can be found here: http://jackrabbit.apache.org.

Getting readyTo follow this recipe, you will need:

f Maven 3.x

f A GateIn 3.2.0 installation (JBoss or Tomcat)

f JackRabbit 2.4.1

As for ModeShape, JackRabbit will be installed automatically through Maven. Prepare to create two portlets that make a local connection and a remote connection to JackRabbit for getting nodes.

Chapter 12

357

How to do it...As ModeShape and JackRabbit are very flexible libraries and can be installed in a very simple mode in different environments, the steps of this recipe will be very similar to the creation of the ModeShape portlets. Let's start:

1. Create the maven pom.xml file as follows:<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>my.gatein</groupId> <artifactId>JackrabbitPortlet</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version>

<dependencies> <dependency> <groupId>javax.portlet</groupId> <artifactId>portlet-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.jcr</groupId> <artifactId>jcr</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>org.apache.jackrabbit</groupId> <artifactId>jackrabbit-core</artifactId> <version>2.4.3</version> </dependency> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-core</artifactId> <version>3.0.3</version> </dependency> </dependencies></project>

Managing Documents Using External ECM Systems

358

2. For the local connection, create a simple my.gatein.LocalJackrabbitPortlet class. In the following sample, you will make the connection in the processAction method:import org.apache.jackrabbit.api.JackrabbitRepositoryFactory;import java.util.ServiceLoader;import javax.portlet.*;import javax.jcr.*;...

@Override public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException {

Properties parameters = new Properties(); parameters.put("org.apache.jackrabbit.repository.uri", "file:/tmp/jackrabbit"); Repository repository = null;

for (RepositoryFactory factory : ServiceLoader .load(RepositoryFactory.class)) { if (factory instanceof JackrabbitRepositoryFactory) try { repository = factory.getRepository(parameters); Session session = (Session) repository.login("default"); Node root = (Node) session.getRootNode(); printAllNodes(root, "");

} catch (RepositoryException e) { e.printStackTrace(); } } }

3. For the Remote Portlet, copy the Local Portlet in to a new my.gatein.RemoteJackrabbitPortlet class and simply change the connection properties in the actionProcess method:Properties parameters = new Properties();parameters.put("org.apache.jackrabbit.repository.uri", "jndi:/jackrabbit");parameters.put("java.naming.factory.initial", "org.apache.jackrabbit.core.jndi.provider.DummyInitialContextFactory");

Chapter 12

359

parameters.put("java.naming.provider.url", JACKRABBIT_SERVER_URL);

4. Use the JackRabbit JNDI Repository Factory instead of JackrabbitRepositoryFactory:...import org.apache.jackrabbit.commons.JndiRepositoryFactory;...

for (RepositoryFactory factory : ServiceLoader .load(RepositoryFactory.class)) { if (factory instanceof JndiRepositoryFactory) try { repository = factory.getRepository(parameters); ...

5. That's it. Now build the project, deploy on the Application Server, and refresh the new portlets using the Application Registry.

6. Click on the Local Controller JackRabbit link shown in the body of the window of the Local JackRabbit Portlet:

7. Access the GateIn log file. By default it is in the logs/server.xml file for JBoss or logs/catalina.out in Tomcat. You will see the complete list of the nodes of the default workspace. Here is an example of a single node:

How it works...In the pom.xml file, you need Portlet 2.0, JCR 2.0 API, and the core library of JackRabbit named jackrabbit-core. JackRabbit needs as a base installation Slf4j for the logging and Lucene libraries for the indexing.

GateIn is built differently with Lucene 3.1.0, JackRabbit needs to be version 3.0.3. Thanks to the isolation of the classloader, a mechanism supported in all application servers, you will never see any classloading issue.

Managing Documents Using External ECM Systems

360

The example of the local portlet needs to declare the directory where we build the JackRabbit repository. In the code of the my.gatein.LocalJackrabbitPortlet class, you declare org.apache.jackrabbit.repository.uri specifying the filesystem path (in the example this is /tmp/jackrabbit) where the repository will be created. During the creation, a default repository.xml file will be created in the chosen directory.

In this configuration, the repository restores the data through the filesystem.

Working with the javax.jcr.RepositoryFactory interface allows using one client together with different repositories. You could do one cycle and iterate all RepositoryFactory implementations present in the classloaders. It would be nice to write only a portlet for all repositories and choose the repository through portlet parameters. See Chapter 6, Developing Portlets for details about the portlet parameters.

In order to create a remote connection against a JackRabbit repository, you have to use JNDI. Make sure that your server is started before using the portlet. If you need more information about how to expose JackRabbit through JNDI, please see: http://jackrabbit.apache.org/shared-j2ee-resource-howto.html

In the remote client portlet, you simply need the following parameters:

f org.apache.jackrabbit.repository.uri: The JNDI name where the repository is installed, it must always start with jndi://

f java.naming.factory.initial: The context used by the client to receive the JNDI information

f java.naming.provider.url: The URL where the repository waits for the connections

See also f The Creating a portlet to integrate JBoss ModeShape recipe

f Chapter 6, Developing Portlets

Creating a portlet to integrate Alfresco using Spring WebScripts

In this recipe, we will describe how you can implement standard portlets using Spring WebScripts when you are using an Alfresco repository instance that is running in the same application server of the GateIn portal.

Chapter 12

361

Alfresco is an open source ECM system that allows you to implement specific ECM architectures and build your own domain model for any type of contents, workflows, rules, security constraints, and collaboration tools. For more information about Alfresco, please visit http://www.alfresco.com.

Spring WebScripts is a framework, initially contributed by Alfresco, which allows you to implement your own RESTful API. This framework supports scripting languages such as JavaScript and FreeMarker.

More details about Spring WebScripts can be found at the following URL:http://www.springsurf.org/sites/1.0.0.M3/spring-webscripts/spring-webscripts-documentation/

The main components that you can use to implement a WebScript are as follows:

f Descriptor (DESC): Describes the service, URLs, authentication, and the output

f Controller (JavaScript or Java): This is the business logic used to read and write against the repository (optional)

f Template (FTL): This is the FreeMarker template, integrated to only read contents from the repository (it is optional if the WebScript is implemented using an AbstractWebScript backed by Java language)

FreeMarker is a template engine used to build custom views. For more information about FreeMarker, see the following URL:http://freemarker.sourceforge.net/

The template could be optional because in the case of a Java-Backed WebScript implementation, you can only use a descriptor and a Java class to implement your WebScript in the same way you implement a custom action with any other MVC framework. This is because AbstractWebScript allows you to directly control of the HTTP request and response.

For more information about Java-Backed WebScripts, you can visit http://wiki.alfresco.com/wiki/Java-backed_Web_Scripts_Samples.

Managing Documents Using External ECM Systems

362

We will demonstrate how to implement two services exposed by WebScripts for providing an advanced search portlet in the portal:

f A WebScript is available via GET for showing the search form (DESC + FTL)

f A WebScript is available via POST for executing search and showing the results (DESC + JavaScript + FTL)

Getting readyThe following are required for this recipe:

f An IDE of your choice

f GateIn-3.2.0.Final-tomcat6

f Alfresco repository application (alfresco.war)

We are assuming that the Alfresco repository (alfresco.war) is correctly deployed in the same application server of GateIn. This scenario allows us to simplify examples without implementing custom and remote authenticators for the WebScripts framework.

How to do it...We will start implementing the first WebScript dedicated to render the search form inside the portlet. We want a WebScript configured to be invoked only using the GET method of the HTTP protocol:

1. First, create in the extensionRoot of Alfresco the following path: /alfresco/extension/templates/webscripts.

2. Create the descriptor. This consists of a new file named fulltextsearch.get.desc.xml that describes the service with this content:<webscript> <shortname>Full Text Search Form</shortname> <description>Show the keyword search form</description> <url>/gatein/search</url> <url>/gatein/search.portlet</url> <authentication>guest</authentication> <format default="html" /> <transaction allow="readonly">required</transaction> <family>GateIn - ECM - Portlets</family></webscript>

Chapter 12

363

3. Now we need to create a new file for the FreeMarker template named fulltextsearch.get.html.ftl and enter a similar snippet:<div class="portlet-section-header">Alfresco - Full Text Search</div><br/><div class="portlet-section-body"><div id="alfrescoSearch"> <form name="alfrescoSearchForm" onSubmit="return submitAlfrescoSearch();"> <table> <tr> <td<span class="portlet-form-label">Keyword:</span></td> <td> <input class="portlet-form-input-field" type="text" name="keyword" /> </td> <td> <input class="portlet-form-button" type="submit" value="Search"/> </td> </tr> </table> </form> <br /> <table id="results"> <tr> <td></td> </tr> </table></div>

4. Finally, you need to add the portlet configuration for the WebScript in alfresco.war/WEB-INF/portlet.xml:<portlet> <description>GateIn - ECM - Full Text Search</description> <portlet-name>FullTextSearch</portlet-name> <portlet-class> org.springframework.extensions.webscripts.portlet.WebScriptPortlet</portlet-class> <init-param> <name>authenticator</name> <value>webscripts.authenticator.jsr168</value> </init-param> <init-param>

Managing Documents Using External ECM Systems

364

<name>scriptUrl</name> <value>/alfresco/168s/gatein/search</value> </init-param>

<supports> <mime-type>text/html</mime-type> <portlet-mode>VIEW</portlet-mode> </supports> <portlet-info> <title>Alfresco Full Text Search</title> <short-title>Alfresco Search</short-title> </portlet-info> </portlet>

How it works...The important parameters of portlet.xml that must be described are as follows:

f portlet-class: This is the implementation class of the portlet

f authenticator: This is the authenticator class used to authenticate user sessions

f scriptUrl: Consists of the context root that starts with alfresco/168s with a suffix based on the WebScript URL available in Alfresco

The wrapper class that is responsible for exposing WebScript as portlets is provided by the Spring WebScripts framework. This class is WebScriptPortlet, and it allows declaring inside the portlet-class element in the portlet.xml of any web application deployed in the same application server of GateIn.

In this way, a portlet can be available in the portal and it is backed by a WebScript implementation.

You can set two different types for the authenticator parameter:

f webscripts.authenticator.jsr168: This uses the same user session taken from the portal (this is the default authenticator, which is used when you don't specify the authenticator parameter)

f webscripts.authenticator.jsr168.webclient: This uses the same user session taken from the portal, but synchronizes the session with the Alfresco Explorer application

If you need a different type of authenticator, you can implement your own and then declare it using the identifier of the Spring bean as the value of the authenticator parameter.

Chapter 12

365

In this way, you can implement your own Spring WebScripts for any type of operations and then you can declare them as portlets.

There's more...We will show another example of processing a WebScript using the JavaScript controller to execute a query against the Alfresco repository and return the results using a FreeMarker template.

Searching documentsLet's start to implement the complementary WebScript for finishing the advanced search portlet that we started in the previous section. This new WebScript service must be exposed by the HTTP POST method, and it will need a keyword parameter for executing the full text search:

1. First, as we have already seen, we can start declaring the WebScript descriptor; this time the file will be named fulltextsearch.post.desc.xml because we want to expose this result service via HTTP POST method. We are also declaring this WebScript with an output format portlet, because there are some Ajax calls to refresh the view context of the previous WebScript:<webscript> <shortname>Full Text Search Results</shortname> <description>Show the results of the keyword search</description> <url>/gatein/search?keyword={keyword}</url> <authentication>guest</authentication> <format default="portlet" /> <transaction allow="readonly">required</transaction> <family>GateIn - ECM - Portlets</family></webscript>

2. The WebScript controller can be implemented using a Lucene query against the SearchService of Alfresco with a JavaScript file fulltextsearch.post.js:var keyword = "";keyword = args.keyword;var searchResults = null;if(keyword != undefined && keyword!=""){ var luceneQuery = "TEXT:\""+keyword+"\""; searchResults = search.luceneSearch(luceneQuery);} else { status.code = 400; status.message = "Keyword not provided."; status.redirect = true;}model.keyword = keyword;model.searchResults = searchResults;

Managing Documents Using External ECM Systems

366

3. Finally, the output template will iterate searchResults to show a portlet section inside the FreeMarker file named fulltextsearch.post.portlet.ftl:<p>Results for the keyword: <strong>${keyword}</strong></p><p>Click on an item to download the document</p>

<#if searchResults?exists>

<table id="results">

<#list searchResults as hit> <tr> <td><a style="text-decoration: underline;" href="/alfresco${hit.url}" target="_blank">${hit.properties.name}</a></td> </tr> </#list>

</table><#else>

<p>No results found.</p>

</#if><br /><a href="#" onClick="viewSearchForm();">Return to the search form</a><br />

See also f The Creating a portlet to integrate a CMIS repository

f The Creating a portlet to integrate JBoss ModeShape

f The Creating a portlet to integrate Apache JackRabbit recipe

IndexSymbols329 Portlet Bridge 227@ApplicationScoped bean 311_ctx key 205<f:facet> 312@RenderMode annotation 159, 161@RequestScoped bean 312

Aaccess-permissions 66Account Info tab 91, 92action

used, to pass form parameters 162-164actionProcess method 358Add new Node operation 52aircraft.cnd 352Alfresco

about 361URL 361

Alfresco integrationportlet creating for, Spring WebScripts used

360-365search portlet 365, 366

And Close button 278annotation

using, instead of configuration file 198Apache JackRabbit integration

portlet, creating for 356-360Apache Lucene 17application-ref 66Application Registry node 218Application Registry Service configuration

273applications

securing, steps for 150-152

app schema 204Arjuna 230authenticated portlet

about 231migrating, steps for 231-233

authenticator parameter 364authority 89

BbaseURL, LDAP query 105binary package

GateIn, installing from 8, 9Business Info tab 92

Ccars.cnd 352categories

securing, steps for 145-150Chemistry project

URL 334Chromattic

URL 150classpath schema 204clean phase 13CLI

CLI WAR, building from source 321, 322deploying, to GateIn 320, 321portal page, exporting with 324, 325

CLI WARbuilding, from source 321source, retrieving 321

Clone Node operation 52CMIS

about 334URL 334

368

CMIS API 344CmisUtils 339Command Line Interfaces. See CLIComponentConfig annotation 198component-plugins-configuration.xml 239configRepository.xml 352configuration file

annotation, using 198configuration.xml

about 241application block 196ui-component-config block 196

Content Management Interoperability Services. See CMIS

content storageconfiguring 14-17

context.xml file 135Copy Node operation 52createDocument(session,fileItem)

method 342createEntryAttributeValues attribute 114create method 306ctxDNs attribute 114Cut Node operation 52

Ddashboard

managing 53managing, steps for 54-56

Decoration Themes tab 50Delete Node operation 52demo user information 222deploy directory 245deployers 212deploy(Iterable<GadgetImporter>

gadgets) 258Desktop SSO

SPNEGO integration 122, 123, 127development environment

setting up 22setting up, steps 23-28

display() method 160, 161dlr key 205doView method 159driver class 17

EEAR

components, including in 76, 79, 84, 87, 88Eclipse IDE

URL, for downloading 22ECM

about 237, 333URL 334

Edit Node page operation 52Edit this Node operation 52Enterprise Content Management. See ECMEnterprise Deployment Descriptors (DD) 129EPP (Enterprise Portal Platform) 228events

used, for portlet communication 181-192eXo

wikipedia, URL 194exoJCR 14

physical storage elements 14eXo JCR

about 239URL 150

eXo.require row 220expire operation 196

Ffactory.getRepository(parameters)

operation 355file schema 204filterStocks method 181fmt:setBundle tag 169forgot password feature 18form parameters

passing, action used 162-164FreeMarker

URL 361

GGadget Deployer 269Gadget Registry Service

about 258deploy(Iterable<GadgetImporter>

gadgets) 258getAllGadgets 258

369

getGadget(String name) 258getGadgetUrl(String name) 258removeGadget(String name) 258saveGadget(Gadget gadget) 258

gadgetsabout 43, 253category, changing 270-274creating, steps for 266, 267, 269importing 254importing, steps for 254-257new Skype Gadget, creating 264-269removing 260removing, steps for 260-263resizing 275resizing, steps for 275, 276server 259user preferences, setting 280-283

Gadgets button 275gadgets.hostname field 259gadget wrapper portlet

about 277creating, steps for 277-280

Gadget Wrapper Portlet 47GateIn

about 193CLI, deploying to 320, 321configuring, to send e-mails 18, 19configuring, with JAAS 132deployers 212installing, from binary package 8, 9installing, from source code 10-12LDAP, configuring with 100-114running, on machine 20, 21starting, with JBoss AS 6 instance 21, 22starting, with JBoss AS 7 instance 22

gatein-domain 132GateIn instance

pre-requisites 8, 9GenericPortlet 160, 161getAllGadgets 258getGadget(String name) 258getGadgetUrl(String name) 258getTransactionManager() method 230GIMP 245Github

URL 10

Google GadgetsURL 253

groovy scripts_ctx key 205dlr key 205isLT key 205isRT key 205locale key 205nodeurl key 205orientation key 205portletContent key 205schemas 204

groovy scripts, schemasapp schema 204classpath schema 204file schema 204jar schema 204par schema 204system schema 204war schema 204

groupLDAPClasses 105groupName attribute 233groupObjectClassFilter, LDAP filter 106groupObjectClass, LDAP object 106groups

managing 93managing, steps for 94, 95Organization Service, configuring 99removing 96updating 96users, assigning 97, 98

groupsURL, LDAP query 105

HHome Info tab 92

Ii18n 220idAttributeName attribute 112, 114Import Applications button 267, 274importJavascript (module_id) 219init-param 297init parameters 194installing

GateIn, from binary package 8, 9

370

GateIn, from source code 10, 11install phase 13isLT key 205isRT key 205

JJAAS

GateIn, configuring with 132GateIn configuring with, JBoss AS (5.x and 6.x)

used 133, 134JAAS modules

selecting 136, 137JAAS realm

confiuring, Tomcat used 134, 135jar schema 204Java Authentication Authorization Service. See

JAASJava-Backed WebScript implementation 361Java-Backed WebScripts

URL 361Java Community Process. See JCPJava Content Repository (JCR) 14Java Development Kit (JDK) 8Javascript Config Service 220JavaScript resources

adding, to portlet 218-220Java Specification Request. See portlet 2.0

specification basedjavax.portlet.GenericPortlet class 195javax.portlet.Portlet interface 195JBoss AS 6 instance

GateIn, starting with 21, 22JBoss AS 7 instance

GateIn, starting with 22JBoss Cache 241JBoss ModeShape integration

portlet, creating for 348-356JBoss Portal 228JBoss Portlet Bridge

URL 296JCP 227JCR

about 237used, for migrating portlet 237, 238

JCR caching 241jcr-configuration.xml 239

JDBCURL 17

JJBoss AS (5.x and 6.x)used, for configuring GateIn with JAAS 133,

134jQuery

used, for creating portlet 298-305using, in portlet 304

JSF 2portlet events, creating 305-311

JSF 2 portletcreating 285creating, steps for 286-296requisites 286

JSF backing bean 297js-path module 219js-priority module 219JSR 289 specification 232JSR-303 285JTA (Java Transaction API) 228

KkeyTab parameter 123

Llabel element 74layering 248layout, portals

changing 34-36LDAP

configuring, with GateIn 100-114ldapCreatedTimeStampAttr,

LDAP attribute 106ldapDescriptionAttr, LDAP attribute 106ldapModifiedTimeStampAttr,

LDAP attribute 106LDAP Service 106LDAP store

integrating with 100local-config tag 226Local Controller ModeShape link 353locale

in portlet, handling 220-223locale key 205Localization Lifecycle 225lock-config.xml 241

371

login pagecreating, for new portal 138, 139

LuceneURL 17

Lucene 3.1.0 359Lucene Query Language 17

Mmanaged resource

retrieving, with RESTful interface 322maxConnection 102membershipLDAPClasses 105membershipObjectClassFilter,

LDAP filter 106membershipObjectClass, LDAP object 106Membership panel 32membershipTypeLDAPClasses 105membershipTypeMemberValue, LDAP

attribute 105membershipTypeNameAttr, LDAP

attribute 106membershipTypeObjectClassFilter,

LDAP filter 106membershipTypeObjectClass,

LDAP object 106membershipTypeRoleNameAttr 105membershipTypeURL, LDAP query 105minConnection 102ModeShape 348Move Down operation 52Move Up operation 52my.gatein.LocalJackrabbitPortlet

class 358, 360

Nnavigation

node label, modifying 329node name, localizing 170path, changing 327, 328sitemap page, removing 326tree managing, XML used 73-75

navigation node labelmodifying 329

navigation pathchanging 327, 328

navigation treemanaging 50-53managing, XML used 73

nodeurl key 205NT LAN Manager (NTLM) 122

OOpenSSO 118Optional, LoginModule 134Oracle page

URL 229Organization for the Advancement of

Structured Information Standards (OASIS) 334

Organization Framework 99organization service 98org.exoplatform.application.gadget.Gadget

257org.exoplatform.portal.resource.

GateInSkinConfigDeployer file 210org.exoplatform.portal.resource.

GateInSkinConfigRemoval file 210org.exoplatform.portal.resource.SkinService

class 211org.exoplatform.portal.webui.application.

UIGadget 257org.exoplatform.portal.webui.application.

UIPortlet 197org.exoplatform.portal.webui.portal.UIPortal

197org.exoplatform.portal.webui.workspace.

UIPortalApplication 197org.exoplatform.services.jc r.config.

RepositoryServiceConfiguration 239org.exoplatform.services.jcr.ext.distribution.

DataDistributionManager 239org.exoplatform.services.jcr.ext.hierarchy.

NodeHierarchyCreator 239org.exoplatform.services.jcr.

RepositoryService 239org.exoplatform.services.jcr.webdav.

WebDavServiceImpl 239org.exoplatform.services.organization.idm.

CustomMembershipLoginModule 137org.exoplatform.services.security.j2ee.

DigestAuthenticationJbossLogin Module 137

372

Page Selector tab 53parameters

passing, to render instead Action URL 165ParentAppStateManager 197parentMembershipAttributeName

attribute 114parentMembershipAttributePlaceholder

attribute 114par schema 204password 102passwordAttributeName attribute 112Permission Setting tab 32PicketLink

about 99, 107example, files 107

PicketLink IDM 14pico container

URL 206portal-name module 219portal page

exporting, with CLI 324, 325exporting, with RESTful Interface 325managing, steps for 37-39removing 42removing, from navigation 326, 327

portalsdefault portal, used for new portal

site 329, 331default portal, URL 30groups, managing 93-95groups, removing 96groups, updating 96layout, changing 34-36logging into, administrator account

used 93-95login page, creating for 138, 139managing 30managing, steps for 31-33managing, XML used 60, 62, 66page, removing 42pages, managing 37-41pages managing, XML used 66, 67public access, setting 34removing 34securing 130, 131user membership, removing 93users, creating 92

org.exoplatform.services.security.j2ee.DigestAuthenticationJettyLoginModule 137

org.exoplatform.services.securit y.j2ee.DigestAuthenticationTomcatLogin Module 137

org.exoplatform.services. security.j2ee.JbossLoginModule 137

org.exoplatform.services.security.j2ee.JettyLoginModule 137

org.exoplatform.services.security.j2ee.TomcatLoginModule 137

org.exoplatform.services.security.jaas.DefaulLoginModule 136

org.exoplatform.services.security.jaas.IdentitySetLoginModule 136

org.exoplatform.services.security.jaas.SharedStateLoginModule 136

org.exoplatform.services.transaction.TransactionService 230

org.exoplatform.web.application.ApplicationLifecycle interface 224

org.exoplatform.web.application.javascript.JavascriptConfigDeployer file 210

org.exoplatform.web.security.PortalLoginModule 136

org.exoplatform.webui.core.UIComponent class 198

org.exoplatform.webui.core.UIComponentDecorator 197

org.exoplatform.webui.core.UIContainer 197org.exoplatform.webui.core.

UIPortletApplication 197org.exoplatform.webui.core.

UIPortletApplication class 195org.exoplatform.webui.form.UIForm 197org.gatein.wci.security.WCILoginModule 136org.jboss.cache.transaction.

TransactionManagerLookup 230orientation key 205

PPage Management console 42page-reference 75pages

securing 142, 144

373

portlet descriptorURL 161

portlet descriptor file 161portlet events

creating, in JSF 2 305-311portlet-ref 66portlets

securing, steps for 153-155Portlet Setting tab 216preferences 66PrintWriter 304

using, within portlet class 161private render parameter 164profileLDAPClasses 105profileURL, LDAP query 105Project Object Model (POM) 12providerURL 102Public Render Parameters 172

Rregistered portlets

managing 42-47managing, XML used 68, 69

removeGadget(String name) 258renderChildren() method 203renderCSS method 211renderTemplate method 205renderURL portlet tag 165repository-configuration.xml

about 240portal-system 240portal-work 240system 240

Required attribute 229Required, LoginModule 134Require feature tag 282Requisite, LoginModule 134Resource Bundle Service 225, 226ResourceRequest portlet 304RESTful interface

managed resource, retrieving 322RESTful Interface

portal page, exporting with 325RichFaces 4 portlet

creating, steps for 312-316rootdn 102

users, managing 90-92users, removing 92

portletabout 42, 194categories configuration, defining 70-73communicating between, events

used 181-192creating for Alfresco integration, Spring

WebScripts used 360-365creating, for Apache JackRabbit integration

356, 358, 360creating, for CMIS repository

integration 334-344creating, for JBoss ModeShape integration

348-356creating, jQuery used 298-305creating, portlet 2.0 specification

based 158-161creating, to search stocks 172-177, 180doView method from GenericPortlet,

overriding 161interface, implementing 162JavaScript resources, adding 218-220locale, handling 220-223migrating 237migrating, JCR used 237, 238PrintWriter, using with portlet class 161registered portlets, managing 42-45registered portlets managing, XML used 68,

69search portlet 345-347settings, managing 49, 50skin, handling 207, 208, 210

portlet 2.0 specification basedportlet, creating 158-161

portlet:defineObjects tag 164portlet application 172PortletApplicationController 195, 196Portlet Bridge 285portlet-class element 364portlet container 160portlet content

localizing, user locale used 166-169navigation node name, localizing 170, 171

portletContent key 205

374

TTemplateStatisticService 206test phase 13Tomcat

used, for configuring JAAS realm 134, 135Tomcat 6.x bundle 9transactional portlet

about 228migrating, steps for 228-230

transactions 228

Uui-component-config tag 198uicomponent key 205UIGadget component 257UIGadgetPortlet class 280UIPortalNavigation component 202updatePrice method 304userDisplayNameAttr, LDAP attribute 105userFirstNameAttr, LDAP attribute 105userLastNameAttr, LDAP attribute 105userLDAPClasses 105user locale

used, to localize portlet content 166-169userMailAttr, LDAP attribute 105User Membership tab 92userName attribute 233userObjectClassFilter, LDAP query 105userPassword, LDAP attribute 105user preferences, gadgets

setting 280-283user profile

custom field, adding 117, 118User Profile tab 91, 92users

assigning, to groups 97, 98creating 92default membership, setting 115, 116managing 90managing, steps for 91, 92memberships, removing 93other fields, interrogating 233, 234, 237removing 92synchronizing, steps for 140, 142

SSave button 283saveGadget(Gadget gadget) 258security-constraint element 131Select Icon tab 50Select Page window 53Select Permission button 32serveResource method 305serverName 102show-application-mode 214show-application-state 214show-info-bar 66, 214Show info bar by default 33Simple and Protected GSSAPI Negotiation

Mechanism. See SPNEGOSingle Sign On (SSO) 14, 90sitemap managed resource

retrieving 322, 323sitemap page

removing, from navigation 326SiteMap page 30skin

about 244importing, from existing web site 244-251in portlet, handling 207-210

skin-name tag 211Skype Talk Gadget 275source code

GateIn, building from 11-13GateIn, installing from 10installing from, pre-requisites 10-12

SPNEGOabout 122GateIn configuration 122, 123, 127integration, for Desktop SSO 122, 123, 127

Spring WebScriptssearch portlet 365, 366URL 361used for creating portlet, to integrate Alfresco

360-365State Manager

operations 196store operation 196Sufficient, LoginModule 134

375

WebUIabout 193annotation, using 198creating, steps for 194, 195extension class, selecting 197lifecycle, setting 198, 200multiple annotated configurations,

example 198Web User Interface. See WebUIwindow style 212writeCustomizedOnLoadScript

(Writer writer) 220writeJavascript (Writer writer) 219WSRP Portlet 149

XXML

used, for managing navigation tree 73-75used, for managing portal pages 66used, for managing portals 60, 62, 66

XML element 67

YYouTube gadget

importing, in portal 254, 256

userURL, LDAP query 105userUsernameAttr, LDAP attribute 105

Vviews

about 200creating, steps for 200-203

WWAR file 322war schema 204Watchlist-jQuery portlet 303web-app tag 153Web-based Distributed Authoring and

Versioning. See WebdavWebdav 243webscripts.authenticator.jsr168 364webscripts.authenticator.jsr168.webclient

364WebScripts implementation

components 361Web SSO

integrating with 119-121Web SSO integration 119-121

Thank you for buying GateIn Cookbook

About Packt PublishingPackt, pronounced 'packed', published its first book "Mastering phpMyAdmin for Effective MySQL Management" in April 2004 and subsequently continued to specialize in publishing highly focused books on specific technologies and solutions.

Our books and publications share the experiences of your fellow IT professionals in adapting and customizing today's systems, applications, and frameworks. Our solution based books give you the knowledge and power to customize the software and technologies you're using to get the job done. Packt books are more specific and less general than the IT books you have seen in the past. Our unique business model allows us to bring you more focused information, giving you more of what you need to know, and less of what you don't.

Packt is a modern, yet unique publishing company, which focuses on producing quality, cutting-edge books for communities of developers, administrators, and newbies alike. For more information, please visit our website: www.packtpub.com.

About Packt Open SourceIn 2010, Packt launched two new brands, Packt Open Source and Packt Enterprise, in order to continue its focus on specialization. This book is part of the Packt Open Source brand, home to books published on software built around Open Source licences, and offering information to anybody from advanced developers to budding web designers. The Open Source brand also runs Packt's Open Source Royalty Scheme, by which Packt gives a royalty to each Open Source project about whose software a book is sold.

Writing for PacktWe welcome all inquiries from people who are interested in authoring. Book proposals should be sent to [email protected]. If your book idea is still at an early stage and you would like to discuss it first before writing a formal book proposal, contact us; one of our commissioning editors will get in touch with you.

We're not just looking for published authors; if you have strong technical skills but no writing experience, our experienced editors can help you develop a writing career, or simply get some additional reward for your expertise.

Liferay Portal 6 Enterprise IntranetsISBN: 978-1-84951-038-7 Paperback: 692 pages

Build and maintain impressive corporate intranets with Liferay

1. Develop a professional Intranet using Liferay's practical functionality, usability, and technical innovation

2. Enhance your Intranet using your innovation and Liferay Portal's out-of-the-box portlets

3. Maximize your existing and future IT investments by optimizing your usage of Liferay Portal

4. Clear, step-by-step instructions, practical examples, and straightforward explanations

Learning VaadinISBN: 978-1-84951-522-1 Paperback: 412 pages

Master the full range of web development features powered by Vaadin-built RIAs

1. Discover the Vaadin framework in a progressive and structured way

2. Learn about components, events, layouts, containers, and bindings

3. Create outstanding new components by yourself

4. Integrate with your existing frameworks and infrastructure

5. Pragmatic and no-nonsense approach

Please check www.PacktPub.com for information on our titles

JBoss Portal Server DevelopmentISBN: 978-1-84719-410-7 Paperback: 276 pages

Create dynamic, feature-rich, and robust enterprise portal applications

1. Complete guide with examples for building enterprise portal applications using the free, open-source standards-based JBoss portal server

2. Quickly build portal applications such as B2B web sites or corporate intranets

3. Practical approach to understanding concepts such as personalization, single sign-on, integration with web technologies, and content management

JBoss ESB Beginner's GuideISBN: 978-1-84951-658-7 Paperback: 320 pages

A comprehensive, practical guide to developing service-based applications using the Open Source JBoss Enterprise Service Bus

1. Develop your own service-based applications, from simple deployments through to complex legacy integrations

2. Learn how services can communicate with each other and the benefits to be gained from loose coupling

3. Contains clear, practical instructions for service development, highlighted through the use of numerous working examples

Please check www.PacktPub.com for information on our titles


Recommended