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 Place 35 Livery Street Birmingham B3 2PB, UK. ISBN 978-1-84951-862-8 www.packtpub.com
Cover Image by Asher Wishkerman (
[email protected])
Credits Authors Ken Finnigan
Project Coordinator Joel Goveya
Luca Stancapiano Piergiorgio Lucidi Reviewers Antoine Herzog Gurkan Erdogdu Rafael Liu Acquisition Editor Joanna Finchen Lead Technical Editor Azharuddin Sheikh Technical Editor Kirti Pujari
Proofreader Martin Diver Indexer Monica Ajmera Production Coordinator Arvindkumar Gupta Cover Work Arvindkumar 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 more You 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? ff
Fully searchable across every book published by Packt
ff
Copy and paste, print and bookmark content
ff
On demand and accessible via web browser
Free Access for Packt account holders If 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 Contents Preface 1 Chapter 1: Getting Started 7 Introduction 7 Installing GateIn from a binary package 8 Building and installing GateIn from the source code 10 Configuring the content storage 13 Configuring GateIn to send e-mails 18 Running GateIn on your machine 20 Setting up the development environment 22
Chapter 2: Managing Portal Contents Using the GUI
29
Chapter 3: Managing Portal Contents Using XML
59
Chapter 4: Managing Portal Users
89
Introduction 29 Managing portals 30 Managing portal pages 37 Managing registered portlets 42 Managing the navigation tree 50 Managing the dashboard 53 Introduction 59 Managing portals using XML 60 Managing portal pages using XML 66 Managing registered portlets using XML 68 Managing the navigation tree using XML 73 Wrapping it all up in a separate EAR 75 Introduction 89 Managing users 90 Managing groups 93
Table of Contents
Assigning users to groups Integrating with an existing LDAP store Setting an automatic membership after user creation Adding a custom field in the user profile Integrating with Web SSO Integrating with SPNEGO for Desktop SSO
97 100 115 117 118 122
Chapter 5: Securing Portal Contents
129
Chapter 6: Developing Portlets
157
Chapter 7: Developing Using Components API
193
Chapter 8: Migrating from Existing Portals
227
Introduction 129 Securing portals 130 Securing with JBoss AS 133 Securing with Tomcat 134 Choosing the JAAS modules 136 Creating a login page 138 Synchronizing users 140 Securing pages 142 Securing categories 145 Securing applications 150 Securing portlets 153
Introduction 157 Creating a portlet with the Portlet 2.0 Specification 158 Using an action to pass form parameters 162 Using the user locale to localize portlet content 166 Communicating between portlets using Public Render Parameters 172 Communicating between portlets using events 181 Introduction Getting started with WebUI Creating views Handling different skins in a portlet Adding the JavaScript resources to the portlet Handling different locales in a portlet
193 194 200 207 218 220
Introduction 227 Migrating a transactional portlet 228 Migrating an authenticated portlet 231 Migrating a portlet that uses JCR 237 Importing a skin from an existing website 244
ii
Table of Contents
Chapter 9: Managing Gadgets
253
Chapter 10: Frameworks in a Portal
285
Chapter 11: Managing Portal Resources with the Management Component
319
Chapter 12: Managing Documents Using External ECM Systems
333
Index
367
Introduction Importing existing gadgets Removing gadgets Creating gadgets Changing the category of a gadget Resizing gadgets Making the gadget a portlet Setting user preferences
253 254 260 264 270 275 277 280
Introduction 285 Creating a JSF 2 portlet 285 Using jQuery in a portlet 298 Using portlet events in JSF 2 305 Creating a RichFaces 4 portlet 312
Introduction Deploying the CLI to GateIn Retrieving a managed resource with RESTful Interface Exporting a portal page with the CLI Removing a portal page from the navigation Using the default portal as the foundation for a new portal site Introduction Creating a portlet to integrate a CMIS repository Creating a portlet to integrate JBoss ModeShape Creating a portlet to integrate Apache JackRabbit Creating a portlet to integrate Alfresco using Spring WebScripts
319 320 322 324 326 329 333 334 348 356 360
iii
Preface Enterprises 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 covers Chapter 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 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 book In Chapter 1, Getting Started, you will see all the required components for building and executing GateIn. In summary, you should have: ff
2
Any operating system based on Linux, Mac OS X, or Windows
Preface ff
Java Development Kit (JDK) 1.6
ff
Eclipse IDE
ff
Apache Subversion (latest version)
ff
Apache Maven (latest version)
ff
GateIn Portal 3.2.0 and beyond
Who this book is for This book is for anyone who needs to administer or develop applications on GateIn. Java and RESTful architecture skills are suggested but not needed.
Conventions In 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: /platform/users member
When we wish to draw your attention to a particular part of a code block, the relevant lines or items are set in bold: /platform/users member
Any command-line input or output is written as follows: >mvn clean package
3
Preface 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 feedback Feedback 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 support Now 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 code You 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.
4
Preface
Errata Although 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.
Piracy Piracy 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.
Questions You 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.
5
1
Getting Started In this chapter we will cover: ff
Installing GateIn from binary package
ff
Building and installing GateIn from the source code
ff
Configuring the content storage
ff
Configuring GateIn to send e-mails
ff
Running GateIn on your machine
ff
Setting up the development environment
Introduction This 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
Installing GateIn from a binary package GateIn 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 ready A 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. 8
Chapter 1 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 works You 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: ff
bin
ff
client
ff
common
ff
docs
ff
lib
ff
server
The Tomcat 6.x bundle is based on a different structure: ff
bin
ff
conf
ff
gatein
ff
lib
ff
logs
ff
temp
ff
webapps
ff
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.
9
Getting Started
See also ff
The Building and installing GateIn from the source code recipe
ff
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 ready Let's first see if you have installed in your machine the following needed components: ff
Java Development Kit (JDK) 1.6
ff
Subversion client (SVN)
ff
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/ 10
Chapter 1 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. 11
Getting Started
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 gateinportal-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 /gateinportal-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). 12
Chapter 1 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 ff
The Installing GateIn from a binary package recipe
ff
The Running GateIn on your machine recipe
ff
The Setting up the development environment recipe
Configuring the content storage By 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.
13
Getting Started 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: ff
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.
ff
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: ff
Database
ff
File system
Finally, we will see how to configure a JBoss identity that is based only on a separate database instance.
Getting ready Locate 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: /gatein/conf/configuration.properties
If you are using JBoss AS 6 you will find it at the following location: /server/default/conf/gatein/configuration.properties
If you want to use JBoss AS 7, you have to edit the configuration file here: /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:
14
gatein.jcr.datasource.driver
gatein.jcr.datasource.url
Chapter 1
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:@::gateInJcr
gatein.jcr.datasource.username=
gatein.jcr.datasource.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: /lib
For JBoss 6 AS you can use this one: /server/default/lib
For JBoss 7 AS the following directory is available here: /standalone/lib
15
Getting Started 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:@::gateInIdm
gatein.idm.datasource.username=
gatein.idm.datasource.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.
16
Chapter 1 The database connection used in GateIn is a standard JDBC connection that requires the usual parameters for configuration: ff
JDBC driver class
ff
JDBC URL
ff
Username
ff
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 configuration In 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://:/gateInJcr 17
Getting Started gatein.jcr.datasource.username= gatein.jcr.datasource.password=
For the identity manager you should have the following: gatein.idm.datasource.driver=com.mysql.jdbc.Driver gatein.idm.datasource.url=jdbc:mysql://:/gateInIdm gatein.idm.datasource.username= gatein.idm.datasource.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 ff
The Configuring GateIn to send e-mails recipe
ff
The Running GateIn on your machine recipe
Configuring GateIn to send e-mails GateIn 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 ready Locate 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: # Email gatein.email.smtp.username= gatein.email.smtp.password= gatein.email.smtp.host=smtp.gmail.com gatein.email.smtp.port=465 gatein.email.smtp.starttls.enable=true 18
Chapter 1 gatein.email.smtp.auth=true gatein.email.smtp.socketFactory.port=465 gatein.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: ff
Username
ff
Password
ff
Host
ff
Port
ff
EnableTls
ff
Auth
ff
Socket factory port
ff
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 ff
The Configuring the content storage recipe
ff
The Running GateIn on your machine recipe
19
Getting Started
Running GateIn on your machine Let'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 ready Locate 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: ff
If you are using Tomcat then you have a specific script gatein.sh, for Windows gatein.bat, provided in the binary folder /bin
ff
For JBoss AS 6 you can use the standard run.sh script available in this folder /bin
ff
JBoss AS 7 requires using the script standalone.sh stored here
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.
20
Chapter 1 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 instance If you are using GateIn with JBoss AS 6, you have to use the following command line: nohup ./run.sh & 21
Getting Started 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 instance Otherwise, 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 ff
The Configuring the content storage recipe
ff
The Configuring GateIn to send e-mails recipe
Setting up the development environment GateIn 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 ready 1. 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.
22
Chapter 1
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.
23
Getting Started 5. In the Confirm Selected Features window, select all the items and click on the Next button.
24
Chapter 1 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.
25
Getting Started 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.
26
Chapter 1 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.
27
Getting Started
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: ff
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 ff
28
The Building and installing GateIn from the source code recipe
2
Managing Portal Contents Using the GUI In this chapter we will cover: ff
Managing portals
ff
Managing portal pages
ff
Managing registered portlets
ff
Managing the navigation tree
ff
Managing the dashboard
Introduction In 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 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: ff
SiteMap
ff
Sign in
ff
Register
ff
Change Language
ff
Home
The SiteMap page contains a portlet that automatically creates the sitemap of the current default portal by navigating all the pages.
Managing portals This 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.
30
Chapter 2
Getting ready 1. 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:
31
Managing Portal Contents Using the GUI 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.
32
Chapter 2
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.
33
Managing Portal Contents Using the GUI
Removing a portal In 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 portal Another 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 portal GateIn 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.
34
Chapter 2 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
35
Managing Portal Contents Using the GUI 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:
36
Chapter 2
See also ff
The Managing pages recipe
ff
The Managing registered portlets recipe
ff
The Assigning users to groups recipe in Chapter 4, Managing Portal Users
Managing portal pages In 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 ready In 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.
37
Managing Portal Contents Using the GUI 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:
38
Chapter 2 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.
39
Managing Portal Contents Using the GUI
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:
40
Chapter 2 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.
41
Managing Portal Contents Using the GUI
Removing a page Using 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 ff
The Managing portals recipe
ff
The Managing registered portlets recipe
Managing registered portlets So 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.
42
Chapter 2 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 ready Log 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: Your company name <strong>Our Office
Your company address
Your company phone number
<strong>Email us
info@ yourcompany.com
]]>
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.
44
Chapter 2 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.
45
Managing Portal Contents Using the GUI 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: ff
Administration
ff
Dashboard
ff
Gadgets
ff
Web
46
Chapter 2 Under the Administration category, you will find the following portlets: ff
Application Registry: This allows to manage applications (portlet and gadgets)
ff
New Account: This exposes the registration feature
ff
Organization Management: This manages users, groups, and memberships
The Dashboard category contains the following: ff
Dashboard Portlet: This manages the dashboard of users
ff
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: ff
Calculator
ff
Calendar
ff
Rss Reader: An example of a simple RSS Reader
ff
Todo: Manages a simple to-do list
Finally, the last category available is Web, which contains the following portlets: ff
IFrame: Renders an HTML iframe tag configuring the src attribute in the portlet preferences
ff
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: ff
Rows Layout: This allows positioning portlets in rows
ff
Columns Layout: This allows positioning portlets in columns
ff
Autofit Columns Layout: This allows positioning portlets in autofit columns
ff
Tabs Layout: This allows creating tab sections
47
Managing Portal Contents Using the GUI ff
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. 48
Chapter 2 You should see output in your page similar to the following screenshot:
Managing portlet settings In 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 49
Managing Portal Contents Using the GUI
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 ff
The Managing portal pages recipe
ff
The Managing the navigation tree recipe
ff
The Managing the dashboard recipe
Managing the navigation tree For 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.
50
Chapter 2 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 ready 1. 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:
51
Managing Portal Contents Using the GUI 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:
52
Chapter 2 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: ff
Navigation nodes
ff
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 ff
The Managing portal pages recipe
ff
The Managing registered portlets recipe
ff
The Managing the dashboard recipe
Managing the dashboard GateIn 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.
53
Managing Portal Contents Using the GUI
Getting ready Authenticate 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.
54
Chapter 2 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: ff
Use the Page Creation Wizard: Click on Add New Page, leaving your pointer on the Dashboard Editor
ff
Quick way: Click on the plus icon on the right of the current tab of the dashboard page
55
Managing Portal Contents Using the GUI 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.
56
Chapter 2 In this case, the page will have the following default values: ff
Node Name with a suffix automatically generated
ff
Dashboard Layout
ff
no gadgets applied For more information about gadgets, please see Chapter 9, Managing Gadgets.
See also ff
The Managing portal pages recipe
ff
The Managing registered portlets recipe
57
3
Managing Portal Contents Using XML In this chapter we will cover: ff
Managing portals using XML
ff
Managing portal pages using XML
ff
Managing registered portlets using XML
ff
Managing the navigation tree using XML
ff
Wrapping it all up in a separate EAR
Introduction In 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 As you will learn in this chapter, the entire portal configuration will be essentially based on three different configuration sections: portal, pages, and navigation: ff
The portal section will be responsible for information about the layout of and applications available for the portal
ff
The pages section will describe all the details of each page in the portal, including layout and applications
ff
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 XML In 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 ready Locate 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
60
Chapter 3
How to do it... 1. Locate the snippet related to the UserPortalConfigService: org.exoplatform.portal.config.UserPortalConfigService org.exoplatform.portal.config.UserPortalConfigService …
Downloading the example code You 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: org.exoplatform.portal.config.UserPortalConfigService org.exoplatform.portal.config.UserPortalConfigService new.portal.config.user.listener initListener org.exoplatform.portal.config. NewPortalConfigListener this listener init the portal configuration default.portal The default portal for checking db is empty or not financials page.templates.location the path to the location that contains Page templates war:/conf/portal/template/pages 61
Managing Portal Contents Using XML 3. You can also override the default templates for any page type (portal, user, and group): site.templates.location description war:/conf/portal basic classic group user
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
62
Chapter 3
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: ff
The default portal is available from the homepage
ff
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: financials Financials Financials portal en Everyone *:/platform/administrators onDemand 0 63
Managing Portal Contents Using XML web BannerPortlet template par:/groovy/groovy/webui/component/ UIBannerPortlet.gtmpl false Everyone false web NavigationPortlet useAJAX false false Everyone false web BreadcumbsPortlet useAJAX false false 64
Chapter 3 Everyone false web FooterPortlet template par:/groovy/groovy/webui/component/ UIFooterPortlet.gtmpl false Everyone false
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: ff
label: This is the portal label
ff
description: A description for the portal instance
ff
access-permission: The group that can access the portal
ff
edit-permission: The group that can edit the portal
ff
sessionAlive: This shows how to manage the user session
ff
showPortletInfo: Manages the default behavior information bar
ff
page-body: This is the center section of the body for the portal page
65
Managing Portal Contents Using XML 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: ff
application-ref: This is the portlet category
ff
portlet-ref: This is the name of the portlet declaration
ff
preferences: Sets the portlet preferences
ff
access-permissions: Sets permissions to access the portlet
ff
show-info-bar: Manages the information bar
See also ff
The Managing portals recipe in Chapter 2, Managing Portal Contents Using the GUI
ff
The Managing portal pages using XML recipe
Managing portal pages using XML After 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 ready Create 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: contactus Contact us Everyone *:/platform/administrators . . .
66
Chapter 3 . . .
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: ff
name: This is the internal name of the page node
ff
title: This is the title of the page
ff
access-permission: Defines the policy for accessing the page
ff
edit-permission: Defines who can edit this page
ff
portlet-application: Declares a portlet to embed in the page
ff
gadget-application: Declares a gadget to embed in the page
ff
page-body: Declares where GateIn will render the current page
ff
site-body: Declares where GateIn will render the current page for the site
ff
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 ff
Chapter 11, Managing Portal Resources with the Management Component
ff
The Managing portals using XML recipe
ff
The Managing registered portlets in Chapter 2, Managing Portal Contents Using the GUI
ff
The Managing the navigation tree in Chapter 2, Managing Portal Contents Using the GUI
67
Managing Portal Contents Using XML
Managing registered portlets using XML This recipe explains how to attach applications to a page using the XML configuration of GateIn.
Getting ready Let'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: contactus Contact us Everyone *:/platform/administrators web ContactUsPortlet useAJAX false false Contact us Everyone false
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.
68
Chapter 3
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: ff
application-ref: Shows the portlet category as stored in the Application Registry
ff
portlet-ref: Shows the portlet reference as stored in the Application Registry
ff
preferences: Properties for the current portlet instance
In GateIn, you will find the following default values for the application-ref element: ff
web: This is the Web category
ff
exoadmin: This is the Administration category
ff
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: ServicesManagement Services Management manager:/platform/administrators false
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. 69
Managing Portal Contents Using XML
Defining the categories configuration for a portal If 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: . . . org.exoplatform.application.registry. ApplicationRegistryService org.exoplatform.application.registry.impl. ApplicationRegistryServiceImpl new.portal.portlets.registry initListener org.exoplatform.application.registry. ApplicationCategoriesPlugins this listener init the portlets are registered in PortletRegister administration description Administration Administration application for administration 70
Chapter 3 *:/platform/administrators *:/organization/management/ executive-board . . .
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: ff
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.
ff
A component plugin declared as ApplicationCategoriesPlugin because it is the responsible class for creating categories during the first execution of the portal instance.
ff
An init-params element that contains for each category a specific object-param element.
ff
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
71
Managing Portal Contents Using XML An application must be declared in a category definition in the following way: ApplicationRegistryPortlet administration Application Registry Application Registry portlet exoadmin/ApplicationRegistryPortlet *:/platform/administrators *:/organization/ management/executive-board 72
Chapter 3 Finally, let's look at all the needed elements to initialize an application under a category: ff
applicationName: The name of the application instance as registered in the
Application Registry
ff
categoryName: The name of the category
ff
displayName: The default label to use for the current application
ff
description: The description for the application
ff
type: The type of the implementation; the value could be portlet or gadget
ff
contentId: The identifier of the application definition in the portal instance
ff
accessPermissions: The permissions to access the current application
See also ff
The Managing the navigation tree using XML recipe
ff
The Managing portal pages using XML recipe
ff
The Managing portals using XML recipe
Managing the navigation tree using XML In 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 ready Three different configuration files manage the configuration for the navigation of a portal, so let's start discussing about these files: ff
portal.xml: Contains the default layout and the standard applications for a portal
ff
pages.xml: Contains the pages layout and the standard applications for pages
ff
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. 73
Managing Portal Contents Using XML
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: 1 contactus Contact us . . . portal::financials::contactus
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): ff
name: The internal name of the page node
ff
label: This is the label of the page (multi-value)
ff
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.
74
Chapter 3 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 ff
Chapter 11, Managing Portal Resources with the Management Component
ff
The Managing portals using XML recipe
ff
The Managing portal pages using XML recipe
ff
The Wrapping it all up in a separate EAR recipe
Wrapping it all up in a separate EAR We 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 ff
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
ff
Configure your developing environment following the recipe Setting up the development environment from Chapter 1, Getting Started
ff
Locate the Maven project dedicated to the portal example at the following path: /examples/portal
75
Managing Portal Contents Using XML
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.
76
Chapter 3 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: org.exoplatform.container.definition. PortalContainerConfig Add PortalContainer Definitions 77
Managing Portal Contents Using XML registerPlugin org.exoplatform.container.definition. PortalContainerDefinitionPlugin financials-portal financials-portal restfinancials-portal gatein-domainfinancials-portal configuration.properties eXoResources portal dashboard exoadmin eXoGadgets 78
Chapter 3 gwtGadgets eXoGadgetServer rest-financials-portal web financials-portal
8. Update pom.xml in the config module with the following snippet: org.exoplatform.portal exo.portal.parent 3.2.0-GA ../../../pom.xml 4.0.0 exo.portal.financials.portal.config Financials Portal Configuration jar Financials Portal Configuration 79
Managing Portal Contents Using XML 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: financials-portal Financials Portal Ear exo.portal.financials.portal.config-3.2.0-GA.jar financials-portal.war financials-portal rest-financials-portal.war rest-financials-portal exo.portal.financials.portal.jar-3.2.0-GA.jar
11. Update the pom.xml for the EAR module to conform to the new setting: org.exoplatform.portal exo.portal.parent 3.2.0-GA ../../../pom.xml 4.0.0 gatein-financials-portal 80
Chapter 3 ear Financials Portal Ear Financials Portal Ear org.exoplatform.portal exo.portal.financials.portal.config 3.2.0-GA3.2.0-GA org.exoplatform.portal exo.portal.financials.portal.jar 3.2.0-GA org.exoplatform.portal exo.portal.component.web.api 3.2.0-GA provided org.exoplatform.portal exo.portal.financials.portal.war 3.2.0-GA war org.exoplatform.portal exo.portal.financials.portal.rest-war 3.2.0-GA war ${project.artifactId} org.apache.maven.plugins maven-ear-plugin financials-portal 81
Managing Portal Contents Using XML org.exoplatform.portal exo.portal.financials.portal.config true org.exoplatform.portal exo.portal.financials.portal.war financials-portal.war financials-portal org.exoplatform.portal exo.portal.financials.portal.rest-war rest-financials-portal.war rest-financials-portal org.exoplatform.portal exo.portal.financials.portal.jar true
12. Update the realm configuration for the Financials portal in the file financialsportal/ear/src/main/application/META-INF/gatein-jboss-beans.xml
as follows: 82
Chapter 3 financialsportal gatein-domain-financialsportal financialsportal gatein-domain-financialsportal financialsportal gatein-domain-financialsportal financialsportal gatein-domain-financialsportal
13. Override the default portal configuration in the file financials-portal/war/ src/main/webapp/WEB-INF/conf/configuration.xml: war:/conf/financials-portal/common/commonconfiguration.xml war:/conf/financials-portal/jcr/jcr-configuration.xml war:/conf/financials-portal/portal/portalconfiguration.xml
83
Managing Portal Contents Using XML war:/conf/financials-portal/web/web-inf-extensionconfiguration.xml
14. Set the specific realm in the JBoss configuration file financials-portal/restwar/src/main/webapp/WEB-INF/jboss-web.xml: java:/jaas/gatein-domain-financials-portal
15. Change the JCR configuration file for the new portal instance in the file financialsportal/war/src/main/webapp/WEB-INF/conf/financials-portal/jcr/ jcr-configuration.xml in the following way:
org.exoplatform.services.jcr.config. RepositoryServiceConfiguration Sample RepositoryServiceConfiguration Plugin addConfig org.exoplatform.services.jcr.impl.config. RepositoryServiceConfigurationPlugin conf-path JCR configuration file war:/conf/financials-portal/jcr/repositoryconfiguration.xml
16. Change the workspace JCR configuration (content store and index store) in the file / financials-portal/war/src/main/webapp/WEB-INF/conf/financialsportal/jcr/repository-configuration.xml as follows:
84
Chapter 3 . . . . . . . . .
85
Managing Portal Contents Using XML 17. Update the latter configuration files starting from the root portal configuration file
financials-portal/war/src/main/webapp/WEB-INF/conf/financialsportal/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
86
Chapter 3 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.
87
Managing Portal Contents Using XML
How it works... The example portal project provided by the GateIn source code consists of the following Maven modules: ff
config: Includes the portal configurations about the portlet container
ff
jar: Includes Java code for your custom logic and filters
ff
rest-war: Contains the configuration for all the REST API of the portal
ff
war: Contains all the portal configuration files and builds the WAR artifact
ff
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 ff
The Managing portals using XML recipe
ff
The Managing portal pages using XML recipe
ff
The Managing the navigation tree using XML recipe
ff
The Managing registered portlets using XML recipe
88
4
Managing Portal Users In this chapter, we will cover: ff
Managing users
ff
Managing groups
ff
Assigning users to groups
ff
Integrating with an existing LDAP store
ff
Setting an automatic membership after user creation
ff
Adding a custom field in the user profile
ff
Integrating with Web SSO
ff
Integrating with SPNEGO for Desktop SSO
Introduction In 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 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 users In this recipe, we will see how you can create, update, and remove users from the portal instance using the GUI.
Getting ready Log 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.
90
Chapter 4 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.
91
Managing Portal Users All the details about a user can be shown or changed by using the following tabs: ff
Account Info: Contains the account information
ff
User Profile: Contains basic information about the user
ff
Home Info: Contains contact information
ff
Business Info: Contains contact information for business
ff
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 users It 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 users If 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.
92
Chapter 4
Removing memberships In 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 ff
The Managing groups recipe
ff
The Assigning users to groups recipe
Managing groups In 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: ff
Customer Care
ff
Customers
ff
Bank Operators
Getting ready Log in to the portal using an administrator account.
93
Managing Portal Users
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.
94
Chapter 4 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.
95
Managing Portal Users
There's more... Here we will see all the other options available for manipulating groups in the portal.
Removing groups In 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 groups Let'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 ff
The Managing users recipe
ff
The Assigning users to groups recipe
96
Chapter 4
Assigning users to groups We 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 ready Log 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
97
Managing Portal Users 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 Service The 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.
98
Chapter 4 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 /WEB-INF/conf/ configuration.xml file. The default configuration provided in GateIn is the following code: ….. war:/conf/organization/idm-configuration.xml ……. war:/conf/organization/organization-configuration.xml
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_GuideAuthentication_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: ff
IDM
ff
Active Directory
ff
Hibernate
ff
InstallCS
ff
JDBC
ff
LDAP
The following recipes will show some custom operations in the organization making use of the configuration files.
99
Managing Portal Users
See also ff
The Managing users recipe
ff
The Managing groups recipe
Integrating with an existing LDAP store Typically, 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 ready Locate 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: ... war:/conf/organization/exoactivedirectoryconfiguration.xml --> war:/conf/organization/exo/ldap-configuration.xml 100
Chapter 4 war:/conf/organization/organization-configuration.xml war:/conf/jcr/component-plugins-configuration.xml war:/conf/mail/portal-mail-configuration.xml war:/conf/portal/portal-configuration.xml war:/conf/portal/application-registry-configuration. xml war:/conf/portal/controller-configuration.xml war:/conf/portal/web-configuration.xml war:/conf/portal/gadget-configuration.xml
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: org.exoplatform.services.ldap.LDAPService org.exoplatform.services.ldap.impl.LDAPServiceImpl ldap.config Default ldap config ld ap://127.0.0.1:389,10.0.0.1:389
101
Managing Portal Users CN=Manager,DC=exoplat form,DC=org secret 3
5
10
follow ignore --> default
4. Set the following properties for configuring your LDAP server with GateIn:
providerURL: The LDAP endpoint (ldap://:),
can be multi-value; you can add more servers using commas.
102
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 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: org.exoplatform.services.organization. OrganizationService org.exoplatform.services.organization.ldap. OrganizationServiceImpl init.service.listener addListenerPlugin org.exoplatform.services.organization.ldap. OrganizationLdapInitializer this listener populate organization ldap service create default dn ldap.userDN.key The key used to compose user DN cn ldap.attribute.mapping ldap attribute mapping top,person,organi zationalPerson,inetOrgPerson top,organizati onalPerson top,organization alUnit top,org anizationalRole top,groupOf Names
name="baseURL">dc=exoplatform,dc=org allowCreateEntry true parentMembershipAttributeName member 113
Managing Portal Users parentMembershipAttributePlaceholder ou=placeholder,o=portal,o=gatein,dc=mydomain,dc=com isParentMembershipAttributeDN true allowEmptyMemberships true createEntryAttributeValues objectClass=top objectClass=groupOfNames member=ou=placeholder,o=portal,o=gatein,dc=mydomain,dc=com
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: ff
idAttributeName: This is the name of the attribute used as identifier for groups.
ff
ctxDNs: This is the DN for finding this group in the LDAP store tree.
ff
parentMembershipAttributeName: This is the name of the attribute that
manages members for groups. ff
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.
ff
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.
114
Chapter 4 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 ff
The Setting an automatic membership after user creation recipe
ff
The Integrating with Web SSO recipe
ff
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 ready Locate the XML WebUI descriptor for the Organization Service at this path: /WEB-INF/conf/organization/organizationconfiguration.xml
How to do it… 1. Identify the configuration of the event for the creation of the user: new.user.event.listener addListenerPlugin org.exoplatform.services.organization.impl.NewUserEventListener ...
115
Managing Portal Users 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: /platform/users member
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: newadmin
See also ff
The Adding a custom field in the user profile recipe
ff
The Integrating with Web SSO recipe
ff
The Integrating with SPNEGO for Desktop SSO recipe
116
Chapter 4
Adding a custom field in the user profile We want to add a new field for the users and show it through the user profile console.
Getting ready It 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("Nameofthenewprofi leblock "); 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).
117
Managing Portal Users
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 organizationconfiguration.xml. The following is the final form page returned by the framework:
See also ff
The Integrating with Web SSO recipe
ff
The Integrating with SPNEGO for Desktop SSO recipe
Integrating with Web SSO In 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/idmgmt/overview/index.html. 118
Chapter 4
Getting ready 1. 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: Username 119
Managing Portal Users Password
4. If you are using JBoss, uncomment the following snippet inside the file gatein.ear/ META-INF/gatein-jboss-beans.xml: portal gatein-domain portal gatein-domain
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 required portalContainerName=portal realmName=gatein-domain;
6. Add a new valve in the Tomcat configuration inside the file TOMCAT_HOME/webapps/ portal.war/META-INF/context.xml: . . . . . . . . .
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. 120
Chapter 4 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 ff
The Integrating with SPNEGO for Desktop SSO recipe
121
Managing Portal Users
Integrating with SPNEGO for Desktop SSO Another 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 ready 1. 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/jbossnegotiation/
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: 122
Chapter 4 true true HTTP/server.local.network@LOCAL. NETWORK /etc/krb5.keytab true true
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: BASIC org.apache.catalina.authenticator.BasicAuthenticator CLIENT-CERT org.apache.catalina.authenticator.SSLAuthenticator DIGEST org.apache.catalina.authenticator.DigestAuthenticator FORM org.apache.catalina.authenticator.FormAuthenticator NONE
123
Managing Portal Users org.apache.catalina.authenticator.NonLoginAuthenticator SPNEGO org.gatein.sso.spnego.GateInNegotiationAuthenticator
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: portal gatein-domain portal gatein-domain
124
Chapter 4 portal gatein-domain member /platform/users --> portal gatein-domain useFirstPass host true gatein-form-authdomain useFirstPass portal gatein-domain
125
Managing Portal Users 9. Change the GateIn portal application web.xml as follows: SPNEGO SPNEGO /initiatelogin /errorlogin
10. Add the SPNEGO filters in the web.xml: LoginRedirectFilter org.gatein.sso.agent.filter.LoginRedirectFilter LOGIN_URL /portal/private/classic SPNEGOFilter org.gatein.sso.agent.filter.SPNEGOFilter LoginRedirectFilter /* SPNEGOFilter /login
126
Chapter 4 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:
< %=_ctx.appRes("UILoginForm.label.Signin")%> -->
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 ff
The Integrating with Web SSO recipe
127
5
Securing Portal Contents In this chapter we will cover: ff
Securing portals
ff
Securing with JBoss AS
ff
Securing with Tomcat
ff
Choosing the JAAS modules
ff
Creating a login page
ff
Synchronizing users
ff
Securing pages
ff
Securing categories
ff
Securing applications
ff
Securing portlets
Introduction This 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 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: ff
Authentication
ff
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 portals The 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.
130
Chapter 5
Getting ready Locate 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: admin authentication /admin/* POST GET admin NONE
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: the admin role admin
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. 131
Securing Portal Contents
There's more... Configuring GateIn with JAAS GateIn 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: . . . gatein-domain . . .
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 ff
The Securing with JBoss AS recipe
ff
The Securing with Tomcat recipe
132
Chapter 5
Securing with JBoss AS In this recipe, we will configure GateIn with JAAS using JBoss AS (5.x and 6.x).
Getting ready Locate 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: java:/jaas/gatein-domain
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 /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: portal gatein-domain 133
Securing Portal Contents ………..
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: ff
Required: The LoginModule is required to succeed. If it succeeds or fails, authentication still continues to proceed to the next LoginModule in the list.
ff
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.
ff
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.
ff
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 ff
The Securing portals recipe
ff
The Securing with Tomcat recipe
ff
The Choosing the JAAS modules recipe
Securing with Tomcat In this recipe, we will configure a JAAS realm using Tomcat 6.x.x/7.x.x.
Getting ready Locate the declaration of the realm inside /META-INF/context.xml.
134
Chapter 5
How to do it… 1. Change the default configuration for your needs, as described in the previous recipe. The default configuration is the following: ;
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: 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: ff
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.
ff
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 , , elements, or any markup that cannot be present within the element, as it is the responsibility of the portlet container to generate these.
160
Chapter 6 ff
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 class Instead 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 GenericPortlet GenericPortlet 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: @Override protected void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { getPortletContext().getRequestDispatcher("/helloWorld.jsp"). include(request, response); }
161
Developing Portlets
Implementing Portlet interface Instead of extending from GenericPortlet, another option is to create a new class that implements the Portlet interface.
See also ff
The Using an action to pass form parameters recipe
Using an action to pass form parameters This 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 ready The following are required for this recipe: ff
Apache Maven
ff
An IDE of your choice
ff
GateIn-3.2.0.Final
ff
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: Welcome to the HelloWorld portlet Name: 162
Chapter 6
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: Hello! Hello from your first portlet!
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.
163
Developing Portlets 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.
164
Chapter 6 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 URL In 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: John Doe
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 input Once 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: Reset
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.
165
Developing Portlets The following screenshot shows an example of what this could look like in the portlet:
See also ff
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 ready The following are required for this recipe: ff
Apache Maven
ff
An IDE of your choice
ff
GateIn-3.2.0.Final
ff
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 : en de gatein.cookbook.chapter6.HelloWorldPortlet 166
Chapter 6 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 Portlet javax.portlet.display-name=Hello World Portlet link.reset=Reset link.return=Return welcome.heading=Welcome to the Hello World portlet welcome.form.name=Name welcome.form.submit=Say Hello helloworld.heading=Hello! helloworld.text.hello=Hello helloworld.text.message=from your first portlet!
4. Add the following content to HelloWorldPortlet_de.properties: javax.portlet.title=Hallo Welt Portlet javax.portlet.display-name=Hallo Welt Portlet link.reset=Zurücksetzen link.return=zurückkehren helloworld.heading=Hallo! helloworld.text.hello=Hallo helloworld.text.message=von Ihrem ersten portlet! welcome.heading=Willkommen in der Hallo Welt Portlet welcome.form.name=Name welcome.form.submit=Sag Hallo
5. Replace the content of welcome.jsp with the following:
167
Developing Portlets :
6. Replace the content of helloWorld.jsp with the following:
7. Run the following in the root of the project directory to build the web archive: > mvn clean package
168
Chapter 6 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 . 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.
169
Developing Portlets The last changes were to replace any text in the portlet with , 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 name As 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:
170
Chapter 6 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 ff
The Creating a portlet with the Portlet 2.0 Specification recipe
171
Developing Portlets
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 ready The following are required for this recipe: ff
Apache Maven
ff
An IDE of your choice
ff
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: javax.portlet portlet-api 2.0 provided
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.
172
Chapter 6 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] Corp."; availableStocks[1] availableStocks[2] availableStocks[3] availableStocks[4] }
= "IBM:International Business Machines = = = =
"IBN:Icici Bank Limited"; "REV:Revlon"; "RHI:Robert Half International Inc."; "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); 173
Developing Portlets 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: Search Stocks Ticker: 174
Chapter 6
No results found for ticker: 175
Developing Portlets
13. Create portlet.xml in the src/main/webapp/WEB-INF folder of the project. 14. Add the following content to portlet.xml: StockSearch-PRP gatein.cookbook.chapter6.StockSearchPortlet text/html view Stock Search - PRP portlet watch_stock watch_stock gi:watch_stock
15. Create web.xml in the src/main/webapp/WEB-INF folder of the project. 16. Add the following to web.xml: 176
Chapter 6 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.
177
Developing Portlets 23. Add the following content into watchlist.jsp: Stock Watchlist
No stocks being watched at the moment.
24. Create portlet.xml in the src/main/webapp/WEB-INF folder of the project.
178
Chapter 6 25. Add the following content to portlet.xml: WatchList-PRP gatein.cookbook.chapter6.WatchlistPortlet text/html view WatchList - PRP portlet watch_stock watch_stock gi:watch_stock
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.
179
Developing Portlets 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:
180
Chapter 6
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 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 ff
The Creating a portlet with the Portlet 2.0 Specification recipe
ff
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 ready The following are required for this recipe: ff
Apache Maven
ff
An IDE of your choice
ff
GateIn-3.2.0.Final 181
Developing Portlets
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: javax.portlet portlet-api 2.0 provided javax.xml.bind jaxb-api 2.1 provided
3. Create a class named StockEvent within a package called gatein.cookbook. chapter6. 4. Add the following content to it: @XmlRootElement public 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; 182
Chapter 6 } 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] Machines Corp."; availableStocks[1] availableStocks[2] availableStocks[3] availableStocks[4]
= "IBM:International Business = = = =
"IBN:Icici Bank Limited"; "REV:Revlon"; "RHI:Robert Half International Inc."; "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); }
183
Developing Portlets 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; 184
Chapter 6 } } 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: Search Stocks Ticker:
No results found for ticker:
15. Create portlet.xml in the src/main/webapp/WEB-INF folder of the project. 16. Add the following content to portlet.xml: StockSearch-Event gatein.cookbook.chapter6.StockSearchPortlet 186
Chapter 6 text/html view Stock Search - Event portlet gi:watchStockEvent gi:watchStockEvent gatein.cookbook.chapter6.StockEvent
17. Create web.xml in the src/main/webapp/WEB-INF folder of the project. 18. Add the following to web.xml:
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 { 187
Developing Portlets 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: Stock Watchlist 188
Chapter 6
No stocks being watched at the moment.
27. Create portlet.xml in the src/main/webapp/WEB-INF folder of the project.
189
Developing Portlets 28. Add the following content to portlet.xml: WatchList-Event gatein.cookbook.chapter6.WatchlistPortlet text/html view WatchList - Event portlet gi:watchStockEvent gi:watchStockEvent gatein.cookbook.chapter6.StockEvent
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. 190
Chapter 6 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:
191
Developing Portlets
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 ff
The Creating a portlet with the Portlet 2.0 Specification recipe
ff
The Communicating between portlets using Public Render Parameters recipe
192
7
Developing Using Components API In this chapter we will cover: ff
Getting started with WebUI
ff
Creating views
ff
Handling different skins in a portlet
ff
Adding the JavaScript resources to the portlet
ff
Handling different locales in a portlet
Introduction GateIn 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: ff
Event-based flow
ff
Components configuration by annotation
ff
Groovy templates for rendering
ff
Built-in AJAX support
Developing Using Components API 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 WebUI In this recipe, you will see how to add a new configuration to the portlet so that you can use the WebUI extensions.
Getting ready To 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: my.sample.MyPortlet org.exoplatform.webui.core.lifecycle.UIApplicationLifecycle
194
Chapter 7 my.sample.MyPortlet org.exoplatform.webui.application.portlet.ParentAppStateManager
2. Add this configuration in to the portlet.xml file: org.exoplatform.webui.application.portlet. PortletApplicationController webui.configuration /WEB-INF/conf/portlet/MyApplication/MyPortlet/webui/ configuration.xml
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. 195
Developing Using Components API 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: ff
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.
ff
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(WebuiRequestCo ntext context) throws Exception; abstract public void storeUIRootComponent(WebuiRequestContext context) throws Exception; abstract public void expire(String sessionId, WebuiApplication app) throws Exception; } ff
The restore operation loads the component from a store and maintains it in memory.
ff
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.
ff
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.
196
Chapter 7 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 class Many components are provided by WebUI, so you will see a list of the main components to use: ff
org.exoplatform.portal.webui.application.UIPortlet: Represents a
portlet. It is not a JSR 286 portlet, but it provides all the features. ff
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.
ff
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.
ff
org.exoplatform.webui.core.UIPortletApplication: Represents the base
portlet. It excludes many features with respect to the UIPortlet. ff
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.
ff
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.
ff
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.
197
Developing Using Components API 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 file Instead 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 lifecycle If 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 { ... processDecode(...) 198
Chapter 7 // 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: ff
ff ff ff ff ff
org.exoplatform.portal.webui.application.UIPortletLifecycle - org.exoplatform.portal.webui.application.UIPortlet org.exoplatform.portal.webui.page.UIPageLifecycle - org.exoplatform.webui.core.UIComponent org.exoplatform.portal.webui.portal.UIPortalLifecycle - org.exoplatform.portal.webui.portal.UIPortal
ff
org.exoplatform.portal.webui.workspace. UIPortalApplicationLifecycle
ff
org.exoplatform.portal.webui.workspace.UIPortalApplication
ff
org.exoplatform.webui.core.UIComponentDecoratorLifecycle
ff ff ff ff ff
- org.exoplatform.webui.core.UIComponentDecorator org.exoplatform.webui.core.lifecycle.UIFormLifecycle - org.exoplatform.webui.form.UIForm org.exoplatform.webui.core.lifecycle.UIContainerLifecycle - org.exoplatform.webui.core.UIContainer
ff
org.exoplatform.webui.core.lifecycle.UIApplicationLifecycle
ff
- org.exoplatform.webui.core.UIPortletApplication 199
Developing Using Components API 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 views In 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 ready Add 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: Started!!!
2. Add the Groovy template in the configuration.xml: my.sample.MyPortlet org.exoplatform.webui.core.lifecycle.UIApplicationLifecycle app:/groovy/MyApplication/webui/component/MyPortlet.gtmpl 200
Chapter 7 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 { ... }
201
Developing Using Components API The same thing can be done through XML: my.samples.MyPortlet org.exoplatform.webui.core.lifecycle.UIApplicationLifecycle app:/groovy/MyApplication/webui/component/MyPortlet.gtmpl org.exoplatform.portal.webui.navigation.UIPortalNavigation
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 202
Chapter 7 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:
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.
203
Developing Using Components API
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: org.exoplatform.groovyscript.text.TemplateService
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: ff
system: All scripts inside the portal root application. They are resolved by the org. exoplatform.resolver.ServletResourceResolver. It finds the resources in the ServletContext.
ff
classpath: The scripts that will be loaded through classpath. They are resolved by the org.exoplatform.resolver.ClasspathResourceResolver.
ff
app: All scripts inside the custom application. They are resolved by the org. exoplatform.resolver.PortletResourceResolver.
ff
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.
ff
war: All scripts inside a web application. They are resolved by the org. exoplatform.resolver.ServletResourceResolver.
ff
jar: All scripts inside a jar application. They are resolved by the org.exoplatform.
resolver.FileResourceResolver. ff
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.
204
Chapter 7 Notate the uicomponent key in the Groovy script. It allows loading the current UI component application that you have declared in the configuration.xml: my.sample.MyPortlet ...
The other keys available for the Groovy scripts can be found in: ff
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.
ff
locale: The nationality, for example en.
ff
nodeurl: The URL of the uicomponent represented as org.exoplatform.web. url.navigation.NodeURL instance.
ff
_ctx: The map containing all showed variables.
ff
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. 205
Developing Using Components API 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.getCompon entInstanceOfType(TemplateService.class);
Or simply: TemplateService templateService = this.getApplicationComponent(Templat eService.class)
Or by Groovy: def templateService = uicomponent.getApplicationComponent(TemplateSer vice.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 Service Another 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: ff
getTemplateList()
ff
getMaxTime(String id_template)
ff
getMinTime(String id_template)
ff
getExecutionCount(String id_template)
ff
getAverageTime(String id_template)
ff
getSlowestTemplates()
ff
getMostExecutedTemplates()
ff
getFastestTemplates()
See also ff
206
For more information on Configuring the Organization Service refer to Choosing the JAAS modules recipe in Chapter 5, Securing Portal Contents
Chapter 7
Handling different skins in a portlet Each 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 ready You 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: MyApplication MyPortlet Default /skin/portal/webui/component/My/DefaultStylesheet.css
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: MyApplication
207
Developing Using Components API 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: ResourceRequestFilter org.exoplatform.portal.application.ResourceRequestFilter ResourceRequestFilter /*
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:
208
Chapter 7
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.
209
Developing Using Components API 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: ff
org.exoplatform.web.application.javascript. JavascriptConfigDeployer: This reads the configuration of the JavaScript. It will
be exposed in the next recipe. ff
org.exoplatform.portal.resource.GateInSkinConfigDeployer: This
reads the configuration of the skin.
ff
org.exoplatform.portal.resource.GateInSkinConfigRemoval: Removes
the configuration of the skin from the context
210
Chapter 7 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: Default /skin/Stylesheet.css 0
211
Developing Using Components API gatein-sample-skin/WEB-INF/gatein-resources.xml: SimpleSkin /skin/Stylesheet.css 0
There's more... Here we extend some discussions from earlier.
Deployers The 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: ff
ADDED: The new resource is added
ff
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 style Another 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:
212
Chapter 7
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: Simple SimpleBlue SimpleViolet SimpleOrange SimplePink SimpleGreen
If you open exoResources/skin/Stylesheet.css, there is an import of the images folder of the Window Styles: @import url(PortletThemes/Stylesheet.css); 213
Developing Using Components API 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: homepage Home Page ... web HomePagePortlet ... Home Page portlet Everyone false false false
This file creates a page and associates an existing portlet to it. We have three properties here: ff ff
show-info-bar: Shows the base window. show-application-state: Basically, this shows the buttons for minimizing and
maximizing the portlet. ff
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. 214
Chapter 7 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:
215
Developing Using Components API 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.
216
Chapter 7 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 ff
The Adding the JavaScript resources to the portlet recipe
217
Developing Using Components API
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 ready Look 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: eXo.webui.Mycomponent /javascript/eXo/webui/Mycomponent.js
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. 218
Chapter 7 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 gateinservice.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: ff
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.
ff
js-path: The path of the file where the JavaScript code is located.
ff
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: ff
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.
ff
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.
219
Developing Using Components API ff
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: ... 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 portlet According 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 ready You need Version 3.2.0 of GateIn working on Tomcat or on JBOSS, the sample application, and two Resource Bundles to test the localization.
220
Chapter 7
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/WEBINF/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/WEBINF/conf/common/locales-config.xml.
2. Add to the MyPortlet_en.properties file in the MyApplication/WEB-INF/ classes/locale/portlet/MyApplication folder: word1=Test word2=Test2
3. Add also to the MyApplication/ WEB-INF/classes/locale/portlet/ MyApplication/MyPortlet_it.properties file: word1=Prova word2=Prova2
4. Add the chosen locale, in this case the Italian language, and the name of the Resource Bundle file in portlet.xml: locale.portlet.MyApplication.MyPortlet
We can call the Resource Bundle from the portlet: import import import import ... public
java.util.Locale; java.util.ResourceBundle; org.exoplatform.webui.application.*; org.exoplatform.webui.application.portlet.*; class MyPortlet extends UIPortletApplication
{
public MyPortlet() { ... PortletRequestContext context = (PortletRequestContext) WebuiRequestContext .getCurrentInstance(); Locale locale = context.getLocale(); 221
Developing Using Components API ResourceBundle resourceBundle = context.getApplicationResourceBundle(); String word = resourceBundle.getString("word1"); ... } }
Alternatively, we can call the Resource Bundle from the Groovy script as follows:
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:
222
Chapter 7 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: org.exoplatform.services.resources.LocaleConfigService org.exoplatform.services.resources.impl.LocaleConfigServiceImpl locale.config.file war:/conf/common/locales-config.xml
This is the English configuration in the locales-config.xml: en UTF-8 UTF-8 Default configuration for english locale
The first locale in the file is automatically the default, unless it is configured manually.
223
Developing Using Components API 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: ... org.exoplatform.portal.application.localization.LocalizationLifecycle
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); 224
Chapter 7 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: org.exoplatform.services.resources.LocalePolicy org.exoplatform.portal.application.localization. DefaultLocalePolicyService
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 Service The 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.
225
Developing Using Components API 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: org.exoplatform.services.resources.ResourceBundleService org.exoplatform.services.resources.impl.SimpleResourceBundleService locale.portal.expression locale.portal.services ...
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=Annulla word.category=Categoria word.change=Cambia word.city=Citt\u00E0 word.close=Chiudi word.comment=Commento word.content=Contenuto word.country=Nazione
226
8
Migrating from Existing Portals In this chapter, we will cover: ff
Migrating a transactional portlet
ff
Migrating an authenticated portlet
ff
Migrating a portlet that uses JCR
ff
Importing a skin from an existing website
Introduction Due 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
Migrating a transactional portlet Transactions 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 ready In 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: MyPortlet Required
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 import import import
javax.transaction.SystemException; javax.transaction.Transaction; javax.transaction.TransactionManager; javax.transaction.TransactionRequiredException;
import org.exoplatform.container.ExoContainer; 228
Chapter 8 import org.exoplatform.portal.application.PortalRequestContext; import org.exoplatform.webui.application.WebuiRequestContext; import org.jboss.cache.transaction.TransactionManagerLookup; ... @Override protected 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
229
Migrating from Existing Portals
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: ff
org.jboss.cache.transaction.TransactionManagerLookup: Used to get the current Transaction Manager through the getTransactionManager() method
ff
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.
230
Chapter 8
Migrating an authenticated portlet Authentication 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 ready This 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 roles = currentUser.getRoles(); // other users List liferayUsers = UserLocalServiceUtil.getUserByEmailAddre ss(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 231
Migrating from Existing Portals 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 Service UserHandler userHandler = securityService.getUserHandler(); GroupHandler groupHandler = securityService.getGroupHandler();
Organization
// 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 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()
232
Chapter 8 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 user Add 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. 233
Migrating from Existing Portals 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: ... root gtn Root Root 234
Chapter 8 root@localhost my organization ...
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 findUsersByQuery(Query q) throws Exception { if (log.isTraceEnabled()) { Tools.logMethodIn(log, LogLevel.TRACE, 235
Migrating from Existing Portals "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); } }
236
Chapter 8 6. Update the OrganizationService configuration in the organizationconfiguration.xml file: ... org.exoplatform.services.organization.OrganizationService my.extension.MyOrganizationService ...
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 email MyQuery query = new MyQuery("my organization id"); ListAccess users = userHandler.findUsersByQuery(query); System.out.println(users);
This will return the root user.
See also ff
Chapter 5, Securing Portal Contents
ff
Chapter 7, Developing Using Components API
Migrating a portlet that uses JCR JCR 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 ready The 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; 237
Migrating from Existing Portals import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.NodeIterator; ... @Override public 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 238
Chapter 8 .getComponentInstanceOfType(RepositoryService.class); Repository repository = (Repository)repositoryService .getRepository("repository");
The same thing can be done in a groovy script as follows:
uicomponent.getApplicationComponent(Reposi
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: ff
ff
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. 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. 239
Migrating from Existing Portals ff
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: ff
Storage: Can be set through the filesystem or database. The default is hsqldb. Initializer. Here, you can add plugins to initialize the nodes.
ff
Cache: The configuration of the caching of the nodes. GateIn uses JBoss cache 3.2.6 for the caching and clustering.
ff
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.
ff
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 manager The 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. 240
Chapter 8 The following code is an example of how it can be used: // Get the data distribution corresponding to the readable mode DataDistributionType type = manager .getDataDistributionType(DataDistributionMode.READABLE); // Get or create the node corresponding to "john.smith" Node node = type.getOrCreateDataNode(parentNode, "john.smith");
Clustered nodes JBoss Cache is an open source framework used for distributed and transactional caching. GateIn uses it for clustering. Basically, there are two configurations: ff
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. ff
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 cache = 241
Migrating from Existing Portals cacheService.getCacheInstance("/production"); cache.put("mynewkey", "mynewvalue"); ff
And here is an example of a value exported from the cache: ... String value = cache.get("mynewkey"); ...
ff
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 service Webdav (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:
242
Chapter 8 Log in as any GateIn user:
Enter the webdav path:
243
Migrating from Existing Portals You are now connected. From here, you can add a new node:
Importing a skin from an existing website A 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 ready A 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. 244
Chapter 8 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: google-skin ResourceRequestFilter org.exoplatform.portal.application. ResourceRequestFilter ResourceRequestFilter /* GateInServlet org.gatein.wci.api.GateInServlet 0 245
Migrating from Existing Portals GateInServlet /gateinservlet
3. Here is gatein-resources.xml: … GoogleSkin /skin/Stylesheet.css 0 web BannerPortlet GoogleSkin /skin/webPortlet/webui/component/ UIBannerPortlet/Stylesheet.css …
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 { 246
Chapter 8 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:
247
Migrating from Existing Portals 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:
248
Chapter 8
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; 249
Migrating from Existing Portals 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:
250
Chapter 8
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 ff
The Handling different skins in a portlet recipe in Chapter 7, Developing Using Components API
251
9
Managing Gadgets In this chapter we will cover: ff
Importing existing gadgets
ff
Removing gadgets
ff
Creating gadgets
ff
Changing the category of a gadget
ff
Resizing gadgets
ff
Making the gadget a Portlet
ff
Setting user preferences
Introduction Gadgets 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
Importing existing gadgets In 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 ready In 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:
254
Chapter 9 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:
255
Managing Gadgets 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:
256
Chapter 9 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: ff
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. ff
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: ff
app:Calculator
ff
app:Calendar
ff
app:Currency
ff
app:gadget-1270571586
ff
app:OAUTH
ff
app:rssAggregator
ff
app:ServicesManagement
ff
app:SiteExportImport
ff
app:Todo 257
Managing Gadgets
There's more... Now you can see some more details of the services available for the management of gadgets.
Gadget Registry Service The Gadget Registry Service used for the management of gadgets. Here is a list of the main operations: ff
deploy(Iterable gadgets): Deploys a list of gadgets
ff
getGadget(String name): Returns the chosen gadget
ff
getAllGadgets: Returns all gadgets present in the registry
ff
saveGadget(Gadget gadget): Registers the new gadget or updates a
chosen gadget
ff ff
removeGadget(String name): Removes the chosen gadget from the registry 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-registryconfiguration.xml. Here is the configuration: org.exoplatform.application.gadget.GadgetRegistryService org.exoplatform.application.gadget.impl. GadgetRegistryServiceImpl gadgets.country 258
Chapter 9 US US gadgets.language en en gadgets.moduleId 0 0 gadgets.hostName Gadget server url eXoGadgetServer/gadgets developerInfo The group that is allowed to develop gadgets
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 Server Through 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"}}] 259
Managing Gadgets 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 gadgets This recipe will guide you through the process of removing gadgets from the dashboard.
Getting ready You 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:
260
Chapter 9 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:
261
Managing Gadgets 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.
262
Chapter 9 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: /gadgets/Calculator/Calculator.xml
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: ff
It searches for the gadget in the Gadget Registry Service and removes it.
ff
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
263
Managing Gadgets
See also ff
The Migrating a portlet that uses JCR recipe in Chapter 8, Migrating from Existing Portals
ff
The Making the gadget a Portlet recipe
Creating gadgets The creation of a gadget is very simple. This recipe will walk you through the process of creating and deploying a gadget.
Getting ready You 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: ff
Creating a pom.xml file for a new Maven project
ff
Creating a web.xml file , which is mandatory for deploying a gadget project
ff
Creating a gadget.xml file where the gadgets are declared
ff
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: 4.0.0 my skypeGadget 1.1.0-GA war GateIn Portal Skype Gadget Application skypeGadget 264
Chapter 9 2. Add a very basic web.xml file in the src/main/webapp/WEB-INF directory of your project: skypeGadget
3. Finally, add the following content in the gadget.xml file of your src/main/ webapp/WEB-INF directory: /gadgets/SkypeTalk/SkypeTalk.xml
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):
265
Managing Gadgets 5. Now look at the details of the gadgets. Create SkypeTalk.xml in the SkypeTalk folder as declared in the gadget.xml file:
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-askype-button, and the following form will open:
266
Chapter 9 7. Put your Skype username and choose a button in the window. You will end up with this code:
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:
267
Managing Gadgets 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:
268
Chapter 9 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: org.exoplatform.application.gadget.GadgetDeployer org.exoplatform.application.gadget.GadgetDeployer 269
Managing Gadgets
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: http://hosting.gmodules.com/ig/gadgets/file/1000800699 21643878012/youtube.xml
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 ff
Chapter 1, Getting Started
ff
The Migrating a portlet that uses JCR in Chapter 8, Migrating from Existing Portals
Changing the category of a gadget This recipe will guide you through the process of modifying the default category of an existing gadget.
Getting ready Prepare to use the web console, XML configuration, and a few lines of Java code.
270
Chapter 9
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:
GadgetsMine GadgetsMine 271
Managing Gadgets GadgetsMine GadgetsMine GadgetsMine *:/platform/users GadgetsMine SkypeTalk Skype Talk Skype Talk Gadget, easily manage and track your daily to-do list. gadget SkypeTalk 272
Chapter 9 *:/platform/users
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"); 273
Managing Gadgets 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 eXoGadgets = gadgetService.getAllGadgets(); // if (eXoGadgets != null) { ArrayList permissions = new ArrayList(); permissions.add(UserACL.EVERYONE); String categoryName = "Gadgets"; // CategoryDefinition category = registry. getCategory(categoryName); if (category == null) { category = registry.createCategory(categoryName);
274
Chapter 9
Resizing gadgets For the purpose of this recipe, we will be using the configuration example to resize the gadget.
Getting ready Modify 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:
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:
275
Managing Gadgets 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", "" + " TableColumnContainer" + " ColumnContainer" + " ColumnContainer" + "", "TwoColumnContainerLayout")) ; 276
Chapter 9
Making the gadget a portlet The 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 ready You 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:
277
Managing Gadgets 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: Skype Talk Portlet Portlet SkypeTalkPortlet Skype Talk Portlet org.exoplatform.webui.application.portlet. PortletApplicationController 278
Chapter 9 webui.configuration /WEB-INF/conf/portlet/skypetalk/SkypeTalkPortlet/ webui/configuration.xml text/html en locale.portlet.gadget.SkypeTalkPortlet Skype Gadget Portlet Skype Gadget Portlet SkypeTalk url local://SkypeTalk
6. This file needs a WEBUI configuration.xml file to be put inside the WEB-INF/ conf/portlet/skypetalk/SkypeTalkPortlet/webui folder: org.exoplatform.gadget.webui.component.UIGadgetPortlet org.exoplatform.webui.application.portlet.ParentAppStateManager
279
Managing Gadgets 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: org.exoplatform.portal exo.portal.portlet.dashboard war 3.2.0-GA
8. Redeploy the Skype Talk application. Now you are ready to use the new portlet.
Setting user preferences It 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 ready To 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: 280
Chapter 9 var prefs = new _IG_Prefs(__MODULE_ID__); var theURL = 'skype:'+prefs.getString('skypeid')+'?call'; ]]>
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.
281
Managing Gadgets
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: ff
http://localhost:8080/eXoResources/javascript/eXo/gadget/ Gadgets.js
ff
http://localhost:8080/eXoResources/javascript/eXo/gadget/ UIGadget.js
ff
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.
282
Chapter 9 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.
283
10
Frameworks in a Portal In this chapter, we will cover: ff
Creating a JSF 2 portlet
ff
Using jQuery in a portlet
ff
Using portlet events in JSF 2
ff
Creating a RichFaces 4 portlet
Introduction In 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 portlet In 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
Getting ready The following are required for this recipe: ff
Apache Maven
ff
An IDE of your choice
ff
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: javax.faces jsf-api 2.1 provided javax.portlet portlet-api 2.0 provided org.jboss.portletbridge portletbridge-api 3.1.0.Alpha1 org.jboss.portletbridge portletbridge-impl 3.1.0.Alpha1 runtime
286
Chapter 10 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; } }
287
Frameworks in a Portal 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 @ApplicationScoped public class OrderManager implements Serializable { private static final long serialVersionUID = -5776345927432051634L; Set orders = new HashSet(); public void addOrder(Order order) { orders.add(order); } public Set getOrders() { return orders; } public void setOrders(Set orders) { this.orders = orders; } }
288
Chapter 10 7. Create a class named Order within a package named gatein.cookbook. chapter10 with the following content: @ManagedBean @RequestScoped public 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; }
289
Frameworks in a Portal 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()); 290
Chapter 10 } 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:
291
Frameworks in a Portal New Stock Purchase
13. Create newOrder.xhtml in the src/main/webapp folder of the project. 14. Add the following content into newOrder.xhtml:
292
Chapter 10
15. Create orderSuccess.xhtml in the src/main/webapp folder of the project. 16. Add the following content into orderSuccess.xhtml: Order Success! Congratulations, your order was submitted for processing
293
Frameworks in a Portal 17. Create portlet.xml in the src/main/webapp/WEB-INF folder of the project. 18. Add the following content to portlet.xml: Order-JSF javax.portlet.faces.GenericFacesPortlet javax.portlet.faces.defaultViewId.view /newOrder.xhtml text/html VIEW Order JSF Portlet
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: chapter10-jsf 294
Chapter 10 javax.portlet.faces.renderPolicy ALWAYS_DELEGATE javax.faces.FACELETS_VIEW_MAPPINGS *.xhtml javax.faces.DEFAULT_SUFFIX .xhtml Faces Servlet javax.faces.webapp.FacesServlet 1 Faces Servlet *.faces
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:
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. 295
Frameworks in a Portal 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.
296
Chapter 10 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 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 ff
The Using portlet events in JSF 2 recipe
ff
The Creating a RichFaces 4 portlet recipe
297
Frameworks in a Portal
Using jQuery in a portlet In this recipe, we will create a stock watch list that utilizes jQuery to retrieve updated stock prices without refreshing the page.
Getting ready The following are required for this recipe: ff
Apache Maven
ff
An IDE of your choice
ff
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: javax.portlet portlet-api 2.0 provided
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.
298
Chapter 10 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); }
299
Frameworks in a Portal 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: Stock Watchlist
Ticker | Company | Stock Price | |
| | | Refresh Price |
301
Frameworks in a Portal function updatePrice(updatePriceUrl, index) { $.ajax( { type: "POST", url: updatePriceUrl, cache: false, success: function (data) { $('#price'+index). html(data); } } ); }
15. Create portlet.xml in the src/main/webapp/WEB-INF folder of the project. 16. Add the following content to portlet.xml: WatchList-jQuery gatein.cookbook.chapter10.WatchlistPortlet text/html view WatchList - jQuery portlet
302
Chapter 10 17. Create web.xml in the src/main/webapp/WEB-INF folder of the project. 18. Add the following to web.xml:
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 Chapter10jquery category. 25. Click on the Finish icon to save the page. 26. The portlet page should look like the following screenshot:
303
Frameworks in a Portal 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 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 #price'+in dex 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.
304
Chapter 10 In the JSP, 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 2 Update 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 ready The following are required for this recipe: ff
Apache Maven
ff
An IDE of your choice
ff
GateIn-3.2.0.Final-jbossas7-preview
ff
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: javax.xml.bind jaxb-api provided 2.1
305
Frameworks in a Portal 2. Create a class named OrderEvent within a package named gatein.cookbook. chapter10 with the following content: @XmlRootElement public class OrderEvent implements Serializable { private Order order; public static final QName QNAME = new QName("urn:chapter10:jsf:o rder: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 : jsf:OrderEve nt
5. Add the following into portlet.xml just before : jsf:OrderEve nt gatein.cookbook.chapter10.OrderEvent
306
Chapter 10 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 @ApplicationScoped public class OrderManagerEvent implements Serializable { private static final long serialVersionUID = -5776345927432051634L; Set orders = new HashSet(); public void addOrder(Order order) { orders.add(order); } public Set getOrders() { return orders; } public void setOrders(Set 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"); } }
307
Frameworks in a Portal 3. Create a class named OrderList within a package named gatein.cookbook. chapter10 with the following content: @ManagedBean @RequestScoped public class OrderList { @ManagedProperty(value = "#{orderManagerEvent}") private OrderManagerEvent orderManager; public void setOrderManager(OrderManagerEvent orderManager) { this.orderManager = orderManager; } public List getActiveOrders() { List active = new ArrayList(); 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: Active Orders
308
Chapter 10 Order Type #{ord.type} Trade Instruction #{ord.tradeInstruction} Account Number #{ord.accountNumber} Number of Stocks #{ord.numberStocks} Date Entered Status #{ord.status}
6. Add the following into portlet.xml just before : OrderList-JSF javax.portlet.faces.GenericFacesPortlet javax.portlet.faces.defaultViewId.view /orderList.xhtml 309
Frameworks in a Portal javax.portlet.faces.bridgeEventHandler gatein.cookbook.chapter10.OrderEventHandler text/html VIEW Order List JSF Portlet jsf:OrderEv ent
7. Add the following into web.xml: javax.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_ SYSTEM_TIMEZONE true
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.
310
Chapter 10 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. 311
Frameworks in a Portal 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 is present within to define a column heading for display as if the code read
. 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 , such as in Step 5, should use the timezone of the machine instead of UTC for displaying time.
See also ff
The Creating a JSF 2 portlet recipe
ff
The Creating a RichFaces 4 portlet recipe
Creating a RichFaces 4 portlet This 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 ready The following are required for this recipe: ff
Apache Maven
ff
An IDE of your choice
ff
GateIn-3.2.0.Final-jbossas7-preview
ff
Order-JSF project from Creating a JSF 2 portlet and Using portlet events in JSF 2
312
Chapter 10
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: org.richfaces richfaces-bom 4.2.2.Final import pom
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: org.richfaces.ui richfaces-components-ui com.google.guava guava org.richfaces.core richfaces-core-impl com.google.guava guava
313
Frameworks in a Portal 3. Inside the project's pom.xml, add the following: org.apache.maven.plugins maven-war-plugin com.google.guava
4. Create rfOrderList.xhtml in the src/main/webapp folder of the project. 5. Add the following content into rfOrderList.xhtml: Order Type 314
Chapter 10 #{ord.type} Trade Instruction #{ord.tradeInstruction} Account Number #{ord.accountNumber} Number of Stocks #{ord.numberStocks} Date Entered Status #{ord.status}
6. Add the following into portlet.xml just before : OrderList-RichFaces javax.portlet.faces.GenericFacesPortlet javax.portlet.faces.defaultViewId.view /rfOrderList.xhtml javax.portlet.faces.bridgeEventHandler gatein.cookbook.chapter10.OrderEventHandler 315
Frameworks in a Portal text/html VIEW Order List RichFaces Portlet jsf:OrderEv ent
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:
316
Chapter 10
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 WEBINF/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 to and from to . 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 ff
The Creating a JSF 2 portlet recipe
ff
The The Using portlet events in JSF 2 recipe
317
11
Managing Portal Resources with the Management Component In this chapter, we will cover: ff
Deploying the CLI to GateIn
ff
Retrieving a managed resource with RESTful Interface
ff
Exporting a portal page with the CLI
ff
Removing a portal page from the navigation
ff
Using the default portal as the foundation for a new portal site
Introduction The 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
Deploying the CLI to GateIn In this recipe, we will deploy the GateIn management CLI to a GateIn instance, and then verify that the deployment was successful.
Getting ready The following is required for this recipe: ff
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:
320
Chapter 11 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 source To 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: ff
Use a GIT client to clone the repository at git://github.com/gatein/gateinmanagement.git
ff
Download the latest source as a ZIP from https://github.com/gatein/ gatein-management/zipball/master
321
Managing Portal Resources with the Management Component 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 ff
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 ready The following is required for this recipe: ff
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/managedcomponents?format=xml http://localhost:8080/rest/private/managedcomponents is equivalent to http://localhost:8080/rest/ private/managed-components?op=read-resource
322
Chapter 11 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:
323
Managing Portal Resources with the Management Component
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 ff
The Deploying the CLI to GateIn recipe
Exporting a portal page with the CLI In this recipe, we will export the sitemap page managed resource from the portal using the CLI.
Getting ready The following are required for this recipe: ff
GateIn-3.2.0.Final
ff
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
324
Chapter 11 5. Enter the password for root user again. 6. At the shell prompt, enter the following: > export --file /tmp --filter site-name:classic;pagename: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 Interface The 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: ff
http://localhost:8080/rest/private/managed-components/ mop/portalsites/classic.zip?filter=page-name:sitemap;navuri:sitemap
ff
http://localhost:8080/rest/private/managed-components/mop. zip?filter=site-name:classic;page-name:sitemap;nav-uri:sitemap
See also ff
The Removing a portal page from the navigation recipe 325
Managing Portal Resources with the Management Component
Removing a portal page from the navigation In 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 ready The following are required for this recipe: ff
GateIn-3.2.0.Final
ff
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;pagename: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 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:
326
Chapter 11 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 path Instead 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.
327
Managing Portal Resources with the Management Component 3. Move the containing the sitemap to before of the homepage node. It should now look as follows: home Home DISPLAYED portal::classic::homepage sitemap SiteMap DISPLAYED portal::classic::sitemap
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:
328
Chapter 11
Modifying the navigation node label It 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 section and set the value within to Page List. Note that it would also be necessary to modify the label value for all languages that are present within the 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 ff
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.
329
Managing Portal Resources with the Management Component
Getting ready The following are required for this recipe: ff
GateIn-3.2.0.Final
ff
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 to testing, to Testing and to GateIn Testing. 10. Edit navigation.xml by replacing all references to the classic portal name with testing, including the 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
330
Chapter 11 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.
331
12
Managing Documents Using External ECM Systems In this chapter we will discuss the following topics: ff
Creating a portlet to integrate a CMIS repository
ff
Creating a portlet to integrate JBoss ModeShape
ff
Creating a portlet to integrate Apache JackRabbit
ff
Creating a portlet to integrate Alfresco using Spring WebScripts
Introduction In 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 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 see http://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/
334
Chapter 12
Getting ready The following are required for this recipe: ff
Maven
ff
An IDE of your choice
ff
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 CMIScompliant 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: javax.portlet portlet-api 2.0 provided org.apache.chemistry.opencmis chemistry-opencmis-client-impl 0.7.0 commons-fileupload commons-fileupload 1.2.1 javax.servlet servlet-api 2.5 provided org.apache.commons 335
Managing Documents Using External ECM Systems commons-lang3 3.0
3. Add the following to a new file in the WEB-INF folder named web.xml:
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: CmisUpload com.packtpub.gatein.cookbook.ecm.cmis. CmisUploadPortlet text/html view edit CMIS Upload Portlet endpoint false username false password false 336
Chapter 12 repositoryId false binding false
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(CmisPortletConstan ts.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; 337
Managing Documents Using External ECM Systems String errorMessage = (String) request.getParameter(CmisPort letConstants.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(CmisPortletConstan ts.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); 338
Chapter 12 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 parameters = new HashMap(); // Create a session parameters.clear(); // user credentials
339
Managing Documents Using External ECM Systems 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 repos = null; try { repos = factory.getRepositories(parameters); 340
Chapter 12 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(); 341
Managing Documents Using External ECM Systems 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 creat eDocument(session,fileItem) method; it will return a Document instance for the new content: public static Document createDocument(Session session, FileItem fileItem) throws IOException { 342
Chapter 12 String fileName = fileItem.getName(); Map properties = new HashMap(); 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): CMIS - Document uploaded correctly 343
Managing Documents Using External ECM Systems The new document was uploaded correctly, here the details: Path: Name: Id: Base type: Download URL CMIS - Error during document upload
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: ff
Endpoint of the repository: This is the HTTP URL of the CMIS endpoint for the specific binding.
ff
Username: This is the username of the user session that you want to use.
ff
Password: This is the password for creating the user session.
ff
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.
ff
Binding: This is used to specify the protocol (REST or SOAP).
344
Chapter 12 The domain model of CMIS is based on these basic objects: ff
Document: This type of content can contain metadata and content streams
ff
Folder: This is a container for multiple documents
ff
Relationship: This manages the association between a target and a source document
ff
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 documents The 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(CmisPortletCons tants.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); } }
345
Managing Documents Using External ECM Systems //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 fullTextSearch(Session session, String keyword){ String statement = StringUtils.replace(CMIS_FULL_TEXT_QUERY, REPLACER, keyword); ItemIterable searchResults = session. query(statement, false); List results = new ArrayList(); 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; }
346
Chapter 12 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: CMIS - Search results Results for the keyword: <strong> Click on an item to download the document
347
Managing Documents Using External ECM Systems
See also ff
The Creating a portlet to integrate JBoss ModeShape
ff
The Creating a portlet to integrate Apache JackRabbit
ff
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 ready To follow this recipe, you will need: ff
Maven 3.x
ff
A GateIn 3.2.0 installation (JBoss or Tomcat)
ff
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.
348
Chapter 12
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: 4.0.0 my.gatein ModeshapePortlet war 0.0.1-SNAPSHOT javax.portlet portlet-api 2.0 provided javax.jcr jcr 2.0 org.modeshape modeshape-jcr 2.8.1.Final org.modeshape modeshape-cnd 2.8.1.Final org.modeshape modeshape-connector-store-jpa 2.8.1.Final 349
Managing Documents Using External ECM Systems org.modeshape modeshape-connector-filesystem 2.8.1.Final org.modeshape modeshape-web-jcr-rest-client 2.8.1 org.slf4j slf4j-log4j12 1.5.8
2. In portlet.xml, declare the two portlets as follows: LocalModeshapePortlet my.gatein.LocalModeshapePortlet text/html view edit help Modeshape Local Portlet RemoteModeshapePortlet my.gatein.RemoteModeshapePortlet text/html 350
Chapter 12 view edit help Modeshape Remote Portlet
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 import import import ...
org.modeshape.jcr.JcrRepositoryFactory; java.util.ServiceLoader; javax.portlet.*; 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(); } } } 351
Managing Documents Using External ECM Systems 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 import import import import import import import ...
javax.portlet.*; javax.jcr.*; org.modeshape.web.jcr.rest.client.IRestClient; org.modeshape.web.jcr.rest.client.domain.QueryRow; org.modeshape.web.jcr.rest.client.domain.Repository; org.modeshape.web.jcr.rest.client.domain.Server; org.modeshape.web.jcr.rest.client.domain.Workspace; 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 {
352
Chapter 12 List 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.
353
Managing Documents Using External ECM Systems
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: org.modeshape.graph.connector.federation. FederatedRepositorySource /Vehicles/Cars => /Cars /Vehicles/Aircraft => / Aircraft /Vehicles/UFOs => / 354
Chapter 12 The Vehicles repository virtualizes all the three repositories: Cars, Aircrafts, and UFOs. The UFOs repository maintains the references in the filesystem through the modeshapeconnector-filesystem project. The Aircraft repository needs the modeshapeconnector-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. 355
Managing Documents Using External ECM Systems 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 ff
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 ready To follow this recipe, you will need: ff
Maven 3.x
ff
A GateIn 3.2.0 installation (JBoss or Tomcat)
ff
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.
356
Chapter 12
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: 4.0.0 my.gatein JackrabbitPortlet war 0.0.1-SNAPSHOT javax.portlet portlet-api 2.0 provided javax.jcr jcr 2.0 org.slf4j slf4j-log4j12 1.5.8 org.apache.jackrabbit jackrabbit-core 2.4.3 org.apache.lucene lucene-core 3.0.3 357
Managing Documents Using External ECM Systems 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 import import import ...
org.apache.jackrabbit.api.JackrabbitRepositoryFactory; java.util.ServiceLoader; javax.portlet.*; 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");
358
Chapter 12 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. 359
Managing Documents Using External ECM Systems 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: ff
ff
org.apache.jackrabbit.repository.uri: The JNDI name where the repository is installed, it must always start with jndi:// java.naming.factory.initial: The context used by the client to receive the
JNDI information ff
java.naming.provider.url: The URL where the repository waits for
the connections
See also ff
The Creating a portlet to integrate JBoss ModeShape recipe
ff
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.
360
Chapter 12 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/springwebscripts/spring-webscripts-documentation/
The main components that you can use to implement a WebScript are as follows: ff
Descriptor (DESC): Describes the service, URLs, authentication, and the output
ff
Controller (JavaScript or Java): This is the business logic used to read and write against the repository (optional)
ff
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.
361
Managing Documents Using External ECM Systems We will demonstrate how to implement two services exposed by WebScripts for providing an advanced search portlet in the portal: ff
A WebScript is available via GET for showing the search form (DESC + FTL)
ff
A WebScript is available via POST for executing search and showing the results (DESC + JavaScript + FTL)
Getting ready The following are required for this recipe: ff
An IDE of your choice
ff
GateIn-3.2.0.Final-tomcat6
ff
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: Full Text Search Form Show the keyword search form /gatein/search /gatein/search.portlet guest required GateIn - ECM - Portlets
362
Chapter 12 3. Now we need to create a new file for the FreeMarker template named fulltextsearch.get.html.ftl and enter a similar snippet: Alfresco - Full Text Search |