Вы находитесь на странице: 1из 161

WCB DEVELOPMENT

TUTORIAL
GX W E B M A N A G E R 9

Date
October 24, 2011
Target audience
Developer
Target GX WebManager version
GX WebManager 9.16 and higher
Document ID and version
GXD0056_en, version 1.70

WCB DEVELOPMENT Tutorial

SUMMARY
In GX WebManager version 9.0, the development of WebManager Component Bundles (WCBs)
were introduced. WCBs are developed in a Java environment. This document is a tutorial on
how to develop a WCB.
The latest version of this document is available at:
http://connect.gxsoftware.com/WebManager/Documentation.htm
The Javadoc for the GX WebManager API is available at:
http://connect.gxsoftware.com/javadoc/webmanager9/

PREREQUISITES
Content Management

Content Management - Basic

Content Management - Advanced

Basic knowledge of:

Java

Maven

JSPs

Spring

Dependency injection

XML/XSL

OSGi

JSR-170

GX WebManager

RELATED TOPICS

Design API

e-mail info@gx.nl

http://www.gx.nl/

3/161

WCB DEVELOPMENT Tutorial

VERSION CONTROL
Version

Date

Description of the change

1.0

January 15, 2008

Initial version based on 9.2 version

1.01

January 23, 2008

Added section to Chapter 7 about deploying custom libraries

1.02

February 26, 2008

Added document ID on title page

1.10

April 1, 2008

Updated for WebManager version 9.4 (section 6.3 on the WCB

1.20

July 10, 2008

Updated for GX WebManager 9.5.

1.21

October 22, 2008

Several textual and substantive improvements

lifecycle)

Added sections on service dependencies


Updated event management chapter
Updated content migration chapter
1.22

October 22, 2008

Added section on Entity Manager versus JCRUtil


Added chapter on scheduled tasks
Added chapter on session management

1.23

October 28, 2008

Updated signature of run method in scheduled tasks

1.24

November 13, 2008

Added remark about invocation of setEntityClassNames from

1.25

November 21, 2008

1.26

December 18, 2008

Changed pathnames in the test bundles section.

1.30

January 22, 2009

Updated for GX WebManager 9.7

1.31

February 11, 2009

Formatting

multiple component definitions.


Fixed closing NB remarks about @Child and @Collection
annotations which were incorrect.

Added chapter 1, including an explanation of force


registration of WCBs
1.40

July 10, 2009

Updated for GX WebManager 9.8

1.50

May 11, 2010

Updated for GX WebManager 9.12

1.51

July 1st, 2010

Added references to WCB development guidelines where

1.52

August 17, 2010

Code sample on page 69 corrected

1.60

April 7, 2011

Updated for GX WebManager version 9.14

1.70

October 24, 2011

Updated for GX WebManager version 9.16

appropriate

e-mail info@gx.nl

http://www.gx.nl/

4/161

WCB DEVELOPMENT Tutorial

TABLE OF CONTENTS
1

Introduction ............................................................................................... 11

1.1

Platform strategy ...................................................................................... 11

1.2

WebManager components ............................................................................. 12

1.3

WCB certification ...................................................................................... 13

1.4

Getting started ......................................................................................... 13

1.5

Further reading ......................................................................................... 15

2
2.1

2.2

2.3

Adding an input field to a component ................................................................. 17


Adding a field to an Element component ........................................................... 17
2.1.1

Adding a property to the Business object ................................................... 18

2.1.2

Adding a property to the form backing object ............................................. 18

2.1.3

Adding a language label ....................................................................... 18

2.1.4

Adding a field to the edit JSP ................................................................. 19

2.1.5

Adding a field to the show JSP ............................................................... 19

2.1.6

Result ............................................................................................ 19

Adding a field to a Panel component ................................................................ 20


2.2.1

Adding a property to the form backing object ............................................. 20

2.2.2

Adding a language label ....................................................................... 21

2.2.3

Adding a field to the edit JSP ................................................................. 21

2.2.4

Result ............................................................................................ 21

Adding a field to a Media Item component ......................................................... 22


2.3.1

Adding a metadata field to the Media item version ....................................... 23

2.3.2

Adding a metadata field to the form backing object ...................................... 23

2.3.3

Adding a language label ....................................................................... 23

Adding a field to the edit JSP ........................................................................... 24


2.3.4
2.4
3

Result ............................................................................................ 24

Creating submenus ..................................................................................... 25


More component types ................................................................................... 29

3.1

Introduction............................................................................................. 29

3.2

Presentation component .............................................................................. 29


Using multiple styles at the same time ................................................................ 30
3.2.1

Using one runtime style ....................................................................... 30

3.3

Service component ..................................................................................... 31

3.4

Form component ....................................................................................... 31

3.5

Servlet component ..................................................................................... 31

4
4.1

Architectural concepts explained ...................................................................... 33


MVC ...................................................................................................... 33

e-mail info@gx.nl

http://www.gx.nl/

5/161

WCB DEVELOPMENT Tutorial

4.2

Spring MVC ............................................................................................. 33

4.3

Business / domain object ............................................................................. 34

4.4

Form backing object .................................................................................. 34

4.5

Controller ............................................................................................... 34

4.6

Abstraction layers ..................................................................................... 34

4.7

OSGi ..................................................................................................... 36

4.8

Service framework .................................................................................... 36

4.9

WCBs .................................................................................................... 37

4.10

Component definitions ............................................................................. 37

4.11

Inversion of Control (IoC) .......................................................................... 38

4.12

Dependency Injection .............................................................................. 38

Licensing .................................................................................................. 39

5.1

Default licensing ....................................................................................... 39

5.2

License a component .................................................................................. 39

Authorization ............................................................................................. 41

6.1

Default authorization ................................................................................. 41

6.2

Permission category ................................................................................... 41

6.3

Permissions ............................................................................................. 42

6.4

Permission groups ..................................................................................... 42

6.5

Using permissions ...................................................................................... 43

6.6

Element permission ................................................................................... 44

The WCB life cycle ....................................................................................... 45

7.1

WCB versus OSGi bundle .............................................................................. 45

7.2

Lifecycle of an OSGi bundle .......................................................................... 45

7.3

7.2.1

OSGi bundle states ............................................................................. 45

7.2.2

OSGi bundle lifecycle.......................................................................... 45

Lifecycle of a WCB .................................................................................... 46


7.3.1

Bundle Manager Workflow .................................................................... 48

installBundle() ......................................................................................... 48
update() ................................................................................................ 48
start() ................................................................................................... 48
stop() .................................................................................................... 48
uninstall() .............................................................................................. 49
purge() .................................................................................................. 49
7.3.2

Shutting down WCBs in a clustered environment .......................................... 49

isShuttingDown() ...................................................................................... 49

7.4

7.3.3

WCB Lifecycle Methods ........................................................................ 50

7.3.4

WCB lifecycle in a dual master clustered environment ................................... 52

WCB management console ........................................................................... 53

e-mail info@gx.nl

http://www.gx.nl/

6/161

WCB DEVELOPMENT Tutorial

7.5

Felix shell TUI .......................................................................................... 54


7.5.1

Managing WCBs from the Felix Shell TUI .................................................... 55

7.6

Service dependencies.................................................................................. 56

7.7

Using Services in static methods ..................................................................... 57

Deploying arbitrary resources ........................................................................... 59

8.1

Introduction............................................................................................. 59
8.1.1

Deploying custom JSP tags .................................................................... 59

8.1.2

Deploying custom JSP tag files ............................................................... 60

8.1.3

Adding and accessing an arbitrary resource in a WCB ..................................... 60

8.1.4

Deploying and accessing static files ......................................................... 61

8.1.5

Deploying custom libraries .................................................................... 63

Migration ................................................................................................... 65

9.1

Introduction............................................................................................. 65

9.2

Version number ......................................................................................... 65

9.3

WCB updates ............................................................................................ 66

9.4

Changes in the data model ........................................................................... 66


9.4.1

Explicitly setting the Platform Dependency ................................................ 67

9.4.2

Example .......................................................................................... 67

10

Configuration Management ........................................................................... 71

10.1

Adding properties to the Configuration Management ........................................... 71

10.1.1

Create config_metatype.xml ............................................................... 71

10.1.2

Create config.xml ............................................................................ 72

10.1.3

Register the configuration set ............................................................. 72

10.2

Using custom configuration properties ........................................................... 73

10.2.1

Define a service component in the Activator ............................................ 73

10.2.2

Define an interface for the HWC service ................................................. 74

10.2.3

Create an implementation of the interface .............................................. 74

(continued from the previous page) ................................................................... 75


10.2.4

Add a dependency to the configuration service ......................................... 76

10.2.5

Use the dependency in the implementation .............................................. 76

10.2.6

Changing the values of the properties .................................................... 77

11

Online help.............................................................................................. 79

11.1

Enabling Online help in a WCB ..................................................................... 79

11.2

Providing the Online help files .................................................................... 80

11.3

Writing the Online help files ....................................................................... 81

11.3.1

Including an image ........................................................................... 82

11.3.2

Creating a reference to a character in an image ........................................ 82

11.3.3

Creating a link to an external URL ........................................................ 83

e-mail info@gx.nl

http://www.gx.nl/

7/161

WCB DEVELOPMENT Tutorial

11.3.4

Creating a table ............................................................................. 83

11.4

Adding the help button to a Panel ................................................................ 84

11.5

Adding the help button to an Element ........................................................... 85

11.6

Adding the help button to a custom Media Item ................................................ 86

12

Installing WCBs and WCAs ............................................................................ 87

12.1

Installing a WCB or WCA using the WCB Management Console ................................ 87

12.2

Installing a WCB or WCA using the work/deploy directory .................................. 88

13

The Entity Manager .................................................................................... 91

13.1

Introduction ......................................................................................... 91

13.2

Implementing an Entity ............................................................................ 91

13.3

Registering an Entity ............................................................................... 94

13.4

Using the Entity Manager .......................................................................... 94

13.5

Entity Management in the controller ............................................................. 95

13.6

Entity retrieval ...................................................................................... 96

13.7

Property annotations ............................................................................... 98

13.7.1

@Property .................................................................................... 98

13.7.2

@Child ........................................................................................ 98

13.7.3

@Collection .................................................................................. 99

13.8

Components of the Entity Manager .............................................................. 100

13.8.1

Entity Manager .............................................................................. 100

13.8.2

Entity Domains .............................................................................. 101

13.8.3

Entity Factories ............................................................................. 101

13.8.4

Persistence Managers ...................................................................... 101

13.9
14

The Entity Manager versus JCRUtil .............................................................. 101


Extensibility ........................................................................................... 103

14.1

Extension consumers .............................................................................. 105

14.1.1

Registering a WCB consumer component ................................................ 105

14.1.2

Registering consumer extensions ......................................................... 105

14.2

Extension providers ................................................................................ 105

14.2.1

Register a WCB provider component ..................................................... 105

14.2.2

Registering provider extensions .......................................................... 106

14.3

Alternate view extension example .............................................................. 108

14.3.1

ExtensionProviderFactory ................................................................. 108

14.3.2

AlternateViewProvider ..................................................................... 109

14.3.3

AlternateViewController .................................................................. 110

14.3.4

Implement alternate view (jsp) .......................................................... 110

14.4

Publishing and Subscribing to Events ............................................................ 111

14.4.1

WebManager nl.gx.webmanager.services.event Package .................... 112

e-mail info@gx.nl

http://www.gx.nl/

8/161

WCB DEVELOPMENT Tutorial

14.4.2

Writing an event handler ................................................................. 112

14.4.3

Subscribing to events ...................................................................... 114

15

Adding a complex field to a component .......................................................... 117

15.1

Editing multi value property with multi selection field ...................................... 117

15.2

Editing multi value property with multiple checkboxes ...................................... 119

15.3

Editing multi value property with multi selection field and multiple checkboxes ........ 120

15.4

Editing multi value property with single selection field and multiple checkboxes ....... 122

16

Creating and using a testbundle ................................................................... 125

16.1

What is a testbundle? ............................................................................. 125

16.2

Creating a testbundle ............................................................................. 125

16.3

Building and deploying the testbundle ......................................................... 126

16.4

Running the testbundle ........................................................................... 126

16.5

Adding tests to the default testbundle ......................................................... 126

16.6

Accessing the WebManager API from the testbundle ......................................... 127

16.7

Using the Spring mock framework ............................................................... 130

17

Scheduled tasks ...................................................................................... 131

17.1

Introduction ........................................................................................ 131

17.2

The service ......................................................................................... 131

17.3

Schedule a task .................................................................................... 131

17.4

Session management .............................................................................. 133

17.5

Schedule updates .................................................................................. 133

17.6

Scheduled tasks in a dual master clustered environment .................................... 134

18

Session Management ................................................................................. 135

18.1

Sessions ............................................................................................. 135

18.2

Session stack ....................................................................................... 135

18.3

Retrieving and creating sessions................................................................. 136

19

Various topics......................................................................................... 139

19.1

Property Editors ................................................................................... 139

19.1.1

Implement the custom Property Editor ................................................. 140

19.1.2

Register the custom Property Editor .................................................... 141

19.1.3

The custom Property Editor in action ................................................... 141

19.2

Validators ........................................................................................... 142

19.3

HTTP Client ........................................................................................ 143

19.4

Adding Custom Indicators to the Performance Dashboard ................................... 145

19.5

Creating an extension for the User Profiles component .................................... 147

19.5.1

Generating the sample profile extension WCB from the user profiles archetype . 148

19.5.2

UserManagement interface methods .................................................... 150

e-mail info@gx.nl

http://www.gx.nl/

9/161

WCB DEVELOPMENT Tutorial

19.5.3

Custom profile declarations............................................................... 152

19.5.4

Adding a custom tab to the User Profiles component ............................... 154

19.5.5

Adding a custom field to the user details tab .......................................... 158

19.5.6

Exporting data from custom fields ....................................................... 161

e-mail info@gx.nl

http://www.gx.nl/

10/161

WCB DEVELOPMENT Tutorial

INTRODUCTION

1. 1

Pl at f or m st ra t e gy

It was important to re-organize the architecture of GX WebManager in such a way that new
vertical and horizontal applications could quickly be built on top of GX WebManager. This lead to
the platform strategy.
In previous releases almost all functionality was interconnected. As a result, adding, removing, or
changing functionality in GX WebManager was a risky business. Unwanted side-effects could
happen as a result of such modifications. Therefore, a lot of effort had to be made to test and
debug the complete application after a sometimes minor change.
In GX WebManager 9, the platform and the components have been separated. Modifying the
platform implies checking if the other functionalities of the platform are still intact. Making
changes to a component implies only checking if that specific component still behaves as
expected.
As only GX makes changes to the platform, in a project it is not possible to affect the platform
any more. As a result, the engine of GX WebManager will always as be stable as the last release
and will have a non-changing interface with the components. Only individual components (called
WCBs, WebManager Component Bundles) can be modified and therefore be corrupted.

Solution Frameworks

WCB
UI
Frame
work

WCB
WCB

WCB
WCB

WCB

UI
Frame
work

WCB

WCB

GX Platform

e-mail info@gx.nl

http://www.gx.nl/

11/161

WCB DEVELOPMENT Tutorial

A vigorous technological turn was needed to give developers the opportunity to easily build
solutions or new products on top of this GX platform. Nevertheless, GX really wanted to achieve
this for the following reasons:

With a platform, there is a much clearer distinction between customiz ation and the
standard product. By introducing the use of explicit APIs, with which functionalities can
be added to the basic platform, the procedure for developers is much clearer. Among
other things, this leads to a much more flexible and predictable met hod of executing
updates and migrations than before.

Implementation partners met, with the current APIs that are in general directed towards
customization of the design, the borders of these APIs. They could customize a lot, but
adding real functionalities in a consistent way was technically still complex.

Partners who want to create an own product with GX WebManager as an OEM product
underneath, can also do this with the platform strategy.

With this platform not only partners, but also GX itself, can easil y realize new solutions
or products like IP-TV platforms, Printing-On-Demand applications, and ASP services.

1. 2

W e b Ma n a g er c o m p o n e nts

GX WebManager 9 introduced component based development. A component is a small piece of


software that serves a particular service. This may be a GUI component, but can also be a
headless service. Each component has a particular component type which identifies the properties
and logic the component serves. In GX WebManager the possible component types are:
Component type

Description

Element

Represents a content element in GX WebManager. An element can be


inserted in a page, page section, media item or page model.

Panel

Represents a panel pop-up in GX WebManager. A panel is accessible from


the top menu.

MediaItem

Represents a particular media type in the Media Repository.

Page metadata

This component allows the WCB programmer to add additional metadata


fields to each page in GX WebManager.

Form

With this component type, new Handlers, Validators and Routers can be
added to the Advanced Forms module.

Presentation

Contains a collection of design entities (JSPs, images, stylesheets)


defining the appearance of the website environment.

Service

This is a so-called headless service, it has no user interface. Examples of


services are mailing services, newsfeed imports, storage services and
scheduling services.

Servlet

A servlet is an object that receives a request and generates a response


based on that request.

e-mail info@gx.nl

http://www.gx.nl/

12/161

WCB DEVELOPMENT Tutorial

Multiple components can be combined into one WCB, a WebManager Component Bundle. A WCB is
a set of components that logically belong to the same software component. For example, an
authorization WCB may contain an authorization service as well as a panel in which users and
authorization can be maintained.
Multiple WCBs can be combined into a WCA, a WebManager Component Archive. This is in
particular useful for system administrators: a set of WCBs can be uploaded into a GX WebManager
installation with only a single update.

1. 3

WC B c er ti f i c a ti o n

WCBs can be certified when they conform to the guidelines described in the document WCB
Development Guidelines. Conforming to these guidelines will improve the overall quality of the
WCB

in

all

its

aspects;

well-designed,

well-documented,

consistent,

compatible,

Internationalization ready and migration ready. For that reason it is important to know and
understand these guidelines before you start developing them. The WCB Development Guidelines
document

can

be

download

from

http://connect.gxsoftware.com/WebManager/Documentation.htm.
Since these guidelines are that important this document will reference the relevant guideline
when appropriate.

1. 4

G etti n g st ar te d

The best way to start developing a WCB is by using an archetype. The example command below
creates an element archetype:
mvn archetype:generate
-DinteractiveMode=false
-DarchetypeGroupId=nl.gx.webmanager.archetypes
-DarchetypeArtifactId=webmanager-element-archetype
-DarchetypeVersion=<WebManager Version>
-DgroupId=com.gxwebmanager.helloworld
-DartifactId=helloworldelement
-Dclassprefix=HelloWorld
-s ..\WebManager9\settings.xml
The command creates a folder structure and a couple of files. For more information on how to
create

WCBs

using

archetypes,

see

the

WCB

Development

Quick

Start

on

http://connect.gxsoftware.com/WebManager/Documentation.htm#GXD0055 .
This newly created element has an ID (HelloWorld). This ID is invented by the developer. To
prevent more than one WCB getting the same ID, these IDs should be registrated on
www.wcmexchange.com. It is best practice to register the ID before creating the WCB. Th is is the
only way to be sure that this ID is unique. Registering afterwards and finding out that the ID has
already been taken, implies recreating the WCB, which is a lot of work.
Until GX WebManager version 9.7, the WCB ID registration check worked by the WCB Management
Console making a connection to www.wcmexchange.com. Since many of the production sites do
not allow connections to the Internet, this check does not work on such installations. To solve

e-mail info@gx.nl

http://www.gx.nl/

13/161

WCB DEVELOPMENT Tutorial

this,

offline

WCB

ID

registration

has

been

introduced.

When

registering

WCB

on

www.wcmexchange.com, a key (hash of the WCB ID) will be generated. This signature should then
be added to the WCBs activator. Private and public keys will be used to provide security.
Note:

Existing WCBs that do not have the key in their activator, will still be operational.

All guidelines and conventions for WCB development , can be found in the WCB Developer

Guidelines http://connect.gxsoftware.com/WebManager/Documentation.htm#GXD00573.

e-mail info@gx.nl

http://www.gx.nl/

14/161

WCB DEVELOPMENT Tutorial

Sample activator.java file with key:


...
public class Activator extends ComponentBundleActivatorBase {
/**
* Creates and returns the bundle definition of the WCB.
* @return the bundle definition of the WCB.
*/
@Override
protected ComponentBundleDefinition getBundleDefinition() {
ComponentBundleDefinitionImpl componentBundleDefinition =
new ComponentBundleDefinitionImpl();
componentBundleDefinition.setId(WCBConstants.BUNDLE_ID);
componentBundleDefinition.setName(WCBConstants.BUNDLE_NAME);
componentBundleDefinition.setNameSpace(WCBConstants.NAMESPACE_URI);
componentBundleDefinition.setDescription(WCBConstants.BUNDLE_DESCRIPTION);
componentBundleDefinition.setComponentDefinitions(getComponentDefinitions());
componentBundleDefinition.setWCBKey("69867a502f84ab7d36cb69a0d65a02c1");
return componentBundleDefinition;
}
...

1. 5

F urt h e r r e a di n g

Where to find more information:

WCB Development courses see


http://www.gxsoftware.com/nl/producten/diensten/Training.htm .

http://connect.gxsoftware.com/WebManager/Documentation.htm - Documentation on
specific license components and especially the Software Development Kit.

http://connect.gxsoftware.com/Forums.htm

Discussions

about

GX WebManager,

including discussions related to WCB Java development.

Articles van be found on http://connect.gxsoftware.com/Blogs.html.

The WIKI contains HOWTOs, samples, and best practices and can be found here:
http://connect.gxsoftware.com/confluence/display/GXDEV/Home .

e-mail info@gx.nl

http://www.gx.nl/

15/161

WCB DEVELOPMENT Tutorial

e-mail info@gx.nl

http://www.gx.nl/

16/161

WCB DEVELOPMENT Tutorial

ADDING AN INPUT FIEL D TO A COMPONENT

2. 1

A d di n g a fi el d t o a n El em e nt c o m p o n e nt

The WCB Quick start guide explains how to create a very basic element component using the
element archetype. The archetype generates the following files (in the case of the HelloWorld
example):
Resource

Description

Activator.java

Bundle activator

CustomElement.java

Interface representing the custom element

CustomElementController.java

Controller of the element component

CustomElementFBO.java

Form backing object of the element component

CustomElementImpl.java

Component and element implementation (Business


Object)

editCustomElement.jspf

JSP that renders the element in the Edit environment

messages_en_US.properties

US English language file

messages_nl_NL.properties

Dutch language file

showCustomElement.xml

Descriptor of the JSP rendering the element on the


Website environment

showCustomElement.jspf

JSP rendering the element on the Website environment

smallCustomElementIcon.gif

Small icon associated with the element used for


example in the Insert menu

largeCustomElementIcon.gif

Large icon associated with the element used for


example in the toolbar

In this section we explain how to enhance this component with an additional field called author.
To add this field to the Hello World element component the following steps must be performed:
1.

Add the property to the Business object (CustomElementImpl.java)

2.

Add the property to the form backing object (CustomElementFBO.java)

3.

Add a label to the language files to translate Author in the supported languages.

4.

Add the field to the edit JSP (editCustomElement.jspf)

5.

Add the field to the show JSP (showCustomElement.jspf)

Throughout this section the HelloWorld element will be used as an example.

e-mail info@gx.nl

http://www.gx.nl/

17/161

WCB DEVELOPMENT Tutorial

2.1.1

Adding a property to the Business object

The Business object, CustomElementImpl.java represents the data model of the Hello World
element. Since we want the author field to be persisted, we will add this property to this class.
We define the property as an instance variable and add a getter and setter for it:
@Property
public String getAuthor() {
return JcrUtil.getString(getNode(), "myJcrPrefix:author");
}
public void setAuthor(String author) {
JcrUtil.setString(getNode(), "myJcrPrefix:author", author);
}

2.1.2

Adding a property to the form backing object

Since author also has to be editable in the Edit environment, we also add the field to the Form
backing object, CustomElementFBO.java. This is done in a similar way as was done for the
Business object:
private String myAuthor;
public String getAuthor() {
return myAuthor;
}
public void setAuthor(String author) {
myAuthor = author;
}

2.1.3

Adding a language label

Because we want the word Author to be translated in the proper language in the Edit
environment as well as the Website environment, we must define a language label for it. The
element

archetype

generated

(messages_en_US.properties)

two
and

one

language
for

Dutch

files,

one

for

US

English

(messages_nl_NL.properties)

(additional language files can also be added). When creating language files, be sure that they
conform to the WCB Development Guidelines (G026, G027 and G028 in particular).
To each of these language files the author label must be added. The ID of the label is suffixed by
an = and followed by the translated value in the language to be added. The following code
snippet needs to be added to the messages_en_US.properties and must be repeated for
each supported language:
helloworldelement.author=Author

e-mail info@gx.nl

http://www.gx.nl/

18/161

WCB DEVELOPMENT Tutorial

2.1.4

Adding a field to the edit JSP

Since Author must be editable by users logged in to the Edit environment of WebManager, we
must add

the

field

to the JSP

that renders

the element in

the

Edit environment

(editCustomElement.jspf). Standard tags for property editing are supported by the wmedit
tab library. The wmedit tag library generates the HTML for editing a property of a particular
type. The property type that the tag should generate the HTML for is provided after the :
separator. For a regular text input field the edit tag is input. To create an input field for editing
the author, we add this to the JSP:
<fmt:message
key="helloworldelement.author"
/>:ID we added to the language fields in
Note:
The helloWorldElement.author
refers to the label
<wmedit:input
path="author"
/> uses two tag libraries; fmt and wmedit. These tag
the
previous subsection.
This code snippet
Libraries must be imported into the JSP, therefore the header of the JSP should contain at least
these lines:
<%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>
<%@ taglib prefix="wmedit" uri="http://www.gx.nl/taglib/wmedit" %>

2.1.5

Adding a field to the show JSP

The Author field can now be edited in the Edit environment but must also be shown in the
Website environment. We therefore enhance the show JSP (showCustomElement.jspf) to
display its value:
<c:set var="element" value="${presentationcontext.element}" />
Author: ${element.author}

2.1.6

Result

After adding the field to the element, the result is as follows:

Figure 1. HelloWorld element with an additional Author field

e-mail info@gx.nl

http://www.gx.nl/

19/161

WCB DEVELOPMENT Tutorial

2. 2

A d di n g a fi el d t o a Pa n el c o m p o n e nt

In the Quick start guide it was explained how to create a very basic panel component using the
panel archetype. This panel consisted of only one tab, the HelloWorld tab. The archetype
generates the following files in case of the HelloWorld example:
Resource

Description

Activator.java

Bundle activator

CustomPanelController.java

Controller of the whole panel

CustomPanel.java

Form backing object of the whole panel

CustomTabController.java

Controller of the HelloWorld tab contained by the


panel

CustomTabFBO.java

Form backing object of the HelloWorld tab contained


by the panel

customTab.jspf

JSP that renders the HelloWorld tab

messages_en_US.properties

US English language file

messages_nl_NL.properties

Dutch language file

In this section we explain how to enhance this component with an additional field called author.
To add this field to the Hello World panel component the following steps must be performed:
1.

Add the property to the form backing object of the tab on which the field should be
displayed (CustomTabFBO.java)

2.

Add a label to the language files to translate Author in the supported languages.

3.

Add the field to the edit JSP (customTab.jspf)

Note: In this example we do not persist the author. If we do want to persist the value we would
define a Business object (which has nothing to do with this panel) and persist the entered value in
the onSubmit of the tab controller.

2.2.1

Adding a property to the form backing object

Since the author must be editable in the panel within the Edit environment, we also add the field
to the Form backing object, CustomTabFBO.java:
private String myAuthor;
public String getAuthor() {
return myAuthor;
}
public void setAuthor(String author) {
myAuthor = author;
}

e-mail info@gx.nl

http://www.gx.nl/

20/161

WCB DEVELOPMENT Tutorial

2.2.2

Adding a language label

See section 2.1.3 for a complete description of adding language labels. The following snippet
should be added to each language file:
helloworldpanel.author=Author
When adding language labels, be sure that they conform to the WCB Development Guidelines
(G027 and G028 in particular).

2.2.3

Adding a field to the edit JSP

See section 2.1.4 for a complete description of adding a field to the edit JSP. The following
snippet should be added to the customTab.jspf:
<fmt:message key="helloworldpanel.author" />:
<wmedit:input path="command.author" />

2.2.4

Result

After adding the field to the element, the result is as follows:

Figure 2. HelloWorld panel with an additional Author field

e-mail info@gx.nl

http://www.gx.nl/

21/161

WCB DEVELOPMENT Tutorial

2. 3

A d di n g a fi el d t o a M e di a It e m c om p o n e nt

In the Quick start guide it was explained how to create a very basic media item component using
the media item archetype. The archetype generates the following files in case of the
HelloWorld example:
Resource

Description

Activator.java

Bundle activator

CustomMediaItemController.java

Controller of the metadata part of the custom


media item

CustomMediaItemVersion.java

Interface representing the custom media item


version

CustomMediaItemVersionFBO.java

Form backing object for the custom metadata


part of the custom media item version

CustomMediaItemVersionImpl.java

Implementation of the custom media item


version

editMetadata.xml

Descriptor of the JSP that renders the custom


metadata part of the custom media item

editMetadata.jspf

JSP that renders the custom metadata part of


the custom media item

messages_en_US.properties

US English language file

messages_nl_NL.properties

Dutch language file

custommediaitem.gif

Icon associated with the media item. Used in


the menu and Media Repository search results.

In this section we explain how to enhance this component with an additional metadata field
called author. To add this field to the custom metadata part of the custom Media item
component the following steps must be performed:
1.

Add the property to the Media item version (CustomMediaItemVersionImpl.java)

2.

Add the property to the form backing object (CustomMediaItemVersionFBO.java)

3.

Add a label to the language files to translate Author in the supported language s.

4.

Add the field to the edit JSP (editMetadata.jspf)

e-mail info@gx.nl

http://www.gx.nl/

22/161

WCB DEVELOPMENT Tutorial

2.3.1

Adding a metadata field to the Media item version

The Business object CustomMediaItemVersionImpl.java represents the data model of the


Hello World media item. Since we want the author metadata field to be persisted we add this
property to this class. We define the property as instance variable and add a getter and setter for
it:
@Property
public String getAuthor() {
return JcrUtil.getString(getPrivateNode(), "myJcrPrefix:author");
}
public void setAuthor(String author) {
JcrUtil.setString(getPrivateNode(), "myJcrPrefix:author", author);
}

2.3.2

Adding a metadata field to the form backing object

Since the author also has to be editable in the Edit environment, we also add the metadata field
to the Form backing object of the metadata part (CustomMediaItemVersionFBO.java). This
is done in a similar way as we did it for the Business object:
private String myAuthor;
public String getAuthor() {
return myAuthor;
}
public void setAuthor(String author) {
myAuthor = author;
}

2.3.3

Adding a language label

See section 2.1.3 for a complete description of adding language labels. The following snippet
should be added to each language file:
helloworldmediaitem.author=Author
When adding language labels, be sure that they conform to the WCB Development Guidelines
(G027 and G028 in particular).

e-mail info@gx.nl

http://www.gx.nl/

23/161

WCB DEVELOPMENT Tutorial

Adding a field to the edit JSP


See section 2.1.4 for a complete description of adding fields to the edit JSP. The following
snippet should be added to the editMetadata.jspf:
<fmt:message key=" helloworldmediaitem.author" />:
<wmedit:input path="author" />
Note: After running the JSP, the language labels are automatically generated (Configure >
Language labels).

2.3.4

Result

After adding the field to the element, the result is as follows:

Figure 3. Media item with an additional Author field in the custom metadata

e-mail info@gx.nl

http://www.gx.nl/

24/161

WCB DEVELOPMENT Tutorial

2. 4

C re a ti n g s u bm e n us

The GX WebManager MenuItemComponentDefinition interface, which is extended by the


ElementComponentDefinition

and

PanelComponentDefinition

interfaces,

contains

methods that make it possible to create submenus and place new entries into a submenu. Use the
following methods to do so:

getSubMenu - Returns a string identifying the submenu within the target menu where in
which the component will appear.

getSubMenuTextMessageKey Returns the key from the message properties file of the
component which is mapped to the value that is used for the description of the submenu
within the target menu. If different components return different descriptions, one is
chosen at random (the last one registered). If this method returns null and there is
already an entry for the submenu then the current descriptions are retained.

getSubMenuLocationAfter Returns the ID of the menu item in the submenu after


which the new menu item should be inserted.

getSubMenuLocationBefore Returns the ID of the menu item in the submenu before


which the new menu item should be inserted.

The implementation class provides the related setters which enable you to set these for element
and panel components in the Activator class.
Notes:

For each element/panel component which you want to put in a submenu use the
setSubMenu method to pass on the identifier of the menu in which it should be put. If
there is no menu with that identifier, one will be created.

If you create a menu, be sure to call the setSubMenuTextMessageKey for at least one
of the components. This puts an entry inside that submenu which ensures that the
language labels can be retrieved from the resource file. This works in the same was as
the getMenuTextMessageKey.

The getSubMenuLocationAfter and getSubMenuLocationBefore methods are used


to designate a location for the menu inside the submenu (where the
getTargetMenuLocationAfter and getTargetMenuLocationBefore methods
designate a location for the submenu inside the main menu).

If you want to designate a location for the submenu inside the main menu, you have to
repeat this for each entry which it places inside the submenu, otherwise it may appear
at the bottom of the main menu.

All submenus that you create appear at the bottom of the GX WebManager to which it is
added. Use the getTargetMenuLocationAfter and
getTargetMenuLocationBefore methods to determine the order the submenus
appear if you create more than one submenu.

e-mail info@gx.nl

http://www.gx.nl/

25/161

WCB DEVELOPMENT Tutorial

The following code sample shows how to create a submenu. It is based on the helloworldelement
archetype:
/**
* Returns the component definition for the element component.
* @return The component definition for the element component
*/
protected ComponentDefinition getElementComponentDefinition() {
ElementComponentDefinitionImpl definition = new
ElementComponentDefinitionImpl(false,
HelloWorldElementImpl.class);
definition.setId(WCBConstants.ELEMENT_COMPONENT_ID);
...
definition.setSubMenu("sub_helloworldelements");
definition.setSubMenuTextMessageKey("element.helloworldelements");
...
return definition;
}

Note: The the messages_en_US.properties and messages_nl_NL.properties files contain


a value for the element.helloworldelement property.

e-mail info@gx.nl

http://www.gx.nl/

26/161

WCB DEVELOPMENT Tutorial

The following example is based on the GX WebManager Social Integration add -on WCBs

setGeneralDefinitionPropertiesAndPermissions(definition,
CommentsElementComponent.class.getName(),
CommentsElementImpl.class.getName(), CommentsElementController.class.getName(),
WCBConstants.ELEMENT_COMMENTS_COMPONENT_ID,
WCBConstants.ELEMENT_COMMENTS_COMPONENT_NAME,
CommentsElement.class);

// Element specific methods


definition.setMenuTextMessageKey("element.commentselement.menutitle");
definition.setPresentationScope("FacebookSocialPluginsCommentsElement");
setIcons(definition);

definition.setSubMenuLocationAfter(WCBConstants.ELEMENT_LIKEBOX_COMPONENT_ID.replaceAll(" \
\.", ""));
definition.setSubMenuLocationBefore(WCBConstants.ELEMENT_ACTIVITYFEED_COMPONENT_ID.replace
All("\\.", ""));
definition.setSubMenuTextMessageKey("element.facebooksocialplugins");

definition.setSubMenuLocationAfter(WCBConstants.ELEMENT_SEND_COMPONENT_ID.replaceAl l("\\."
, ""));
definition.setSubMenuLocationBefore(WCBConstants.ELEMENT_SEND_COMPONENT_ID.replaceAll(" \\.
", ""));
...
setGeneralDefinitionPropertiesAndPermissions(definition,
SendElementComponent.class.getName(),
SendElementImpl.class.getName(), SendElementController.class.getName(),
WCBConstants.ELEMENT_SEND_COMPONENT_ID, WCBConstants.ELEMENT_SEND_COMPONENT_NAME,
SendElement.class);

// Element specific methods


definition.setMenuTextMessageKey("element.sendelement.menutitle");
definition.setPresentationScope("FacebookSocialPluginsSendElement");
setIcons(definition);

definition.setSubMenuLocationAfter(WCBConstants.ELEMENT_LIKE_COMPONENT_ID.replaceAll(" \\."
, ""));
190
definition.setSubMenuLocationBefore(WCBConstants.ELEMENT_LIKEBOX_COMPONENT_ID.replaceAll("
\\.", ""));
...

(continued on the following page)

e-mail info@gx.nl

http://www.gx.nl/

27/161

WCB DEVELOPMENT Tutorial

setGeneralDefinitionPropertiesAndPermissions(definition,
RecommendationsElementComponent.class.getName(),
RecommendationsElementImpl.class.getName(),
RecommendationsElementController.class.getName(),
WCBConstants.ELEMENT_RECOMMENDATIONS_COMPONENT_ID,
WCBConstants.ELEMENT_RECOMMENDATIONS_COMPONENT_NAME,
RecommendationsElement.class);

// Element specific methods


definition.setMenuTextMessageKey("element.recommendationselement.menutitle");
definition.setPresentationScope("FacebookSocialPluginsRecommendationsElement");
setIcons(definition);

definition.setSubMenuLocationAfter(WCBConstants.ELEMENT_ACTIVITYFEED_COMPONENT_ID.replaceA
ll("\\.", ""));
definition.setSubMenuLocationBefore(WCBConstants.ELEMENT_FACEPILE_COMPONENT_ID.replaceAll(
"\\.", ""));
...
// Set component dependencies
definition.setDependencies(new
ComponentDependency[]{createAuthorizationServiceDependency(),
createSocialIntegrationBaseConfigurationServiceDependency()
});

definition.setSubMenu("sub_facebooksocialplugins");
// Make sure the sub-menu is positioned above the Twitter Widgets sub-menu.
definition.setTargetMenuLocationBefore("sub_twitterwidgets");

// Create the permission category and permissions


createElementPermissions(definition, componentid, clazz);

e-mail info@gx.nl

http://www.gx.nl/

28/161

WCB DEVELOPMENT Tutorial

MORE COMPONENT TYPES

3. 1

I ntr o d u cti o n

The previous chapter described the main WebManager component types element, panel and media
item. This section describes the component types presentation, service, form, and servlet.

3. 2

Pr es e nt at i o n c o m p o n e nt

A presentation component contains all the files that make up the layout of the Website
environment. By default WebManager comes with one presentation WCB called the GX
WebManager Corporate Style Presentation. This presentation can be overruled or additional
styles can be added using the presentation component. In the context of this document, a style
refers to the layout objects that are contained by a presentation component (including JSPs,
images, etc.).
The presentation component in fact only copies JSP and static files to particular directories and
as a result adjusts the style of the Website environment.

JSPs are copied from /src/main/resources/editpresentation to the <backend


web root>/WEB-INF/project/<Component bundle id> directory

Static files are copied from /src/main/resources/static

to

<static

web

root>/static
Where:

<backend web root> indicates the root folder of where the backend web application is
located,

for

example

/webmanager-webapps/webmanager-backend-

webapp/target/webmanager-backend-webapp-9.7.0-SNAPSHOT

<static web root> indicates the root folder of where the static web application is
located,

for

example

/webmanager-webapps/webmanager-static-

webapp/target/webmanager-static-webapp-9.7.0-SNAPSHOT
When creating presentation WCBs, be sure that they conform to the WCB Development Guidelines
(G083-G098, G138, G139, G140, G142, G143 and G144 in particular).

e-mail info@gx.nl

http://www.gx.nl/

29/161

WCB DEVELOPMENT Tutorial

Using multiple styles at the same time


It is possible to have two (or more) styles installed on the same WebManager installation at the
same time without them conflicting with each other. In order to do so the following two rules
must be followed:

The name property in the JSP descriptors must be unique across all installed styles. A
good way to ensure its uniqueness is to prefix the name with the WCB id.

All

static

files

must

be

located

in

unique

subfolder

within

/src/main/resources/static. A good way to ensure its uniqueness is to use the WCB


ID as folder name.
In this case the different styles will be available in WebManager and can be assigned to pages,
elements, etc., individually. When a style is removed by uninstalling the presentation component
which publishes the style, only those elements that were specifically assigned to this style ar e
affected. Henceforth, these objects will use the default style instead.

3.2.1

Using one runtime style

The opposite approach is to have multiple presentation components each of which overwrites the
other. Only one presentation component will be active at a time. In this case the following rules
must be followed:

The name property in the JSP descriptors must be the same for each target presentation
type across all styles. For example, the name of the descriptor of the page presentation
is the same for all styles, likewise for the name of the descriptor of the text element
presentation, etc.

All static files must be located in the same subfolder and have the same filename .

In this case the complete style of a website can be changed by uploading a new style. Howeve r in
this case the you must perform the following tasks in the order given:
1.

Uninstall the old style by uninstalling the old presentation component. It doesnt matter
whether you purge the content.

2.

Install the new style by installing the new presentation component.

Be aware that between the first and second step the website will have no style and so is
practically offline. This only lasts for a short time if you install the new style immediately.

e-mail info@gx.nl

http://www.gx.nl/

30/161

WCB DEVELOPMENT Tutorial

3. 3

Se rvi c e c o mp o n e n t

A service component is a component that provides a (usually headless) service that can be used by
other WCBs. Typical services are, for example, an authorization service, license service or
preferences service. They provide functionality that is available for all WCBs, therefore they are
implemented as a service.
The service component created by the archetype as described in the Quick start guide already
provides a simple service component. The only thing you have to do is to implement the service
and define the interface that it exposes. Other WCBs will be able to define a dependency on this
service component and use it.
When creating service components, be sure that they conform to the WCB Development
Guidelines (G083-G098, G112 and G151 in particular).

3. 4

F or m c om p o n e nt

A form component can be used to deploy form handlers into the WebManager application without
a server restart. The form archetype generates a handler with an empty implementation. To
implement the handler the doHandle method of the handler must be implemented.
When creating form components, be sure that they conform to the WCB Development Guidelines
(G083-G098 in particular).

3. 5

Se rvl et c o m p o n e nt

A servlet component is a component that registers a servlet which is mapped to a predefined URL.
The syntax of this URL is:
http://<hostname>:<portnumber>/<context>/wcbservlet/<componentid>
Where componentid equals the id of the component definition. The servlet component definition
contains some properties that you must pay particular attention to with regard to servlets. These
property methods are:
setName

Sets the name of the component but sets also the servlet name
(returned by
javax.servlet.HttpServlet.getServletName().

setServletClassName

Sets the class name of the servlet to be instantiated by the servlet


component.

setProperties

Defines the init parameters usually defined by adding init-param


attributes to the servlet definition in the web.xml. These
properties can be retrieved from
javax.servlet.HttpServlet.getInitParameter() and
javax.servlet.HttpServlet.getInitParameterNames

Note: The ServletContext of this servlet is the same as the ServletContext of


nl.gx.webmanager.servlet.WCBDispatcherServlet

since

this

is

the

servlet

that

dispatches all incoming HTTP requests to the appropriate component servlets.

e-mail info@gx.nl

http://www.gx.nl/

31/161

WCB DEVELOPMENT Tutorial

When creating servlet components, be sure that they conform to the WCB Development Guidelines
(G083-G098 in particular).

e-mail info@gx.nl

http://www.gx.nl/

32/161

WCB DEVELOPMENT Tutorial

ARCHITECTURAL CONCEP TS EXPLAINED

4. 1

MV C

MVC is an abbreviation of Model-View-Control and is a design pattern used extensively in GX


WebManager 9 since GX WebManager uses the Spring MVC framework which is based on this
pattern. This pattern separates software components into three responsibilities: the data model,
the view (user interface), and the controller that handles the interaction between these two. The
MVC model can be depicted as follows:

Figure 4. The Model-View-Controller model


The MVC design pattern helps decoupling the graphical user interface from the application logic.

4. 2

Sp ri n g MV C

Spring MVC is a web application framework used by WebManager 9 to ren der the Edit
environment. It is designed around a generic Spring Servlet (the DispatcherServlet) that
dispatches requests to controllers based on URL mappings defined in a XML configuration file
(springmvc-servlet.xml). Using the Spring MVC framework has the following important
advantages in the case of WebManager 9:

Spring MVC provides an API to easily apply the MVC design pattern. Models, views and
controllers are identified in the API and Spring MVC provides default implementations for
them.

Spring handles automatic binding of posted values in HTML forms onto a Java object
called the Form Backing Object. This increases ease of development since developers do
not have to parse HTTP requests themselves.

Spring MVC provides default property editors (used for converting Strings to complex
Java objects) and provides an easy way to create custom property editors. Property
editors make it easy to transform a String (coming for example from a posted HTML form
field) into a complex Java object.

Spring MVC, however, does not provide a way to deal with a component-based application like
WebManager. For this reason an additional WebManager-specific web layer has been built on top
of Spring MVC to take care of this. This API is the same as the Spring API itself. So while
developing in WebManager you do not actually see the difference.

e-mail info@gx.nl

http://www.gx.nl/

33/161

WCB DEVELOPMENT Tutorial

4. 3

B usi n es s / d o m ai n o bj e ct

A business object, or domain object, is an object that is modeled after a business concept.
Business Objects within a system are typically stateful, persistent and long-lived. Business Objects
contain business data and models the business behavior. Examples of business objects are
authors, persons, books or cars.

4. 4

F or m b a c ki n g o bj e ct

A common misperception is that the form backing object and business object are one and the
same. While a business objects target is a business concept, a Form backing objects target is a
particular view (not necessarily presenting business object properties). A form backing object is
typically not persistent, is short-lived and is only used as data transfer object between HTML and
zero or more Java business objects.

4. 5

C o nt r ol l er

A controller is the glue between the model and the view. The view and the model (usually
containing at least the Form backing object) are both created by the controller. It is the
controller that determines whether values posted by an HTML form should be submitted and if
one or more business objects should be updated. It is the controller which handles the data
transfer between the business object and Form backing object. It is the controller which receives
HTTP requests and creates the HTTP responses.

4. 6

A bs tr a cti o n l ay e rs

Separating the application into several abstraction layers is a good design principle which
prevents tight coupling of the software. Loose coupling of the software, made possible by
introducing abstraction layers, makes the software very flexible, pluggable and extendible which
is very important for a component-based application like WebManager.
For this reason WebManager 9 identifies five different abstraction layers as depicted in the figure
below:

e-mail info@gx.nl

http://www.gx.nl/

34/161

WCB DEVELOPMENT Tutorial

Figure 5. Abstraction layers in WebManager 9

User interface layer


The user interface layer contains the software components that handle the rendering of the user
interface for the end-user. Such a component is called a View. A View might generate XML and/or
PDF but mostly it will generate HTML. The source of the view can be defined in many formats, but
in most cases the language used will be JSP (which makes such a view a JSTL View).
With an edit view in the picture above we refer to a View that is used to render a software
component (like an element or panel) in the Edit environment. With a Website view we refer to
a View that renders the software component on the Website environment.
Note that when using the Spring MVC web application framework views are agnostic. This means
that views are not bound to a specific source or output format like HTML or JSP but may use any
source and any output format. However, views usually use JSP as source format and provide HTML
as output.
Web layer
The web layer is responsible for all communication between the model and the view. It retrieves
the HTTP requests, generates the view and returns the result. The controller uses a data transfer
object called the FormBackingObject to hold the data that must be rendered by the view or to
hold data submitted by an end-user using an HTML form. The controller handles the transfer of
property values between the FormBackingObject and the Business objects it represents. The
FormBackingObject will be closely related to one or more Business objects in the Domain
model layer but is not necessarily the same. For the Edit environment the controller will be a
Spring MVC controller, for the Website environment this will be the ControllerServlet.
The controller is responsible for handling all requests and returning the proper results. The Web
layer typically has dependencies with all other layers.

e-mail info@gx.nl

http://www.gx.nl/

35/161

WCB DEVELOPMENT Tutorial

Services layer
The services layer exposes several services to software components like authorization,
configuration, preferences, session management and transaction management. Services provide
business logic that involves multiple business objects. Business objects themselves usually only
contain logic to update or retrieve its own properties.
Persistence layer
The persistence layer is responsible for persisting the business objects invoked by the controller.
It is good practice to define a DAO to prevent the Business object from containing dependencies
on a particular persistence implementation like the JCR or JDBC calls.
Domain model layer
The domain model layer contains all business logic (business objects and business rules). The
domain model objects are implemented as POJOs and do not contain references to any of the
other layers. The several other layers however may refer to objects in the domain model layer.
The business objects usually contain only business logic to update and retrieve their own
properties. When business logic involves multiple business objects, it is recommended that you
use a service instead.

4. 7

OS Gi

The OSGi Service Platform specification is an open services standard that defines a standardized,
component-oriented computing environment for networked services. Adopting OSGi has several
advantages.

OSGi addresses all major concerns with regard to implementing a Service Container.
Service registry, location, and management are all well defined.

OSGi supports hot deployment of bundles (services and components) and addresses the
specific classloader issues.

OSGi is an open standard, has open source implementations, and a lot of commercial
backing.

Services in an OSGi service platform are implemented and deployed as bundles. A bundle is
actually just a JAR file containing specific manifest entries.

4. 8

Se rvi c e fr a m e w or k

The GX WebManager service framework is the central part of the GX WebManager architecture. It
allows GX WebManager to be modular by physically separating logical components into services
with well defined interfaces. As the Service Framework follows the Service Container paradigm it
provides the execution environment and the mechanisms for dynamically locating, accessing and
managing services.

e-mail info@gx.nl

http://www.gx.nl/

36/161

WCB DEVELOPMENT Tutorial

Figure 6. Abstraction layers in WebManager 9

The GX WebManager Service Framework achieves this by embedding an implementation (Felix) of


the OSGi Service Platform Specification in GX WebManager. As a result the Service Framework
domain closely resembles that of the OSGi Service platform.

4. 9

WC B s

A WCB is short for WebManager Component Bundle. It is a wrapper around an OSGi bundle with
WebManager specific enhancements. A WCB is an OSGi bundle that publishes one or more
components. Such a component may be an element, panel, media item, service, task or a form. A
component is not a concept that is defined by OSGi but is rather WebManager specific. For this
reason you cannot, for example, start or stop individual components but only the bundle as a
whole.

4. 1 0 C o m p o ne n t d ef i n i ti o ns
A component definition defines the basic behavior of the component, most importantly its type.
The supported component types are:
Component type

Purpose

Element

Add a new content element to the WebManager edit environment.

Panel

Add a new popup panel to the WebManager edit environment.

MediaItem

Add a new media item type to the WebManager edit environment.

Form

Add a new handler to the WebManager edit environment.

Presentation

Modify the presentation of the Website environment.

Service

Provide a headless service to be used by other WCBs.

Page metadata

Add an additional metadata section to pages in the GX WebManager edit


environment.

Servlet

Add a new servlet to the WebManager installation.

e-mail info@gx.nl

http://www.gx.nl/

37/161

WCB DEVELOPMENT Tutorial

4. 1 1 I nv ers i o n o f C o n tr ol ( I oC )
Inversion of control is a pattern that allows an object to become a passive participant in the
system. When the IoC technique is used, an object relinquishes control over some feature or
aspect to the framework or environment.

4. 1 2 D e p e n d e n cy I nj e ct i o n
Dependency injection is a specific type of IoC. Using this technique an object on which a service
object depends is injected by the framework into that service object. This can be done using the
setter or constructor technique (other techniques can also be used).
Dependency injection is heavily used when defining service dependencies. When a component
defines a service dependency, a reference to that service is injected by the framework into that
component instead of the component itself retrieving the reference.

e-mail info@gx.nl

http://www.gx.nl/

38/161

WCB DEVELOPMENT Tutorial

LICENSING

5. 1

D e f a ul t l i c e nsi n g

Licensing in WebManager 9 is applied on the component level, therefore a WCB can contain many
components, each of which has a different license. The components contained by the WCB are
active or inactive depending on whether the appropriate license is available for the installation in
which the WCB is deployed.
By default a component is not licensed. This means that the WCB containing the component can
be uploaded to any WebManager installation without license issues. The default licensing model is
triggered by providing the argument false as an input of the constructor of the component
definition. The examples below illustrate how to create unlicensed components.
new ElementComponentDefinitionImpl(false);
new PanelComponentDefinitionImpl(false);
new MediaItemComponentDefinitionImpl(false, CustomMediaItemVersionImpl.class)
new FormComponentDefinitionImpl(false);
new PresentationComponentDefinitionImpl(false);
Note that a Service component type is always unlicensed.

5. 2

Li c e ns e a c o m po n e nt

To license a component, you must do two things:


1.

Register the licensed component with GX in order to generate a license file including a
license for this component

2.

Indicate in the component definition that it is a licensed component by providing true


as input argument of the constructor

The examples below illustrate how to create licensed components.


new ElementComponentDefinitionImpl(true);
new PanelComponentDefinitionImpl(true);
new MediaItemComponentDefinitionImpl(true, CustomMediaItemVersionImpl.class)
new FormComponentDefinitionImpl(true);
new PresentationComponentDefinitionImpl(true);
Note that a Service component type is always unlicensed.

e-mail info@gx.nl

http://www.gx.nl/

39/161

WCB DEVELOPMENT Tutorial

e-mail info@gx.nl

http://www.gx.nl/

40/161

WCB DEVELOPMENT Tutorial

AUTHORIZATION

6. 1

D e f a ul t a ut h o ri z a ti o n

By default a WCB does not define any authorization which means that all components contained
by the WCB can be used by anyone at any time (unless a component is licensed and the
application server on which it is installed does not have the proper license). This is normally done
during development but it is not recommended in a live situation. The following section describes
how to define custom authorization.

6. 2

Pe rm i ss i o n c at e g o ry

In WebManager 9 a WCB may define zero or more permissions. A permission is a fine -grained
definition of a particular operation on one or more objects. Assigning the permission to a role
grants that role the particular permission. A permission must always be positive - it grants
particular rights and never denies rights.
Permissions always belong to one particular permission category. A WCB may define zero or more
permission categories, at most one for each component it contains. However, different
components may use one and the same permission category.
The first step in creating custom authorization is to define the permission categories. A
permission category has the following properties:
Property

Purpose

Value

A unique identifier for the permission category.

labelId

Id of the label in messages_<language>_<country>.properties


containing the translation of the name of the permission category in the
supported language(s).

showAsComponent

Indicates whether this permission category appears in the webinitiative


configuration panel of the Edit environment. If it is visible, the WCB as a
whole can be turned on and off per web initiative.

After creating an instance of the permission category and setting its properties, use
setPermissionCategory on the component definition to assign the category to a component
definition. Note that if you reuse the same permission category for another component, you
should set the permission category only once. If you define the permission category twice, this
will result in a conflict.
For example, to define a permission category for a WCB Management console panel, you would
use the following:
PermissionCategoryImpl wcbManConsoleCategory = new PermissionCategoryImpl();
wcbManConsoleCategory.setValue(WCB_MANAGMENT_CONSOLE_COMPONENT_ID);
wcbManConsoleCategory.setLabelId(WCB_MANAGMENT_CONSOLE);
wcbManConsoleCategory.setShowAsComponent(true);
panelDefinition.setPermissionCategory(wcbManConsoleCategory);

e-mail info@gx.nl

http://www.gx.nl/

41/161

WCB DEVELOPMENT Tutorial

When the WCB is deployed, the permission categories defined in each component definition
contained by the WCB are installed or updated automatically.
When defining and using permission categories, be sure that they conform to the WCB
Development Guidelines (G050 and G051 in particular).

6. 3

Pe rm i ssi o ns

A permission grants a role the rights to perform a particular operation on one or more objects.
This of course is a definition that can be interpreted in many ways, and the permission itself does
not define the exact operation it grants permission for and the object on which the operation
operates. It is the software itself using the permission that defines what rights the permission
provides.
A permission consists of the following properties:
Property

Purpose

Value

Unique identifier of the permission.

labelId

Id of the label in messages_<language>_<country>.properties


containing the translation of the name of the permission in the
supported language(s).

permissionCategory

Permission category to which the permission belongs.

To instantiate permissions invoke the constructor of PermissionImpl. The value, labelId and
permissionCategory are input arguments of the constructor.
The permissions defined in each permission category of the WCB are installed or updated
automatically when the WCB is deployed.
When defining and using permissions, be sure that they conform to the WCB Development
Guidelines (G052 and G053 in particular).

6. 4

Pe rm i ssi o n gr o u ps

Because of the fine-grained definition of permissions, assigning permissions to the proper roles
can sometimes be difficult. The default WebManager application itself already contains over 70
separate permissions and this amount will only grow in the future or when you deploy additional
WCBs. For ease of use, the concept of a permission group was introduced. A permission group is
nothing more than a collection of permissions. Instead of assigning individual permissions to a role
it is also possible to assign a permission group to a role, thus implicitly assigning a collection of
permissions to the role.
Permissions defined in a WCB can be added to such a permission group using the method
setPermissionsForPermissionGroup on the component definition. WebManager defines five
different permission groups:
Permission group ID

Purpose

CASUAL_USER_PERMISSION_GROUP

Permission group for casual users

e-mail info@gx.nl

http://www.gx.nl/

42/161

WCB DEVELOPMENT Tutorial

EDITOR_PERMISSION_GROUP

Permission group for editors

MAIN_EDITOR_PERMISSION_GROUP

Permission group for main editors

APPLICATION_MANAGER_PERMISSION_GROUP

Permission group for Application managers

DEVELOPER_PERMISSION_GROUP

Permission group for developers

The class nl.gx.webmanager.wcb.WCBConstants contains the identifiers for these groups.


The code example below shows how to add a collection of permissions to a permission group:
PermissionImpl[] allPermissions =

new PermissionImpl[] {

openPanelPermission, startStopPermission, installUpdatePermission};


panelDefinition.setPermissionsForPermissionGroup(WCBConstants.CASUAL_USER_P
ERMISSION_GROUP, noPermissions);

When the WCB is deployed, the permissions are automatically added to the permission group and
thus implicitly to all roles assigned to this permission group. This way even a WCB that defines
restricted access can become available to users automatically without manual intervention.

6. 5

Usi n g p er mi ss i o n s

To use permissions in your code you first have to define a dependency with the Authorization
service. The Authorization service provides a method checkAccess which takes the permissions
value as an input argument in order to check whether the current user has the specific
permission. Depending on the result of this call a particular piece of software will or will not be
invoked.
The code snippets below show how to define the dependency with the authorization service in the
activator followed by an authorization check.
ComponentDependencyImpl authDependency = new ComponentDependencyImpl();
authDependency.setServiceName(AuthorizationService.class.getName());
authDependency.setRequired(true);
panelDefinition.setDependencies(new ComponentDependency[]{authDependency};

if (authorizationService.checkAccess(CREATE_BOOK)) {
return createBook();
}
else {
LOG.warning(User does not have authorization to create books);
return null;
}
Note: CREATE_BOOK in the code snippet above refers to a public static final String
defined once. It is a good code practice to define such static String definitions in this manner.

e-mail info@gx.nl

http://www.gx.nl/

43/161

WCB DEVELOPMENT Tutorial

To check authorization from a JSP use the wmedit:checkPermission tag. Input arguments are
the permission value and ID of the website. The code snippet below shows an example how to
conditionally print the text user is authorized to generate PDF for the permission with value
pdf_generate:
<c:set var="website" value="${editcontext.website}" />
<wmedit:checkPermission value="pdf_generate" website="${website.id}">
User is authorized to generate PDF
</wmedit:checkPermission>
When using permissions, be sure that they conform to the WCB Development Guidelines (G052 and
G053 in particular).

6. 6

El em e nt p e rmi ssi o n

For element components, the element itself is created and deleted by the WebManager
framework rather than the controller contained by the element component itself. While create
and delete authorization checks would typically be programmed in a Java class of the WCB, for
element components this is done differently.
To define the permissions that grant the rights to create an instance of the element, invoke
setCreatePermissions on the element component definition. To define the permissions that
grant the rights to delete an instance of the element, invoke setDeletePermission.
For example for a BookElement it would look like this:
Permission[] createPermissions = new Permission[]{createPermission};
elemCompDef.setCreatePermissions(BookElement.class, createPermissions);
Permission[] deletePermissions = new Permission[]{deletePermission};
elemCompDef.setDeletePermissions(BookElement.class, deletePermissions);
When defining and using element permissions, be sure that they conform to the WCB Development
Guidelines (G052 and G053 in particular).

e-mail info@gx.nl

http://www.gx.nl/

44/161

WCB DEVELOPMENT Tutorial

THE WCB LIFE CYCLE

7. 1

WC B v e rs us OS Gi b u n dl e

A WCB is an OSGi bundle that WebManager is capable of dealing with. A WCB implements the OSGi
specification but also implements WebManager specific logic not defined by the OSGi
specification. It is important to understand the differences between a WCB and OSGi bundle :

A WCB is an OSGi bundle but an OSGi bundle not necessarily a WCB. A WCB is a specific
type of OSGi bundle.

An OSGi bundle is unaware of the concept of components, unlike a WCB.

A WCB exists only within the active state of the OSGi bundle. In other words, the API
that makes the OSGi bundle a WCB can only be accessed as long as the OSGi bundle is in
an active state.

7. 2

7.2.1

Li f e cy cl e o f a n O S Gi b u n dl e

OSGi bundle states

An OSGi bundle may be in one of the following states:


Property

Purpose

Installed

The bundle has been successfully installed.

Resolved

All Java classes that the bundle needs are available. This state indicates
that the bundle is either ready to be started or has stopped.

Starting

The bundle is being started, the BundleActivator.start method has


been called, but the start method has not yet returned.

Active

The bundle has successfully started and is running.

Stopping

The bundle is being stopped. The BundleActivator.stop method has


been called but the stop method has not yet returned.

Uninstalled

7.2.2

The bundle has been uninstalled. It cannot move into any other state.

OSGi bundle lifecycle

The lifecycle of an OSGi bundle is depicted in the figure below. The lifecycle starts with the
installation of the bundle. If all Java classes that the bundle depends on are available, the OSGi
bundle proceeds to the Resolved state. From there the bundle can be started or uninstalled.
When started, the bundle becomes active unless some runtime error occurs during the starting
of the bundle. If that happens, the bundle remains resolved. An active bundle can be stopped in
which case the bundle proceeds to the resolved state. When a resolved bundle is uninstalled it
proceeds to the uninstalled state and stays there.

e-mail info@gx.nl

http://www.gx.nl/

45/161

WCB DEVELOPMENT Tutorial

Figure 7. OSGi bundle lifecycle

7. 3

Li f e cy cl e o f a W C B

As stated previously, a WCB lives only within the active state of the OSGi bundle. Therefore the
API that makes an OSGi bundle a WCB can only be accessed during the active state of the OSGi
bundle. The API methods of a WCB that affect the lifecycle of the WCB are available on a
component bundle, which is only available when the bundle is active. These methods are:
Lifecycle

Purpose

Start()

Registers services that each component within the bundle exposes if


all required service dependencies are available. For each component
at least a component service is registered.

Stop()

Stops all services registered by the component.

Update()

Updates a WCB to a new version.

Uninstall()

Removes all content that was created during the installation of the
component and uninstalls the WCB.

Uninstall(doPurge)

Removes all content that was created during and after the
installation of the component and uninstalls the WCB. Notes:

A WCB must be in an active state before you can uninstall it.

Purge()

If a WCB has dependencies with another WCB, you must first


uninstall the dependent WCB.
Removes all content that was created during and after the
installation of the component. Note: a WCB must be in the active
state before you can purge it.

e-mail info@gx.nl

http://www.gx.nl/

46/161

WCB DEVELOPMENT Tutorial

The lifecycle of a WCB is depicted below.

Figure 8. WCB lifecycle.

e-mail info@gx.nl

http://www.gx.nl/

47/161

WCB DEVELOPMENT Tutorial

7.3.1

Bundle Manager Workflow

The workflow for each of the Bundle Manager methods is described in this section.
installBundle()
1.

installBundle() is invoked.

2.

The validity of the WCB is checked. If the platform version is not within the WCBs
platform version range (if specified), the install stops. If the platform is valid, the OSGi
bundleContext.installBundle method is invoked.

3.

The bundle is installed.

update()
1.

update() is invoked.

2.

A check is done to see whether the version number of the WCB or bundle has been
incremented and is higher than the currently installed version. If the version number is
not correctly incremented, the update stops. If the version number is correctly
incremented,

the

platform

validity

is

checked.

Note:

If

the

setting

enable_wcb_development_mode in WebManager Setup (/web/setup) is set to false,


this check is skipped.
3.

If the platform version is not valid, the update stops. If the platform is valid, the OSGi
method bundle.update is invoked

start()
1.

start() is invoked.

2.

start() invokes the OSGi bundle.start method.

3.

The Dependency Manager invokes the ComponentBundleImpl.start() method.

4.

A check is done to determine whether the WCB is being started for the very first time. If
it is the very first time the WCB has been started, the ComponentManager method
installBundle is invoked. Proceed to step 6.

5.

If the WCB is not being started for the very first time, a check is done to see whether the
WCB

has

been

modified

(updated).

If

the

WCB

has

been

modified,

the

ComponentManager method updateBundle is invoked.


6.

A check is done to see whether the service dependencies for the bundle are available.

7.

The services for the bundle are registered with the OSGi.

stop()
1.

stop() is invoked.

2.

The OSGi method bundle.stop is invoked.

3.

The bundles services are unregistered from the OSGi.

e-mail info@gx.nl

http://www.gx.nl/

48/161

WCB DEVELOPMENT Tutorial

uninstall()
1.

uninstall() is invoked.

2.

Should the WCB be purged? If yes, the ComponentManager.purgeBundle method is


invoked.

3.

If the WCB is not purged, the ComponentManager.uninstallBundle method is


invoked.

4.

The OSGi method bundle.uninstall is invoked.

purge()
1.

purge() is invoked.

2.

The ComponentManager.purgeBundle method is invoked.

7.3.2

Shutting down WCBs in a clustered environment

Beginning with GX WebManager version 9.14, it is possible to have more than one master server
running in a clustered environment. In order to correctly manag e the stopping of WCBs in a multimaster envionrment, a new lifecycle method has been added that provides you information about
the context of the lifecycle transition (stopping) of a WCB. The context can be an explicit
stop/uninstall/purge of the WCB or this can be the shutdown of the cluster node execution
environment of the WCB. With this context information the WCB developer can determine what
logic to execute within this context (remove global sources, etc.)
A developer does not have to stop a WCB on a single or clusterwide node because the WCB is
already stopping (only on a single node because of OSGi/Felix framework shutdown or
clusterwide) and this lifecycle state transition cannot be interrupted or controlled until the WCB
has stopped.
isShuttingDown()
Indicates whether the Felix framework is shutting down. Returns TRUE if the Felix
framework is shutting down and FALSE if not.

e-mail info@gx.nl

http://www.gx.nl/

49/161

WCB DEVELOPMENT Tutorial

7.3.3

WCB Lifecycle Methods

The WCB lifecycle methods make it possible for a WCB to respond to OSGi bundle.install,
bundle.update, bundle.start, bundle.stop and bundle.uninstall methods. These
methods are invoked within the WCB after the OSGi method is executed. This gives you extra
flexibility to perform other tasks in response to a change in a WCBs lifecycle.
The WCB lifecycle methods are:
Lifecycle

Purpose

onStart()

Allows a component to attach logic to a start event. For example:


public void onStart() {
LOG.log(Level.INFO, onStart() invoked!");
}
Dependencies: onStart() is invoked after onInit() and before
onInstall() and onUpdate(), therefore onStart() should not depend on
logic contained within onInstall() and onUpdate():
onInit()
onStart()
onInstall()
----------onInit()
onStart()
onUpdate()

onInstall()

Allows a component to attach logic to an install event. For ex ample:


public void onInstall() {
LOG.log(Level.INFO, onInstall() invoked!");
}
Dependencies: onInstall() is invoked after onInit() and onStart():
onInit()
onStart()

onStop()

onInstall()
Allows a component to attach logic to a stop event. For example:
public void onStop() {
LOG.log(Level.INFO, onStop() invoked!");
}
Dependencies: onStop() is invoked before onDestroy(), therefore it
should not depend on logic contained within onDestroy():
onStop()
onDestroy()

e-mail info@gx.nl

http://www.gx.nl/

50/161

WCB DEVELOPMENT Tutorial

onUpdate()

Allows a component to attach logic to an update event. For example:


public void onUpdate() {
LOG.log(Level.INFO, onUpdate() invoked!");
}
Dependencies: onUpdate () is invoked after onInit() and onStart():

onUninstall()

onInit()
onStart()
onUpdate()
Allows a component to attach logic to an uninstall event. Note: The WCB
containing the component must be in an ACTIVE state in order for the
onUninstall() method to be invoked. For example:
public void onUninstall() {
LOG.log(Level.INFO, onUninstall() invoked!");
}
Dependencies: onUninstall() is invoked before onStop() and
onDestroy(), therefore it should not depend on logic contained within
onStop() and onDestroy():

onPurge()

onUninstall()
onStop()
onDestroy()
Allows a component to attach logic to a purge event. Note: The WCB
containing the component must be in an ACTIVE state in order for the
onPurge() method to be invoked. For example:
public void onPurge() {
LOG.log(Level.INFO, onPurge() invoked!");

onInit()

}
Dependencies: onPurge() has no dependencies.
Allows a component to attach logic to an init event. For example:
public void onInit() {
LOG.log(Level.INFO, onInit() invoked!");

onDestroy()

}
Dependencies: onInit() is always invoked first, therefore it should not
depend on logic from any other method.
Allows a component to attach logic to a destroy event. For example:
public void onDestroy() {
LOG.log(Level.INFO, onDestroy() invoked!");
}
Dependencies: onDestroy() has no dependencies:
onStop()
onDestroy()

e-mail info@gx.nl

http://www.gx.nl/

51/161

WCB DEVELOPMENT Tutorial

7.3.4

WCB lifecycle in a dual master clustered environment

Keep in mind the following information when developing WCBs in a dual master clustered
environment.

onInstall() and onUninstall() are executed only on the cluster node where the WCB is
installed and therefore are only invoked once in a cluster.

onStart() and onStop() are executed on all cluster nodes and therefore will be invoked
multiple times in a cluster.

Use isShuttingDown() is to differentiate between logic that has to be executed on an


explicit (WCB Management Console etc.) or implicit (server shutdown) WCB stop lifecycle
transition.

e-mail info@gx.nl

http://www.gx.nl/

52/161

WCB DEVELOPMENT Tutorial

7. 4

WC B m a n ag e m e nt c o ns ol e

Another WebManager specific tool for WCB management is the WCB management console. This is a
WCB available from the Configure > WCB Management Console menu in the Edit environment of
WebManager. While the Felix shell TUI provides access to the OSGi bundles only (and is unaware
of WCB specific functionality), the WCB management console provides access to WCB specific
lifecycle methods, like purge. For more information on the WCB Management Console, refer to
the online help of this WCB. A screenshot of this console is displayed below.

Figure 9. Integrated WCB Management console

e-mail info@gx.nl

http://www.gx.nl/

53/161

WCB DEVELOPMENT Tutorial

7. 5

F el i x s h el l T UI

Felix, the OSGi implementation that WebManager 9 uses, comes with a shell tool which integrates
with Apache Tomcat in order to provide command line access to the OSGi bundles. The tool can
be installed by installing the org.apache.felix.shell.tui-0.9.0-r511518.jar the same
way you install other WCBs.
When the Felix shell TUI is running, you can enter commands in the Tomcat console. The most
commonly used command is ps which shows an overview of all installed OSGi bundles and their
state. An example of this console is displayed below.

Figure 10. Felix shell TUI in Apache Tomcat console


Other commands that can be used can be found on http://felix.apache.org/site/apache-felixusage-documentation.html.

e-mail info@gx.nl

http://www.gx.nl/

54/161

WCB DEVELOPMENT Tutorial

7.5.1

Managing WCBs from the Felix Shell TUI

In addition to the functionality available for managing WCBs from the WebManager WCB
Management Console, there are equivalent commands available in the Felix Shell TUI that you can
invoke directly from the command line.

You must first install the webmanager-felix-

shellcommands-9.4.0.jar using the WCB Management Console or by

copying it to the

<WebManager root>\work\deploy directory. You can find webmanager-felix-shellcommands9.4.0.jar in the <WebManager root>\webmanager\backend\WEB-INF\bundles directory.
To use the commands on a WCB in the Felix Shell TUI, you must determine the ID of a WCB by
issuing the ps command. After you issue the ps command, you will see a list of the WCBs as
shown in the figure above.
Command

Purpose

wmstart [id]

Registers services that each component within the bundle exposes if


all required service dependencies are available. For each component
at least a component service is registered

wmstop [id]

Stops all services registered by the component.

wmupdate [id]

Updates the WCB to a newer version. If a problem is encountered


during the update, the WCB is automatically rolled back to the existing
version. The following describes a typical WCB update scenario:
1.

The WCB developer creates a 1.1 version of a WCB and gives


it to a system administrator.

2.

The system administrator copies the updated WCB to the


machine running WebManager. If WebManager is running in a
clustered environment, the updated WCB is copied to the
machine running the master WebManager instance.

3.

The system administrator determines the id of the WCB in the


Felix Shell TUI.
The system administrator executes the wmupdate method,
specifying the id of the WCB to update and the absolute path
of the .jar file containing the updated WCB. For example:
wmupdate 59
file:/home/deploy/helloworld_1_1.jar
The WCB is updated to the new version and is set to the
active state. In a clustered environment, the WCB is also
automatically updated on all slave instances.

wmpurge [id]

Removes all content that was created during and after the installation
of the component.

e-mail info@gx.nl

http://www.gx.nl/

55/161

WCB DEVELOPMENT Tutorial

wmuninstall [id]

Removes all content that was created during the installation of the
component.
Notes:

A WCB must be in the active state before you can uninstall it.

If a WCB has dependencies with another WCB, you must first


uninstall the dependent WCB. Note: A WCB must be in the
active state before you can purge it.

7. 6

Se rvi c e d e p e n d en c i es

A WCB may depend on services exposed by the platform or other WCBs. Examples are using the
Entity Manager, Data Source Manager or Configuration Management service in a WCB. To use these
services a service dependency must be defined in the Activator to get the service injected into
the component of the WCB. In the dependency it can be defined if the availability of the service
is required or not. When a component defines a required dependency on a service that is not
available, the component will not be started at all. When the dependency is not required, the
component will be started but the service will not be injected.
As an example the code snippet below shows how to define a service dependency with the
Configuration Management service:
ComponentDependencyImpl serviceDependency = new ComponentDependencyImpl();
serviceDependency.setServiceName(ConfigurationManagement.class.getName());
serviceDependency.setRequired(true);
definition.setDependencies(new ComponentDependency[]{serviceDependency});

Note that the definition in the code snippet above is the Component definition of the component
that uses the service.
The exact classes in which the services are injected and the way they are injected depends on the
component that defines the dependency. There are two ways the service might be injected into
the components classes:

By Felix, accessing the field directly. Felix injects the service in the class marked as
implementation class (ComponentDefinitionImpl.setImplementationClassName) by
manipulating a class member of which the type is the same as the one defined by the
service dependency. Even if this field is private, Felix will still assign the service to this
private field.

By WebManager, using a setter for the field in the class marked as instance class in the
component

definition

(ComponentDefinitionImpl.setInstanceClassName).

WebManager will try to find a setter for the field in this class with the same type as the
service. If the setter exists, it will invoke that setter with the service.

e-mail info@gx.nl

http://www.gx.nl/

56/161

WCB DEVELOPMENT Tutorial

The table below shows an overview on the service injections for each component type.
Component

Service is injected in

Injection type

Element

SimpleElementComponent

Field access

Element

ElementBase

Setter method

Panel

SimplePanelComponent

Field access

Panel

PanelBase

Setter method

Media Item

SimpleMediaItemComponent

Field access

Media Item

MediaItemArticleVersionImpl

Setter method

Form

SimpleFormComponent

Field access

Form

FormHandlerBase

Setter method

Page metadata

SimpleMetaDataComponent

Field access

Page metadata

PageMetaDataBase

Setter method

Presentation

SimplePresentationComponent

Field access

Service

SimpleServiceComponent

Field access

Servlet

SimpleServletComponent

Field access

Note that using service dependencies as described here is a required guideline for level 1
certification, see guideline G023 of the WCB Development Guidelines document.

7. 7

Usi n g Se rvi c es i n sta ti c m et h o ds

Note that the service dependencies described in the previous section are always injected on
instance fields (non-static). In static methods however, a non-static field cannot be accessed and
so such a service dependency cannot be used. Static service injection is not supported and so
using services acquired from a service dependency cannot be used in static method.
Only for this particular use case, the service can be retrieved from the static framework instance.
ConfigurationManagement service = FrameworkFactory.getInstance()
.getFramework().getService(ConfigurationManagement.class.getName());

Note: This is only allowed for static methods or any other use case the service cannot be acquired
using a service dependency. In all other use cases this is a bad practice and guideline violation
(see G023 of the WCB Development Guidelines document).

e-mail info@gx.nl

http://www.gx.nl/

57/161

WCB DEVELOPMENT Tutorial

e-mail info@gx.nl

http://www.gx.nl/

58/161

WCB DEVELOPMENT Tutorial

DEPLOYING ARBITRARY RESOURCES

8. 1

I ntr o d u cti o n

A WCB may contain Java files, images, JSPs, etc. Some of the resource types are covered by the
component types that WebManager supports. For example, WebManager supports deploying JSPs
for the Website environment by the presentation component and deploying static files by all
component types.
This chapter describes the deployment of any resource that is not covered by a component type.
Examples are:

Deploying a custom tag library

Deploying filters

Deploying a custom web.xml

Deploying a custom library

Adding an arbitrary resource to your WCB which must be accessible from the WCB

8.1.1

Deploying custom JSP tags

A tag library is a collection of JSP tags you can import and use in a JSP. JSP tags are in fact
functions you can invoke from any JSP in the same way you define methods in a Java class and
import that class into another.
To deploy a custom tag library onto a normal web application, the following steps must be
performed:
1.

The tag library must be defined in the <jsp-config> section of the web.xml, for
example:
<taglib>
<taglib-uri>http://www.gx.nl/taglib/idg_functions</taglib-uri>
<taglib-location>/WEB-INF/prj/tld/gx_functions.tld</taglib-location>
</taglib>

2.

The taglib-location in the web.xml points to the location of the tld file which defines
the contents of that tag library. This tld may define a function that points to a
method in a particular Java class on the Application servers classpath, for example:
<function>
<name>getAllBooks</name>
<function-class>nl.gx.product.BookTag</function-class>
<function-signature>Book[] getAllBooks()</function-signature>
</function>

3.

The Java classes that implement the actual tags are compiled to a JAR and copied to
/WEB-INF/lib to ensure they are on the Application servers classpath.

e-mail info@gx.nl

http://www.gx.nl/

59/161

WCB DEVELOPMENT Tutorial

Since the web.xml must be changed and this cannot be done at runtime, it is not possible to add
a custom tag or tag library from a WCB, therefore to add a custom tag or tag library the steps
above must be performed. Note you must restart the application server to apply the changes.

8.1.2

Deploying custom JSP tag files

A custom JSP tag file is very similar to a custom JSP tag, the only difference is that the actual
function is not implemented in Java but in JSP. The extension of the custom tag file is by
convention .tag.
To deploy a custom tag file, perform the following steps:
1.

Create a /tags subdirectory within the /src/main/resources directory of your WCB.

2.

Create or copy the tag file to the newly created /tags directory.

Now when the WCB is deployed, the tag file is automatically available through the following path:
/WEB-INF/tags/[BUNDLE_ID]
Where

the

[BUNDLE_ID]

is

the

actual

bundle

id

of

the

WCB,

for

example

nl.gx.webmanager.mypresentationwcb.
To use the tag file, you must add the following line to your WCB:
<%@ taglib tagdir="/WEB-INF/tags/[BUNDLE_ID]" prefix="myprefix" %>
You can then call the tag as you would normally do using the prefix and tag filename.

8.1.3

Adding and accessing an arbitrary resource in a WCB

This section describes how to distribute and access an arbitrary resource within a WCB. For
example, suppose that you want to distribute along with a WCB a static .properties file
containing configuration properties used by some Java class within the WCB. Note: This example
describes static properties - If you want the properties to be configurable, you must use the
ConfigurationManagement service instead.
To distribute the resource with the WCB simply put it somewhere in /src/main/resources. It
can be stored in any subfolder. To access this resource from Java, simply retrieve the resource
from the classloader of that class by invoking getResourceAsStream on the class. For
example,

if

we

distributed

file

called

config.properties

/src/main/resources/conf/books we could access this resource from Book.java like this:

e-mail info@gx.nl

http://www.gx.nl/

60/161

in

WCB DEVELOPMENT Tutorial

Class<Book> clazz = Book.class;


InputStream propFile =
clazz.getResourceAsStream( "conf/books/config.properties" );
Properties props = new Properties();
props.load( propFile );

8.1.4

Deploying and accessing static files

By static files we mean files that are typically static within the context of a Web application
like images, JavaScript files, HTML files and stylesheets. Such static files can have two purposes:

To be used in the Edit environment. The file is only used to render the Edit environment
of the WCB.

To be used in the Website environment. The file is part of the Website presentation.

Deploying static files to the Edit environment


To

deploy

static

files

to

the

Edit

environment,

simple

copy

the

resources

to

/src/main/resources/static/backend. When the WCB is deployed, the files will be copied to


the web root directory of the static Web application in a folder named:/wm/b/wcb/<Component
bundle definition ID>.
Since this directory is not accessible from the Website environment , the file will not be accessible
from the Website environment.
To access static files from Java, the URL can be calculated by
public String getIcon() {
return "/wm/b/wcb/" + Activator.BUNDLE_DEFINITION_ID + "/icon.gif";
}
Alternatively, some API classes contain methods to retrieve the static backend directory:
CmsItemBase.getStaticResourceDir();
ElementComponentDefinition:getStaticBackendDir();

If the full path name to the file is needed, the pathname of the static web root directory can be
retrieved from the Configuration management service:
confManagement.getParameter("website_settings.www_root_directory");

To point to this resource from a JSP, the easiest way is to set the path into the reference data of
the controller. The controller always has access to the activator of the compon ent by which it is
contained. For example:

e-mail info@gx.nl

http://www.gx.nl/

61/161

WCB DEVELOPMENT Tutorial

public

Map<String,

Object>

referenceData(HttpServletRequest

req,

Object

command, Errors errors) throws Exception {


Map<String, Object> data = super.referenceData(req,
command, errors);
data.put("staticBackendDir", "/wm/b/wcb/" +
Activator.BUNDLE_DEFINITION_ID + "/");
return data;
}

All properties written to the reference data are available directly in the JSP, so to define the URL
to an icon called icon.gif, the following code snippet would display that icon in a JSP:
<img src=${staticBackendDir}icon.gif/>

Deploying static files to the Website environment


To deploy static files to the Website environment, the presentation component methodology as
described

in

3.2

should

be

used.

For

this

reason

resources

should

be

placed

in

/src/main/resources/presentationtype/static - The files will be copied to /static,


inside the web root directory of the static Web application when the WCB is deployed.
Note that in order to make this work the WCB must define a presentation component therefore to
develop a WCB containing an element component also containing such static files , a presentation
component must be defined in the activator of that WCB also.
To access the static files from Java, the method Website.getStaticFilesUrl can be invoked
which returns the web root of the static web application. Note that /static must be appended
to the filename since the method returns only the web root.
To access the static files from a JSP, the same method can easily be used since the website is
available in the presentationcontext which is available in any JSP. For example:
<c:set var="staticFilesUrl"
value="${presentationcontext.website.staticFilesUrl}" />
<img src=${staticFilesUrl}/static/project/icon.gif/>
There is an important difference between static files or JSPs for the Website environment
distributed with the WCB and those distributed with a separate presentation component. Static
files and JSPs distributed with a WCB that contain only resources for that WCB are intended for
demonstration purposes only. They provide a default layout which ensures that the WCB has a
presentation on any website onto which it is deployed.
However, because each Website will use a different presentation, a WCB should never contain the
actual presentation itself. A Website usually has one active presentation component which
contains all JSPs and static files used to render the complete Website environment which
overwrites all presentations and static files in a WCB (if it contains these resources).

e-mail info@gx.nl

http://www.gx.nl/

62/161

WCB DEVELOPMENT Tutorial

To ensure that the presentation WCB overwrites all presentations and static files from the
individual WCBs, it must be deployed after deploying all other WCBs.

8.1.5

Deploying custom libraries

Using Java libraries which are available in the Maven repository is quite simple. Defining the
artifact dependency in the pom.xml of the WCB will automatically bundle the JAR with the WCB
and class loading issues will be handled by the framework automatically upon using the library at
runtime.
It becomes more complicated when a Java library is used that is not contained by the Maven
repository. The best approach in this case is to simply upload the JAR file to the (local or remote)
Maven repository.
The following steps must be performed to use a custom Java library in a WCB:

Run a Maven command to upload the JAR file and to create a new artifact for it in the
Maven repository. This command will be explained below.

Define the artifact dependency in the pom.xml. An example is given below.

Compile the WCB, open its generated MANIFEST.MF and:


o

If the library should also be available to other WCBs:

Copy the value of Import-Package from the Manifest into a new


attribute

importPackage

in

the

build/plugins/plugin/

configuration/osgiManifest section of the pom.xml.

Copy the value of Import-Package from the Manifest into a new


attribute

exportPackage

in

the

build/plugins/plugin/

configuration/osgiManifest section of the pom.xml.


o

If case the library should only be available to this WCB:

Copy the value of Import-Package from the Manifest into a new


attribute

importPackage

in

the

build/plugins/plugin/

configuration/osgiManifest section of the pom.xml. For this


situation the Import-Package value contains too much packages:
remove all packages that are contained by the Java library.
In both situations you follow the important rule: Import what you export.
The maven command to upload a JAR file and create a new artifact for it in the Maven repository
has the following syntax:
mvn install:install-file -Dfile=[filename] -DgroupId=[groupId]
-DartifactId=[artifactId] -Dpackaging=jar -Dversion=[version]
-s [path to settings.xml]
Where:

filename is the name of the JAR file to upload

groupId is the group Id that the new Maven artifact should have

articfactId is the artifact Id that the new Maven artifact should have

version is the version number that the new Maven artifact should have

e-mail info@gx.nl

http://www.gx.nl/

63/161

WCB DEVELOPMENT Tutorial

As an example we describe how to use an external library called konakart.jar which we will
not share with other WCBs:
1.

Run the Maven command to upload the JAR file to the Maven repository:
mvn install:install-file -Dfile=konakart.jar -DgroupId=com.konakart
-DartifactId=konakart -Dpackaging=jar -Dversion=1.0.0 -s settings.xml
The library is now available in the Maven repository as an artifact with group Id
com.konakart and the artifact Id konakart.

2.

Define a dependency for this new artifact with scope provided in the pom.xml:
<dependency>
<groupId>com.konakart</groupId>
<artifactId>konakart</artifactId>
<scope>provided</scope>
<version>1.0.0</version>
</dependency>

3.

Build the WCB.

4.

Open the MANIFEST.MF file inside the generated JAR file and copy the value of the ImportPackage property to the clipboard.

5.

Open

the

pom.xml

and

create

new

attribute

importPackage

in

the

build/plugins/plugin/configuration/osgiManifest section.
6.

Paste the value from the clipboard into the importPackage attribute. Remove all packages
exported by the konakart library (this can be looked up by opening the konakart.jar).

e-mail info@gx.nl

http://www.gx.nl/

64/161

WCB DEVELOPMENT Tutorial

MIGRATION

9. 1

I ntr o d u cti o n

Migration is always a difficult issue to confront. The introduction of WCBs significantly eases
migration issues because of the tight coupling between the software version of a WCB and the
data model it uses. The WCB itself is responsible for implementing proper data model migration
and WebManager 9 offers an easy API to implement this.

9. 2

V er si o n n um b er

Each WCB has a version number that consists of three numbers. The version number does have a
meaning and it is very important to use it properly since it tells users of the WCB the kind of
changes they can expect between two separate versions of the WCB. The three parts of the
version number have the following meaning:

Major version
A major software update of the WCB. Such an update may change exposed APIs which
may cause incompatibility with WCBs that depend on it.

Minor version
A backwards compatible WCB update. The exposed API has not been changed and WCBs
that depend on this WCB will thereforebe compatible with the updated version. The API
can be extended with additional functions.

Micro version
Update without any change to the interface or published services.

If you properly use this version numbering system, other developers that may use services or
classes exposed by this WCB will know in advance whether should expect problems when
upgrading to a later version of the WCB.
The version number of the WCB should be defined in the pom.xml of the WCB. For example to
define the version to be 1.0.2:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="" ...>
...
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>1.0.2</version>
...
</project>

When defining version numbers for your WCB, be sure that they conform to the WCB Development
Guidelines (G017 and G019 in particular).

e-mail info@gx.nl

http://www.gx.nl/

65/161

WCB DEVELOPMENT Tutorial

9. 3

WC B u p d at es

When updating a WCB, the version number of the WCB must always be updated. Depending on the
type of change the major, minor or micro version number must be updated:

Major update: Property definitions of node types may be changed or removed, methods
of exposed interfaces and classes may be changed in signature.

Minor update: Additional property definitions may be added to node types but existing
property definitions are not be changed or removed. Additional methods may be added
to exposed interfaces and classes but the signature of existing me thods are not be
changed.

Micro update: Node types are unaffected. The implementation of methods of exposed
interfaces and classes may be changed, but no new methods have been added and the
signature of existing methods are not changed.

Note that proper versioning of your WCB is a required guideline, see G017 and G019 in the WCB
Development Guidelines document.

9. 4

C h a n g es i n t he d a t a m o de l

The version number plays an important role in data model migration. A WCB version is ti ed to a
particular data model and when the data model is updated the WCBs version should also change.
Using the version number it is possible to implement the migration logic that is needed to upgrade
content managed by the entity manager created from an x.y.z version of the WCB to x.y.z+1.
To implement the migration of the data model from x.y.z to x.y.z+1 you should implement
the upgradeContent method in the class defining the data model. Arguments passed to this
method are the fromVersion, which indicates the current version of the data model and a
collection of nodes on which the migration must be performed. This method is invoked by the
framework automatically if a new version of the WCB is installed. Note that a WCB doesnt
necessarily have to define this method - its only invoked, using reflection, if it exists.
The implementation of upgradeContent is up to the developer. It may be implemented in a way
that it is capable of upgrading from any version to the latest version or that it requires previous
updates first. In the latter case, the method should throw an IncompatibeUpdateException
when the implementation of upgradeContent is not capable of migrating from the fromVersion
to this version. The next section clarifies the migration implementation further with an example.
Notes:

The upgradeContent method will only be invoked on nodes managed by the Entity
Manager!

The upgradeContent method is static. For using the Entity Manager (or any other
service) in a static method, see section 7.7.

Proper handling of content migration is a required guideline, see G020 in the WCB
Development Guidelines document.

e-mail info@gx.nl

http://www.gx.nl/

66/161

WCB DEVELOPMENT Tutorial

9.4.1

Explicitly setting the Platform Dependency

By default, the minimum and maximum version of WebManager that a WCB can run on is
calculated as follows: If the version of WebManager for which you build the WCB is x.y.z, then the
minimum version of WebManager on which the WCB can run is x.y and the maximum version is
(x+1).0. It is also possible to explicity set the minimum and maximum versions of WebManager on
which a WCB can run in the POM file using the setting properties webmanager.wcb-min-version
and webmanager.wcb-max-version. For example:
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>nl.gx.webmanager.wcbs</groupId>
<artifactId>webmanager-wcbs</artifactId>
<version>9.4.0-SNAPSHOT</version>
</parent>
<packaging>osgi-bundle</packaging>
<artifactId>wmpjcrbrowser</artifactId>
<name>JCR Browser</name>
<properties>
<webmanager.wcb.max-version>10.4</webmanager.wcb.max-version>
<webmanager.wcb.min-version>9.3</webmanager.wcb.min-version>
</properties>
...
</project>
If webmanager.wcb.min-version and webmanager.wcb.max version are not specified, the default
versions as described above are calculated and used.

9.4.2

Example

In this section we will provide an example of a Keyword WCB that changes its node type
definition (data model) over time. The following updates to the node type definition are
performed:

The 1.0 version of the WCB contains a keyword property

The 1.1 version adds an additional secondaryKeyword property

The 1.2 version adds an additional keywords property is added. The properties keyword
and secondaryKeyword are deprecated

The 2.0 version removes the keyword and secondaryKeyword properties

e-mail info@gx.nl

http://www.gx.nl/

67/161

WCB DEVELOPMENT Tutorial

Note: The fromVersion argument of the upgradeContent method is the version of the WCB
currently installed. This example uses an approach that requires previous version updates, which
means that each version must be installed separately. So to update from 1.0 to 2.0, the system
administrator first has to deploy 1.1, then 1.2 and finally 2.0. The code snippets below provide
examples of how the code looks like for the several versions.
Version 1.0
@Property
public String getKeyword(){...}
public void setKeyword(String keyword) {...}
Version 1.1
@Property
public String getKeyword(){...}
public void setKeyword(String keyword) {...}
@Property
public String getSecondaryKeyword(){...}
public void setSecondaryKeyword(String keyword) {...}

Version 1.2
@Property
@Deprecated
public String getKeyword(){
return getKeywords()[0];
}
public void setKeyword(String keyword) {
setKeywords(new String[]{keyword, });
}
@Property
@Deprecated
public String getSecondaryKeyword(){
return getKeywords()[1];
}
public void setSecondaryKeyword(String keyword) {
setKeywords(new String[]{, keyword});
}
@Property
public String[] getKeywords() {...}
public void setKeywords(String[] keywords) {...}
public static void upgradeContent(String fromVersion, Node[] nodes) throws
IncompatibeUpdateException {
if (fromVersion.compareTo(1.1) != 0) {
String msg = This update requires 1.1. ;
msg += Current WCB version is + fromVersion;
throw new IncompatibeUpdateException(msg);
} else {
updateKeywords(nodes);
}
}

e-mail info@gx.nl

http://www.gx.nl/

68/161

WCB DEVELOPMENT Tutorial

(continued from the previous page)


public static void updateKeywords(Node[] nodes) {
for (int i=0; i<nodes.length;i++) {
Version 2.0 String pKeyword = nodes[i].getProperty("keyword").getString();
String sKeyword =
nodes[i].getProperty("secondaryKeyword").getString();
String[] keywords = new String[]{pKeyword, sKeyword};
nodes[i].setProperty(keywords, keywords);
}
nodes[0].getSession().save();
}

Version 2.0
@Property
public String[] getKeywords() {...}
public void setKeywords(String[] keywords) {...}
public static void upgradeContent(String fromVersion, Node[] nodes) throws
IncompatibeUpdateException {
if (fromVersion.compareTo(1.2) != 0 {
String msg = This update requires 1.2. ;
msg += Current WCB version is + fromVersion;
throw new IncompatibeUpdateException(msg);
}
}

e-mail info@gx.nl

http://www.gx.nl/

69/161

WCB DEVELOPMENT Tutorial

e-mail info@gx.nl

http://www.gx.nl/

70/161

WCB DEVELOPMENT Tutorial

10 CONFIGURATION MANAGE MENT


GX WebManager provides a Configuration Management service. This service is used to store
environment specific settings (e.g. hostname). The settings of the Configuration Management
service can be edited through the /web/setup interface. This section explains how to add a
property to the Configuration Management service and how to retrieve the contents of the
properties in the Java code.
When creating and reading configuration entries, be sure that you conform to the WCB
Development Guidelines (G046 and G152 in particular). When you follow the steps explained in
this chapter, your WCB will conform to these guidelines.

1 0. 1 A d di n g pr o p er ti es to t h e C o nf ig u r ati o n M a n a g em e nt
As of GX WebManager 9.1.4 it is possible to add properties to the Configuration Management
through a WCB. This section explains the steps you need to take to create two new extra
properties. One extra property is an array (a list of server names). The second extra property is a
portnumber.

10.1.1 Create config_metatype.xml


Create

new

file

in

the

root

of

the

resources

folder;

the

new

file

is

called

config_metatype.xml. This file defines the properties that can be used and groups them into a
set. The contents of config_metatype.xml should look like this to create support for the two
new properties:
<?xml version="1.0" encoding="UTF-8"?>
<metatype:MetaData
xmlns:metatype="http://www.osgi.org/xmlns/metatype/v1.0.0">
<OCD name="My Configuration Set" id="my_config_set">
<AD name="Server names" id="my_config_entry_server_names"
type="String" cardinality="-1" />
<AD name="Portnumber" id="my_config_entry_portnumber"
type="String" cardinality="1" />
</OCD>
<Designate pid="my_config_set">
<Object ocdref="my_config_set" />
</Designate>
</metatype:MetaData>

The cardinality can be set for each property. Cardinality -1 means that the property can contain
more than one value. Cardinality 1 means that the property can only hold one value.
Be sure that the id of the configuration set ( my_config_set ) is prefixed by the WCB id, as
described in guideline G152 of the WCB Development Guidelines document.

e-mail info@gx.nl

http://www.gx.nl/

71/161

WCB DEVELOPMENT Tutorial

10.1.2 Create config.xml


While the file config_metatype.xml can be compared to an interface, the file config.xml
can be compared to an implementation of this interface. The config.xml file should also be
placed in the root of the resources folder. The config.xml file contains default values for the
properties as defined in config_metatype.xml. The config.xml for the above example looks
like this:
<configurations>
<config name="default" definition="my_config_set" >
<entry name="my_config_entry_server_names"
value="www.domain1.com,www.domain2.com" />
<entry name="my_config_entry_portnumber" value="8080" />
</config>
</configurations>
The values for a property that can hold multiple values should be entered in a comma-separated
fashion.

10.1.3 Register the configuration set


By just creating the two XML files the Configuration Management service still doesnt know about
the new properties. There are two methods in the Configuration Management service that can be
called that will process the two XML files and register the new properties:

configurationManagement.addConfigurationSetDefinition(
getClass().getResource("/config_metatype.xml"));
configurationManagement.parseAndAddConfigurationSets(
getClass().getResource("/config.xml"), false);

The boolean used with the parseAndAddConfigurationSets method decides whether the values
in the config.xml should overwrite the present values. If set to false, values in the
Configuration Management service will not be overwritten by the values from the config.xml. If
set to true, the values from the config.xml will replace the values from the Configuration
Management service.
The two methods as described above should only be called when the WCB is loaded. The best
place for these lines is in the start method of a configuration service. Remember that this
configuration set is created by the WCB and should thus be removed automatically when the WCB
is purged. So the purge method should delete the configuration set.
In the next section a complete example will be shown, based on above settings.

e-mail info@gx.nl

http://www.gx.nl/

72/161

WCB DEVELOPMENT Tutorial

1 0. 2 Usi n g c ust o m c o n f ig ur at i on p r o p erti e s


This section describes in detail how to use properties that were added to the Configuration
Management service. The next steps have to be taken to make the properties available:

Create a configuration service that has access to the Configuration Management servi ce;

The component that needs access to the configuration properties depends on the
configuration service.

10.2.1 Define a service component in the Activator


In the Activator, a service component is defined. The service component will:

Depend on the Configuration Management service;

Register the configuration sets to the Configuration Management service;

Offer getters to access the properties.

The definition of this service, the HelloWorld Configuration service (HWC service), in the
Activator looks like this:
ServiceComponentDefinitionImpl serviceDef =
new ServiceComponentDefinitionImpl( false );
serviceDef.setId("nl.gx.helloworld.configurationservice");
serviceDef.setName("HelloWorld configuration service.");
serviceDef.setDescription( "Service which contains the configuration
for this WCB." );
serviceDef.setTypeId( ServiceComponentType.class.getName() );
serviceDef.setProperties( new Hashtable<String, String>() );
serviceDef.setImplementationClassName(
HelloWorldConfigurationServiceImpl.class.getName() );
serviceDef.setInterfaceClassNames( new String[]
{HelloWorldConfigurationService.class.getName()} );
serviceDef.setWrapperClassNames( new String[] {} );
ComponentDependencyImpl serviceDependency = new ComponentDependencyImpl();
serviceDependency.setServiceName(ConfigurationManagement.class.getName());
serviceDependency.setRequired(true);
serviceDef.setDependencies(new ComponentDependency[] { serviceDependency });

e-mail info@gx.nl

http://www.gx.nl/

73/161

WCB DEVELOPMENT Tutorial

10.2.2 Define an interface for the HWC service


The interface for the HWC (HelloWorld Configuration) service component is very small. The
getters in the interface are based on the XML examples of the previous sections. A basic interface
looks like this:
public interface HelloWorldConfigurationService {
public String[] getServerNames();
public String getPortnumber();
}

10.2.3 Create an implementation of the interface


The implementation of the interface has a dependency on the Configuration Management service
so it only needs a private member of the ConfigurationManagement type to have access to the
service. The implementation of the HelloWorldConfigurationService interface looks like
this:
public final class HelloWorldConfigurationServiceImpl extends
SimpleServiceComponent implements HelloWorldConfigurationService {
private final static Logger LOG =
Logger.getLogger(HelloWorldConfigurationServiceImpl.class.getName());
private ConfigurationManagement myConfigService;
/**
* Callback method for the dependency manager, called when the
* bundle is started. This start method registers the properties
* to the ConfigurationManagement service.
*/
public void onStart() {
// Note: when the configuration set already exists, its
// existing properties will not be affected.
myConfigService.addConfigurationSetDefinition(
getClass().getResource("/config_metatype.xml"));
myConfigService.parseAndAddConfigurationSets(
getClass().getResource("/config.xml"), false);
}

(continues on next page)

e-mail info@gx.nl

http://www.gx.nl/

74/161

WCB DEVELOPMENT Tutorial

(continued from the previous page)


/**
* Callback method for the dependency manager, called when the
* bundle is purged. This purge method removes the configuration set
* created in the start method.
*/
public boolean onPurge() {
myConfigService.removeConfigurationSet("my_config_set");
return true;
}
public String getPortnumber() {
try {
return myConfigService.getParameter(
"my_config_set.my_config_entry_portnumber");
} catch (ConfigurationManagementException e) {
LOG.log(Level.WARNING, "Could not find parameter
'my_config_set.my_config_entry_portnumber' from

the

ConfigurationManagement service", e);


return null;
}
}
public String[] getServerNames() {
try {
return myConfigService.getParameters(
my_config_set.my_config_entry_server_names");
} catch (ConfigurationManagementException e) {
LOG.log(Level.WARNING, "Could not find parameter
'my_config_set.my_config_entry_server_names' from the
ConfigurationManagement service", e);
return null;
}
}
}
}

e-mail info@gx.nl

http://www.gx.nl/

75/161

WCB DEVELOPMENT Tutorial

10.2.4 Add a dependency to the configuration service


The components that want access to the properties need to create a dependency on the HWC
service that was created in the previous sections. If, for example, an element needs access to the
properties, the dependency would look like this:
ComponentDependencyImpl servDep = new ComponentDependencyImpl();
servDep.setServiceName(HelloWorldConfigurationService.class.getName());
servDep.setRequired(true);
elementDefinition.setDependencies(new ComponentDependency[] { servDep });

Add this dependency to the definition of a component in the Activator. For more detailed
information concerning service dependencies, see section 0.

10.2.5 Use the dependency in the implementation


The dependency to the HWC service can now be used in the element implementation:
public class CustomElementImpl extends ElementBase
implements CustomElement {
private final static Logger LOG =
Logger.getLogger(Activator.class.getName());
private HelloWorldConfigurationService hwcs;
public void setHelloWorldConfigurationService(
HelloWorldConfigurationService hwcs) {
this.hwcs = hwcs;
String ServerNames = "";
for (String ServerName : hwcs.getServerNames()) {
ServerNames += ServerName + ";";
}
LOG.log(Level.INFO,
"Values of the custom properties are:\n Server names:"
+ ServerNames + "\n

Portnumber

: "

+ hwcs.getPortnumber());
}
}

The above code will result in a log message each time an instance of the element is being placed
or refreshed on a page:
INFO: Values of the custom properties are:
Server names: www.domain1.com;www.domain2.com;
Portnumber

: 8080

e-mail info@gx.nl

http://www.gx.nl/

76/161

WCB DEVELOPMENT Tutorial

10.2.6 Changing the values of the properties


Once the properties are present in the Configuration Management service, the value of the
properties can be changed through the /web/setup tool. Log onto the /web/setup page and
navigate to the General configuration tab. Your configuration set should be present between the
default configuration sets (the sets are alphabetically ordered). The set o f the example of this
section looks like this:

Figure 11. Example of a custom configuration set

e-mail info@gx.nl

http://www.gx.nl/

77/161

WCB DEVELOPMENT Tutorial

e-mail info@gx.nl

http://www.gx.nl/

78/161

WCB DEVELOPMENT Tutorial

11 ONLINE HELP
Each WCB may provide its own Online help. This Online help can be written in either XHTML
format or a JSP XML document that generates XHTML. All Online help files provided by WCBs are
collected and merged to a single help application by the Online help WCB. As a result each
element or panel in a WCB that provides Online help is able to show a context sensitive help
button. When the Help button is clicked, the Online help WCB will open the help panel and
navigate to the relevant help topic in the merged help file.
Note that providing proper Online Help in your WCB is required for certain component types, as
described in G108 and G147 of the WCB Development Guidelines document.

1 1. 1 En a bl i n g O nl i n e h e lp i n a WC B
The Online help WCB is a WCB that is part of the WebManager platform and is responsible for
collecting and displaying the Online help files provided by all WCBs that provide Online hel p. The
Online help WCB consists of the Online help panel, a OnlineHelpProvider service interface
and a Online help listener service. The listener service listens to all services being registered that
implement the OnlineHelpProvider service interface. If a WCB containing a service that
implements this interface becomes available, the listener will receive a notification and retrieve
the Online help file from that WCB.
To

enable

Online

help

in

WCB

service

must be

created

that

implements

the

OnlineHelpProvider service interface. Define a service component in the Activator of the WCB
as illustrated below.
ServiceComponentDefinitionImpl definition = new
ServiceComponentDefinitionImpl(false);
definition.setId(WCB_HELPPROVIDER_ID);
definition.setName(WCB_HELPPROVIDER_NAME);
definition.setDescription("Implements the " + WCB_BUNDLE_ID + " online
help provider service");
definition.setTypeId(ServiceComponentType.class.getName());
definition.setProperties(new Hashtable<String, String>());
definition.setImplementationClassName(OnlineHelpProviderImpl.class.getName
());
definition.setInterfaceClassNames(new
String[]{OnlineHelpProvider.class.getName()});
definition.setDependencies(new ComponentDependency[0] );

The WCB must provide the implementation class of the OnlineHelpProvider interface as well.
Note that this implementation must also implement ServiceComponent. The easiest way to do
so is by extending SimpleServiceComponent, as illustrated below.
public class OnlineHelpProviderImpl extends SimpleServiceComponent
implements OnlineHelpProvider {
}

Note: Since the OnlineHelpProvider interface does not define any methods, it is not necessary
to define any methods in the implementation. The implementation of the service itself can just
be empty.

e-mail info@gx.nl

http://www.gx.nl/

79/161

WCB DEVELOPMENT Tutorial

1 1. 2 Pr ovi di n g t h e O nl i n e h el p fil es
The Online help files should be written in XHTML format or in an XML JSP Document format that
generates XHTML. Badly formed XHTML will cause the help to be undisplayable. You should always
validate the XHTML using a validator such as http://validator.w3.org/. A stylesheet for rendering
the Online help file is contained in the Online help WCB and cannot be customized. Online help
files are written in a particular locale. The Online help framework supports providing Online help
in multiple locales.
The Online help files of a WCB must be located in the /help directory of the WCBs resources.
Typically the help file sources will be located in /src/main/resources/help. This is a fixed
location and cannot be configured.
Within that help directory a subdirectory must be created for each supported locale using the
Java i8N notation. Some examples of this notation are:
Locale identifier

Description

en_US

English (U.S.)

en_GB

English (United Kingdom)

fr_FR

French (France)

de_DE

German (Germany)

nl_NL

Dutch (Netherlands)

es_ES

Spanish (Spain)

Within that subdirectory either a file called index.html or index.jsp should be located. If both
files exist, index.jsp will be used. This file represents the Online help file in that particular
locale. The help directory may contain all kinds of resources used in the help file like images or
zip files. These can be referred to by the XHTML or XML JSP Document. Note however that paths
must be relative to the location of the XHTML or XML JSP Document file.

For example, this is how the directory


structure of the help directory might look:

Figure 12. Help directory structure

e-mail info@gx.nl

http://www.gx.nl/

80/161

WCB DEVELOPMENT Tutorial

Note: A WCB contains only one single help file for each locale. It is not possible to provide
multiple separate help files for the individual components contained by the WCB.

1 1. 3 Wri ti n g t h e On l i ne h el p fil es
As stated above, Online help files should be written in XHTML or a XML JSP Document that
generates XHTML. The Online help WCB generates a Table of contents from all Online help files
provided by all Online help enabled WCBs. The Table of contents offers the end -user a way to
navigate through the available help topics.
This Table of contents is generated by parsing the XHTML or XML JSP Document file. Three
special tags contained by that XHTML make up the Table of contents:

The <title> attribute in the <head> becomes the first level navigation

The <h1> tag in the <body> becomes the second level navigation

The <h2> tag in the <body> becomes the third level navigation

To properly enable navigation through the table of contents these tags must be provided with
anchors. Within the title attribute of the header the id must be provided that indicates the id of
the anchor. For example:
<head>
<title id="title">WCB Management Console</title>
...
</head>
Within the body of the document an anchor should be added with a name attribute to the text
contained by the h1 and h2 tags that hold an identifier that is unique within that Online help file.
For example:
<body>
<h1><a name="introduction">Introduction</a></h1>
<p>Text here...</p>
<h2><a name="configuration">Configuration</a></h2>
<p>Text here...</p>
</body>

Note that the ids in all translations of the help file should match. If anchor ids are different per
language, the context sensitive help will not work properly.

e-mail info@gx.nl

http://www.gx.nl/

81/161

WCB DEVELOPMENT Tutorial

11.3.1 Including an image


Take the next steps to include an image to the help:

Create an image and place it in the images subfolder of the help document;

Add an image tag in the help file:


<p><img src="images/import-users.png" alt="Import users" /></p>

The result will look like this:

Figure 13. Including an image in the online help


Note: If you dont add the <p> </p> around the image tag, the image will be placed inline with
the text. In this example it is preferable to have some whitespace above and underneath the
image, so the surrounding <p> </p> is placed.

11.3.2 Creating a reference to a character in an image


In some images a special arrow with a character inside it is used to point out the interesting
part(s) within the image. In the text surrounding the image its useful to refer to this arrow by
referring to this character. The screenshot above shows an image plus text above it that refers to
the image.
The special formatting of the character A is done by using the next piece of XHTML:
<span class="imageletterreference">A</span>

e-mail info@gx.nl

http://www.gx.nl/

82/161

WCB DEVELOPMENT Tutorial

11.3.3 Creating a link to an external URL


When creating a link to an external website, use the class externallink to make sure the link
opens in a new window:
<a href="http://en.wikipedia.org/wiki/Role-based_access_control"
class="externallink">Wikipedia RBAC page</a>

11.3.4 Creating a table


The Authorization and Workflow help contains the next piece of help:

The HTML for the above help is:


<dl>
<dt>Permission category</dt>
<dd>A permission category is a collection of permissions that belong
to one and the same application component. For example:<p />
<table>
<tr>
<th>Application component</th>
<th>Permission category</th>
<th>Permissions</th>
</tr>
<tr>
<td>FAQ</td>
<td>FAQ</td>
<td>Maintain FAQ sets<br />
Maintain FAQ questions<br />
Insert, edit and delete FAQ elements
</td>
</tr>
</table>
</dd>
</dl>

e-mail info@gx.nl

http://www.gx.nl/

83/161

WCB DEVELOPMENT Tutorial

1 1. 4 A d di n g t h e h el p b u tt on t o a Pa n el
By default, the Online help is displayed in the Online help panel, but there is no relation with the
panel for which the Online help was written. To do so a help button must be added to the panel.
This must be done in the configurePanel method of the panel implementation class. This class
typically extends PanelBase.
To add the help button to the panel, simply create an additional PanelButton of type
BUTTONTYPE.HELP for it. For example:
public void configurePanel() {
PanelButton close = new PanelButtonImpl(PanelButton.BUTTONTYPE.CLOSE);
PanelButton apply = new PanelButtonImpl(PanelButton.BUTTONTYPE.APPLY);
PanelButton help = new PanelButtonImpl(PanelButton.BUTTONTYPE.HELP);
PanelButton[] panelButtons = {help, apply, close};
setButtons(panelButtons);
}

The panel will now show an additional Help button which opens the Help dialog when clicked.
However, the button is still not context sensitive so when the Online help panel is opened , it will
not navigate to the relevant topic within the Online help. To enable context sensitive help , the
context of the help that should be displayed must be passed to the help button.
To do so the proper action must be set on the help button using the setAction method. The
syntax of this action must be: <WCB ID>/<Anchor id>. So for example if the ID of a WCB is defined
statically in the Activator and the anchor id is overview then the action would be set as follows:
helpButton.setAction(Activator.WCB_BUNDLE_ID + "/overview");
Since the action to set depends on the context within the panel, setting this property must be
done in a way that the proper action is set when the user is able to click on the help button. For
example, if the context depends on the selected tab within the panel, we can make the Online
help context sensitive to the selected tab by overriding the showForm of the controller as
follows:
public ModelAndView showForm(HttpServletRequest request,
HttpServletResponse response, BindException errors, Map controlModel)
throws Exception {
ModelAndView modelAndView = super.showForm(request, response, errors,
controlModel);
// Set the help context
String selectedTabId = myPanel.getTabset().getSelectedTabId();
if (selectedTabId == null || selectedTabId.equals(OVERVIEW_TAB_ID)) {
String action = Activator.WCB_BUNDLE_ID + "/overview";
myHelpButton().setAction(action);
}
else {
String action = Activator.WCB_BUNDLE_ID + "/errorlog";
myHelpButton().setAction(action);
}
return modelAndView;
}

e-mail info@gx.nl

http://www.gx.nl/

84/161

WCB DEVELOPMENT Tutorial

1 1. 5 A d di n g t h e h el p b u tt on t o a n E le m e nt
To add a help button to an element, simply implement the getHelpTopicsId method in the
Form backing object of the element. The method must return the id of the help topic that has to
be displayed when the help button is clicked. This is typically the class that extends ElementFBO.
For example, to enable context sensitive help in a custom element WCB , add the following
method to the implementation of the Form backing object:
public class CustomElementFBO extends ElementFBO {
...
public String getHelpTopicsId() {
return Activator.OH_ANCHOR_INTRODUCTION;
}
}
Note: In this example the ID of the actual help topic is defined statically (as public static
final String) in the Activator. This is the proper location for such definitions since the
Activator usually defines also namespaces, bundle and component IDs.
When the user of GX WebManager activates the help of the element, the value of
Activator.OH_ANCHOR_INTRODUCTION is looked up and returned by the method. Lets say that
the Activator.java class contains the next definition:
public static final String OH_ANCHOR_INTRODUCTION = "introduction";
Then the help file must contain an anchor with the name introduction:
<h1><a name="introduction">Introduction</a></h1>

e-mail info@gx.nl

http://www.gx.nl/

85/161

WCB DEVELOPMENT Tutorial

1 1. 6 A d di n g t h e h el p b u tt on t o a c ust o m M e di a It e m
To add a help button to the custom metadata part of a custom media item, simply add the
getHelpTopicsId method to the Form backing object of the custom media item. The method
must return the id of the help topic that has to be displayed when the help button is clicked. This
is typically the class that extends MediaItemVersionFBO. For example, to enable context
sensitive help in a custom media item WCB the following method is added to the implementation
of the Form backing object:
public class CustomMediaItemVersionFBO extends MediaItemArticleVersionFBO
{
...
public String getHelpTopicsId() {
return Activator.OH_ANCHOR_INTRODUCTION;
}
}
Note: In this example the ID of the actual help topic is defined statically (as public static
final String) in the Activator. This is the proper location for such definitions since the
Activator usually also defines namespaces, bundle and component IDs.
When the user of GX WebManager activates the help of the custom meta data in the media item,
the value of Activator.OH_ANCHOR_INTRODUCTION is looked up and returned by the method.
Lets say that the Activator.java class contains the next definition:
public static final String OH_ANCHOR_INTRODUCTION = "introduction";
The help file must contain an anchor with the name introduction:
<h1><a name="introduction">Introduction</a></h1>

e-mail info@gx.nl

http://www.gx.nl/

86/161

WCB DEVELOPMENT Tutorial

12 INSTALLING WCBS AND WCAS


Once the development of a WCB is complete, you need to deploy it to GX WebManager. WCBs can
be deployed as a standalone JAR file or several WCBs can be archived in a WCA (WebManager
Component Archive), which is simply a ZIP file containing multiple WCB JAR files. In
GX WebManager, WCBs can be deployed in two ways:

Using the WCB Management Console.

By copying the WCB or WCA to the <webmanager-root>/work/deploy directory.

1 2. 1 I nst al l i n g a WC B o r WC A u si n g t h e W C B M a n a g e m e nt
C o ns ol e
To install or update WCBs using the WCB Management Console, follow these steps:

In GX WebManager, navigate to Configure > WCB Management Console.

Click [Browse] next to Install or update a WCB:

Browse to the WCB JAR file or WCA ZIP file, select it and click [Open].

e-mail info@gx.nl

http://www.gx.nl/

87/161

WCB DEVELOPMENT Tutorial

Click [Apply]. For example:

The WCB or WCBs contained in the WCA are installed. Once installed, you will see the
new WCB(s) in the list. For example:

1 2. 2 I nst al l i n g a WC B o r WC A u si n g t h e w o rk/d epl oy di re c t or y


WCBs can be deployed to GX WebManager using the <webmanager-root>/work/deploy
directory. When a WCB is deployed to this directory, it is immediately installed in GX
WebManager. Once the WCB is installed, the JAR file(s) are deleted from this directory. To install
or update a WCB using the <webmanager-root>/work/deploy directory:

Copy the WCB JAR file or WCA ZIP file to the


<webmanager-root>/work/deploy directory in your deployment. If GX WebManager
is running, it will automatically install all standalone WCBs and/or those contained
within a WCA. If GX WebManager is not running the WCB(s) will be installed the next
time GX WebManager is started. Once installed, you can manage these WCBs using the
WCB Management Console.

e-mail info@gx.nl

http://www.gx.nl/

88/161

WCB DEVELOPMENT Tutorial

e-mail info@gx.nl

http://www.gx.nl/

89/161

WCB DEVELOPMENT Tutorial

e-mail info@gx.nl

http://www.gx.nl/

90/161

WCB DEVELOPMENT Tutorial

13 THE ENTITY MANAGER


1 3. 1 I ntr o d u cti o n
The Entity Manager is a service that is capable of managing entities. An entity is an object like a
Book, Person or Car. Entities, or domain objects, are defined and registered by a component
within a WCB. Entities are implemented as a Java class with particular entity annotations and
always defines an implementation class and an interface. Both the interface and implementation
are registered by the Entity Manager. The Entity Manager supports all CRUD actions on the entity.
So it can Create, Retrieve, Update and Delete entities.
Note that using the Entity Manager is a required guideline, see G125 of the WCB Development
Guidelines document.

1 3. 2 Im pl e m e nti n g a n E nti ty
As stated above, the implementation of an entity consists of two Java classes: the entity
implementation and the interface. The interface contains the public methods that may be invoked
by any external class, even Java classes from another WCB. The interface must extend the
Identifiable interface, which holds getters and setters for the WmId. The WmId is the unique
identifier for the entity inside the Entity Managers registry.
The WmId is an interface. In most cases you will use the JcrWmId implementation of this
interface for identifying entities stored inside the JCR. An example interface of an Entity with
two properties, name and text, is displayed below.
public interface CustomEntity extends Identifiable {
String getName();
void setName(String name);
String getText();
void setText(String text);
}
The entity implementation implements the entity interface described above. To identify that this
Java class contains the definition of an Entity, four specific Entity annotations must be added to
the header of the class.
1.

@Entity: First, the @Entity annotation must be added to the class header to identify
the class as definition of an entity. The annotation takes no arguments.

2.

@Interfaces: Secondly, the interface that is associated with this entity must be
defined using the @Interfaces annotation. It takes the full classname (including
package) of the associated interface as argument.

3.

@Namespace: Thirdly, the namespace for this entity must be defined for this entity
using the @Namespace annotation. The annotation takes two arguments, the prefix and
the URI. Note that the WCB defines one namespace and URI for all classes contained by
the WCB, including the entity. Usually the namespace and URI are defined in the
Activator of the WCB or a Java class containing all constants, l ike Ids, namespace and
URI.

e-mail info@gx.nl

http://www.gx.nl/

91/161

WCB DEVELOPMENT Tutorial

4.

@Nodetype: Finally, when the Entity will be stored inside the JCR, the @NodeType
annotation should be used to define the node type, super type and mixin node types of
the node that represents the entity in the JCR. The @NodeType annotation takes four
arguments; name, registerNodeType, supertype and mixin.
a)

The name argument defines the name of the node type prefixed by the namespace
prefix followed by :.

b)

The registerNodeType argument indicates that the node type must be registered
at the Entity Manager; always specify true for this argument.

c)

The supertype argument defines the primary node type of the node in the JCR. If
there is no particular node type that the entity node type should extend, use the
JCR base node type which is {http://www.jcp.org/jcr/nt/1.0}base. Note
that

this

base

node

type

is

defined

in

nl.gx.webmanager.services.jcrrepository.Constants.NT_BASE_QUALIF
IED_NAME so recommended is to use this static variable.
d)

Finally, the mixin argument defines the additional super node types of the node. In
many cases the node associated with the entity must be able to be referenced and
thus extend {http://www.jcp.org/jcr/nt/1.0}referenceable (defined by
Constants. MIX_REFERENCEABLE_QUALIFIED_NAME.

For

example,

the

annotations

for

an

entity

implementing

the

nl.gx.product.domain.CustomEntity interface could look like this:


// Marks this class as being an entity
@Entity
// Defines the interface that identifies this entity
@Interfaces("nl.gx.product.domain.CustomEntity")
// Defines the namespace of this entity
@Namespace(
prefix = Activator.NAMESPACE_PREFIX,
URI = Activator.NAMESPACE_URI
)
// Defines the node type of this entity
@NodeType(
name = Activator.NAMESPACE_PREFIX + ":" +
Activator.CUSTOM_ENTITY_NODETYPE,
registerNodeType = true,
supertype = Constants.NT_BASE_QUALIFIED_NAME,
mixin = Constants.MIX_REFERENCEABLE_QUALIFIED_NAME
)
The implementation of the entity class itself is rather straigh t forward. To identify properties
that must be persisted, the corresponding getter for the property should be prefixed with the
@Property annotation. Since the entity implementation implements the Identifiable
interface, the entity implementation must implement getId and setId. As stated before, when
the entity is stored in the JCR the WmId used by the entity is most likely the JcrWmId
implementation of that interface.
For proper Entity implementation it is also a good idea to implement the compareTo and equals
methods.

An

example

Entity

implementation

of

an

entity

e-mail info@gx.nl

called

CustomEntityImpl

http://www.gx.nl/

92/161

WCB DEVELOPMENT Tutorial

implementing the CustomEntity interface that contains two properties, name and text, is
shown below. Note that JavaDoc annotations are omitted in this example to decrease its size.
@Entity
@Interfaces("nl.gx.helloWorld.helloWorldPanel.domain.CustomEntity")
@Namespace(
prefix = Activator.NAMESPACE_PREFIX,
URI = Activator.NAMESPACE_URI
)
@NodeType(
name = Activator.NAMESPACE_PREFIX + ":" +
Activator.CUSTOM_ENTITY_NODETYPE,
registerNodeType = true,
supertype = Constants.NT_BASE_QUALIFIED_NAME,
mixin = Constants.MIX_REFERENCEABLE_QUALIFIED_NAME)
public class CustomEntityImpl implements
CustomEntity,Comparable<CustomEntityImpl>{
private String myName;
private String myText;
private JcrWmId myId;
@Property
public String getName() {
return myName;
}
public void setName(String name) {
myName = name;
}
@Property
public String getText() {
return myText;
}
public void setText(String text) {
myText = text;
}
public WmId getId() {
return myId;
}
public void setId(WmId id) {
myId = (JcrWmId) id;
}
public int compareTo(CustomEntityImpl otherEntity) {
int result = getName().compareTo(otherEntity.getName());
if (result == 0) {
result = getId().toString().
compareTo(otherEntity.getId().toString());
}
return result;
}
public boolean equals(Object obj) {
if (obj instanceof CustomEntityImpl) {
return ((CustomEntityImpl) obj).getId().toString().
equalsIgnoreCase(getId().toString());
}
return false;
}
}

e-mail info@gx.nl

http://www.gx.nl/

93/161

WCB DEVELOPMENT Tutorial

1 3. 3 R e gi s t eri n g a n E n t i ty
The implementation of the Entity does not automatically register the entity with the Entity
Manager to be managed by it. To do so, this must explicitly be defined in the Component
definition that defines the entity contained by the Activator. There are three methods available
on the ComponentDefinitionImpl that are relevant to entity registration.
The setEntityClassNames method must be invoked in order to define the classnames of all
entity implementations to be registered. Furthermore, the classnames of the entity factory and
persistence

manager

must

be

defined

using

the

setEntityFactoryClassName

and

setPersistenceManagerClassName methods. The purpose of these classes is to enable the


extensibility of the CRUD actions implemented by the Entity Manager. In this version of
WebManager however, it is recommended that you only use the default implementations, which
are DefaultJcrEntityFactoryImpl and DefaultJcrPersistenceManager. An example
of the entity related definitions in the Component definition for the CustomEntity example is
stated below.
componentDefinition.setEntityClassNames(
new String[] {CustomEntityImpl.class.getName()});
componentDefinition.setEntityFactoryClassName(
DefaultJcrEntityFactoryImpl.class.getName());
componentDefinition.setPersistenceManagerClassName(
DefaultJcrPersistenceManager.class.getName());
Note: be sure not to invoke setEntityClassNames() on the same entity in different
component definitions. This causes a non-deterministic definition of the component that owns the
Entity and will cause errors in some occasions, like purging the component.

1 3. 4 Usi n g t h e E nti ty M a n a g er
The Entity Manager is implemented as a service. This means that the component that uses the
Entity

Manager

must

define

required

service

nl.gx.webmanager.services.entitymanager.EntityManager

dependency

with

the

interface, therefore the

Component definition inside the Activator must contain the following code snippet:
ComponentDependencyImpl emServiceDependency =
new ComponentDependencyImpl();
emServiceDependency.setServiceName(EntityManager.class.getName());
emServiceDependency.setRequired(true);
componentDdefinition.setDependencies(
new ComponentDependency[]{emServiceDependency});
As a result, the Entity Manager service will be injected into the component implementation class.

e-mail info@gx.nl

http://www.gx.nl/

94/161

WCB DEVELOPMENT Tutorial

Furthermore, the bundle dependency must be defined in the pom.xml of the WCB. The following
code snippet illustrates how this dependency is defined within the <dependencies> section of
the pom.xml. Note that the <version> attribute should state the WebManager version against
which the WCB is built.
<dependency>
<groupId>nl.gx.webmanager.bundles</groupId>
<artifactId>webmanager-entitymanager-bundle</artifactId>
<scope>provided</scope>
<version>${parent.version}</version>
</dependency>
<dependency>
<groupId>nl.gx.webmanager.bundles</groupId>
<artifactId>webmanager-entitydomain-bundle</artifactId>
<scope>provided</scope>
<version>${parent.version}</version>
</dependency>

1 3. 5 En ti ty Ma n a g e m e n t in t h e c o ntr oll e r
Now that the Entity is defined and registered at the Entity Manager, the next step is to implement
the create, update and delete actions on the entity. This is typically done in the onSubmit of the
components controller. The onSubmit should handle three different types of actions to be
performed on the entities:
1.

Creating entities using EntityManager.getInstance and EntityManager.persist

2.

Saving modified entities using EntityManager.save

3.

Deleting entities using EntityManager.delete

Note that the difference between EntityManager.persist and EntityManager.save is


that persist() should be invoked when the entity is saved for the first time, and thus does not
yet exist in the internal storage system, and save() should be invoked on entities that have
previously been persisted.
Usually, properties are transferred from the form backing object into the Entity before it is
created or updated. The easiest way to copy the properties is to use the BeanUtils utility class
provided by the Spring framework. The only prerequisite is that the property names of form
backing object match the corresponding properties of the Entity. To copy properties from the
form backing object called CustomTabFBO to the Entity, simply use:
org.springframework.beans.BeanUtils.copyProperties(
myCustomTabFBO, entity);

e-mail info@gx.nl

http://www.gx.nl/

95/161

WCB DEVELOPMENT Tutorial

An example implementation of the onSubmit in the component controller is shown below.


public void onSubmit(HttpServletRequest request, HttpServletResponse
response, Object command, BindException errors, ModelAndView
modelAndView) throws Exception {
super.onSubmit(request, response, command, errors, modelAndView);
// Save the entity that is currently being edited
if (myCustomTabFBO.getEntity() != null) {
CustomEntity entity = myCustomTabFBO.getEntity();
BeanUtils.copyProperties(myCustomTabFBO, entity);
myEntityManager.save(entity);
}
// Check if a new entity must be created
if (myCustomTabFBO.getPanelAction().equals("createEntity")) {
// Create the entity
CustomEntity newEntity =
myEntityManager.getInstance(CustomEntity.class);
// Persist the entity for the first time
myEntityManager.persist(newEntity);
// Select the new entity in the panel
myCustomTabFBO.setSelectedEntity(newEntity);
}
// Check if existing entities must be deleted
if (myCustomTabFBO.getRemoveEntities() != null) {
for (CustomEntity entity : myCustomTabFBO.getRemoveEntities()) {
// Delete the entity
myEntityManager.delete(entity);
}
}

1 3. 6 En ti ty r etr i e v al
Before existing entities can be updated, they must first be retrieved. For this, the Entity Manager
supports two methods; find(WmId) for retrieving entities of which the Id is known and
getAll(Class<T>) to retrieve all entities implementing a particular Entity interface.
Usually the getAll method is used in the referenceData method of the controller in order to
retrieve all entities and insert them into the reference data map. Note that the getAll method
returns an unordered Set containing the entities in a random order. The order can change
between different http requests. For this reason the Set should be ordered when it is displayed
in a table or list by the JSP. The example below shows how to retrieve the entities from the
entity manager, sort them and put them onto a property called allEntities.

e-mail info@gx.nl

http://www.gx.nl/

96/161

WCB DEVELOPMENT Tutorial

public Map<String, Object> referenceData(HttpServletRequest request,


Object command, Errors errors) throws Exception {
Map<String, Object> referenceData;
referenceData = super.referenceData(request, command, errors);
// Retrieve all custom entities and sort them
Set<CustomEntityImpl> entities =
myEntityManager.getAll(CustomEntityImpl.class);
List<CustomEntityImpl> entityList =
new ArrayList<CustomEntityImpl>();
for (CustomEntityImpl entity: entities) {
entityList.add(entity);
}
Collections.sort(entityList);
// Write the sorted list of all entities to the reference data
referenceData.put("allEntities", entityList);
return referenceData;
}
The above example retrieves all entities, but sometimes you might want to retrieve just one
particular entity for which the Id is known. You would do this, for example, to edit a particular
entity. To retrieve the entity from the Entity Manager by its WmId, simply invoke:
myEntityManager.find(jcrWmId);

The WebManager API however already supports a basic property editor for all Entities stored in
the JCR. This property editor is called EntityPropertyEditor

and supports

converting

entities stored in the JCR to a String that identifies that entity and vice versa. To register this
property editor, simply create an instance of this property editor for the entity in the
initBinder method of the controller:
public void initBinder(HttpServletRequest request,
ServletRequestDataBinder binder) throws Exception {
super.initBinder(request, binder);
// Register a custom editor for the custom entity
binder.registerCustomEditor(CustomEntity.class,
new EntityPropertyEditor<CustomEntity>(myEntityManager));
}

e-mail info@gx.nl

http://www.gx.nl/

97/161

WCB DEVELOPMENT Tutorial

1 3. 7 Pr o p erty an n o t ati o ns
As stated before, the @Property annotation before a method indicates that the method is the
getter method for a particular property that should be persisted by the Entity Manager. The
property may be of a primitive type like String, boolean and Integer. Complex object types
are supported including FileResource. Complex property types are discussed in detail in this
section.

13.7.1 @Property
When the regular @Property annotation is used for a complex object type, the property is
created as a reference. This means that the physical location of the object in the persistence
system (for example the JCR) is not defined; it may be stored anywhere in that persistence
system. The property itself only stores a reference to this object (in the JCR , for example, it
stores the UUID of the node as String property).
The @Property annotation has the following properties:
Property

Description

name

The name of this property in the repository. Leave this empty if you want to
use the same name as the property in the Java class.

type

The JSR-170 property type to use. The default is


PropertyType.UNDEFINED which means that the type will be determined
by the type of the property in the Java class.

mandatory

Specifies whether this property is mandatory. The default is false.

multiple

Specifies whether the property may have more than one value. The default is
false.

constraints

Add JSR-170 constraints to the property. By default no constraints are added.

13.7.2 @Child
The @Child annotation can be used to indicate that the object should not be stored as a
reference property but as a child property of this entity. As a result, the physical location of the
object will be such that there will be a parent-child relation between the entity and the object to
which is referred by the @Child annotation.
In the case of the JCR this means that the node that corresponds to the entity is a parent node of
the node that corresponds to the object of the property marked with the @Child annotation.
Note that this is an important difference compared to the by reference approach, for example
when using XPATH queries to search for the child node.

e-mail info@gx.nl

http://www.gx.nl/

98/161

WCB DEVELOPMENT Tutorial

The @C3hild annotation has the following properties:


Property

Description

name

The name of this property in the repository. Leave this empty if


you want to use the same name as the property in the Java
class.

mandatory

Specifies whether this property is mandatory. The default is


false.

allowsSameNameSiblings

Specifies whether this child node can have same-name siblings.


In other words, whether the parent node can have more than
one child node with the same name. The default is false.

requiredPrimaryTypes

Specifies the minimum set of primary node types that the child
node must have. The default is
{http://www.jcp.org/jcr/nt/1.0}base which is the JSR170 base type of all node types.

defaultPrimaryType

Specifies the default primary node type that will be assigned to


the child node if it is created without an explicitly specified
primary node type. The default is
{http://www.jcp.org/jcr/nt/1.0}unstructured which
indicates the unstructured node type.

Note:

The @Child annotation supports multiple values by returning an array of the child class
(For example returning CustomEntity[]).

The @Child entity is an alternative to the @Property annotation, they should not be
used both.

13.7.3 @Collection
The @Collection annotation can be used for properties of which the object type implements
java.util.Collection . This annotation offers more advanced options mainly by supporting
so called lazy collections. A lazy collection is a collection of objects of which the objects
themselves are not instantiated upon instantiation of the collection itself. There is therefore a
significant difference between a regular collection (i.e. a Set) of CustomEntity objects and a
lazy collection of CustomEntity objects. While the regular collection will contain a list of
references to the actual instances of CustomEntity objects, the lazy collection only contains a
list of references without the CustomEntity objects themselves being loaded upon creation of
the collection itself. This provides an important performance gain when such a collection contains
many objects and nothing is actually done with the objects in the collection themselves. A lazy
collection instantiates the objects within the collection only at the time they are needed.

e-mail info@gx.nl

http://www.gx.nl/

99/161

WCB DEVELOPMENT Tutorial

The @Collection annotation has the following properties:


Property

Description

type

The type of the collection property, which must be either


Type.LIST or Type.Set.
Specifies the class associated with the objects contained by the

memberType

collection.
Specifies whether the collection should use lazy loading or not.

lazy

Default is true.
Specifies if the objects contained by the reference should be stored

reference

by reference or as child objects. Default is by reference.


Note: The @Collection annotation should be used in combination with the @Property
annotation. If only the @Collection annotation is provided, the property will not be identified
as a property of the entity.

1 3. 8 C o m p o ne n ts o f t h e E ntit y M a na g e r
The Entity Manager comprises several components that together form a layered archite cture, as
depicted in the image below. Each layer delegates method calls to the appropriate component on
the layer below it.

EntityManager

JcrEntityDomain

DefaultJcrEntityFactory

DefaultJcrPersistenceManager

AnotherEntityDomain

DefaultJcrEntityFactory

DefaultJcrPersistenceManager

AnotherEntityFactory

DefaultJcrPersistenceManager

13.8.1 Entity Manager


The Entity Manager is available as a service in the GX WebManager platform. Its function is to
hide all underlying layers from the WCB programmer. Internally, the Entity Manager manages a
cache for each active session.

e-mail info@gx.nl

http://www.gx.nl/

100/16
1

WCB DEVELOPMENT Tutorial

13.8.2 Entity Domains


In the image on the previous page, two types of Entity Domains are visible directly beneath the
Entity Manager. Such Entity Domains represent a namespace for identifying entities. They each
provide an implementation of Identifiable specifically for this EntityDomain. The
JcrEntityDomain and its accompanying implementation of WmId and JcrWmId are available. It
is the responsibility of the EntityManager to select the right EntityDomain for an Entity and
then delegate to it.

13.8.3 Entity Factories


Entity Factories comprise the next layer. They provide a mechanism for post -processing entities
after they have been constructed. Usually only one type of Entity Factory is used, namely the
DefaultJcrEntityFactoryImpl. Each Entity Factory can handle multiple types of entities.
Basically there is one EntityFactory per Component that handles all entities specified by that
Component. It is the responsibility of Entity Domain to delegate to the appropriate Entity Factory
for a given type of entity.

13.8.4 Persistence Managers


The last layer within the Entity Manager is formed by the Persistence Managers. It is the
responsibility of the Persistence Managers to handle the communication with the underlying
persistent storage and construct entities. One Persistence Manager is instantiated per entity type
which handles all requests for that type of entity. It is the responsibility of the Entity Factories to
manage those Persistence Managers and delegate to them. Typically only one type of Persistence
Manager is used, namely the DefaultJcrPersistenceManager.

1 3. 9 Th e E nti ty M a n a g e r v ers us J C R U til


In this chapter we extensively elaborated on using the Entity Manager. However, if you generate
an element or media item from its archetype you will notice that these components do not use
the Entity Manager to persist their properties. The element and media item implementations are
marked with the @NodeType and @Property annotations but note the missing @Entity
annotation. The missing @Entity annotation indicates that this class is not managed by the
Entity Manager.
Persistent classes not managed by the Entity Manager are entities of which its lifecycle is
managed by WebManager. For example, if a custom element is added to a page in WebManager it
is the WebManager framework that creates a new instance of that element. If the custom element
subsequently is marked for deletion and the page is submitted, the element will be dele ted by
the WebManager framework.
This is different from entities managed by the Entity Manager. With those entities you will have
full control over the lifetime of the entity instances. To create such an entity instance you must
explicitly invoke the Entity Manager (i.e. an entity in a panel), the same applies for deleting the
entity.

e-mail info@gx.nl

http://www.gx.nl/

101/16
1

WCB DEVELOPMENT Tutorial

Persistent classes not managed by the Entity Manager are:


Persistent class

Description

Custom media item

Class that extends MediaItemArticleVersionImpl or

implementation

MediaItemVersionImpl, i.e.
CustomMediaItemArticleVersionImpl

Custom

element

Class that extends ElementBase, i.e. CustomElementImpl.

implementation
A persistent class that is not managed by the Entity Manager may persist its properties using the
nl.gx.webmanager.foundation.JcrUtil class. This class contains methods to read and
write values from and to JCR nodes. The getters and setters of an entity managed by the Entity
manager usually only store the value in a class variable and persist the entity by using
EntityManager.persist(). Using JCRUtil is an alternative to EntityManager.persist()
however it should be invoked to persist each property individually. For example, to persist the
company property:
public void setCompany(String company) {
JcrUtil.setString(getPrivateNode(),
WCBConstants.NAMESPACE_PREFIX + ":company", company);
}

Notes:

The getPrivateNode() receives the node instance that is created automatically by the
WebManager framework and that contains the persistent properties.

Important to note is that even persistent class not managed by the Entity Manager can
still persist its properties using the Entity Manager. To do so simple add only one child
property to the class that has the same type as the entity managed by the entity
manager.

e-mail info@gx.nl

http://www.gx.nl/

102/16
1

WCB DEVELOPMENT Tutorial

14 EXTENSIBILITY
GX WebManager extensibility provides a framework for the runtime extension of WebManager
components like services, elements and panels. This can be done by defining extension points in
WCBs (called consumers) that are implemented and provided runtime by other WCBs (called
providers). The extension points are realized by the delegation of services in the framework to
other WCBs. These services can be service components like a find books service ; the delegation
of MVC controller calls is also possible within this framework. This delegation of controllers makes
it possible to, for example, replace the complete view of WCBs in the framework with an
alternate view from another WCB, to add a view (new text field) and/or services.
The Extensibility framework consists of a set of framework interfaces like the Extensible,
ExtensionPoint and ViewExtension interfaces. By providing an implementation of these
interfaces in your WCB and registering these interfaces and extension points in the service
framework, WCBs can consume and provide extension points.

e-mail info@gx.nl

http://www.gx.nl/

103/16
1

WCB DEVELOPMENT Tutorial

The following figure illustrates the basic steps that are required to make a WCB extendable
(consumer) and to create an extension for it (provider):

Figure 14. WCB consumer and provider

e-mail info@gx.nl

http://www.gx.nl/

104/16
1

WCB DEVELOPMENT Tutorial

The example code in this section comes from the GX WebManager Bookstore WCB example,
available for download from the GX WCM Exchange (www.wcmexchange.com).

1 4. 1 Ex t e nsi o n c o ns u m ers
Extension consumers are WCB components that consume the extension services of WCBs that
provide these services. A WCB component becomes a consumer by adding the interface classname
Extensible to its interface classnames. The framework recognizes this interface and will
register the component in the framework as a WCB component extension consumer.
A consumer can consume the extension services of more than one component and the developer
of the WCB component that consumes the service decides how and in what order these services
are consumed. The developer therefore has access to the set of extensions that are registered at
the consumer component by the getExtensions() method.

14.1.1 Registering a WCB consumer component


By registering the Extensible interface, the component becomes a WCB component consumer
and will be ready for consuming extension services in the service framework.
String[] componentInterfaces = { Component.class.getName(),
ElementComponent.class.getName(), Extensible.class.getName() };
elementDefinition.setInterfaceClassNames(componentInterfaces);

14.1.2 Registering consumer extensions


To define which services a WCB component will consume, you must register the extension
services. This can be done by registering the exact interfaces the component will consume:
// Extensions
ComponentExtensionImpl viewExtensionPoints = new ComponentExtensionImpl();
viewExtension.setServiceName(ViewExtension.class.getName());
elementDefinition.setExtensions( new ComponentExtensionImpl[] { viewExtension } );

1 4. 2 Ex t e nsi o n pr ovi d e r s
Extension providers are WCB components that provide extension services for other WCBs. A WCB
component

becomes

an

extension

provider

by

adding

the

interface

classname

ExtensionProvider to its interface classnames. The framework recognizes this interface and
will register the component in the framework as a WCB component extension provider.

14.2.1 Register a WCB provider component


By registering the ExtensionProvider interface, the component becomes a WCB component
provider and will be ready to provide extension services to the service framework:

e-mail info@gx.nl

http://www.gx.nl/

105/16
1

WCB DEVELOPMENT Tutorial

definition.setInterfaceClassNames(
new String[]{ExtensionProvider.class.getName()});
The WCB component also has to register a valid implementation of the ExtensionProvider
interface that can act as a factory for the service extensions:
definition.setImplementationClassName(ExtensionProviderImpl.class.getName());
The provider implementation has to implement the ExtensionProvider interface and therefore
has to implement the following methods :
-

public String getTargetComponentId()


This method must return the ID of the component that is allowed to consume the
extension services provided by the component.

public Object getServiceInstance(Class interfaceClass)


This method must return an instance of the implementation class of the requested
interface.

It is your choice to decide whether the getServiceInstance(..) method should:


-

Always returns the exact same object (singleton instance). This object is cached within
the service component

Create a new instance of the requested object for each request to the extension service.

14.2.2 Registering provider extensions


To define exactly which services a WCB component will provide, the registration of these
extensions services is needed. This can be done by registering the exact interfaces the component
will provide in the WCB Activator:
// Extensions
ComponentExtensionImpl bookServiceExtension = new
ComponentExtensionImpl();
bookServiceExtension.setServiceName(FindBookService.class.getName());
definition.setExtensions(new ComponentExtension[] { bookServiceExtension
});

The extension service interfaces must be registered at the consumer and provider component.

e-mail info@gx.nl

http://www.gx.nl/

106/16
1

WCB DEVELOPMENT Tutorial

The following figure shows the workflow of an example of an extension for the GX Books Example
WCB. The extension (provider) extends the findBooks() method of the consumer further by
searching web sites and other databases for books when a user executes a search:

Figure 15. WCB provider workflow

e-mail info@gx.nl

http://www.gx.nl/

107/16
1

WCB DEVELOPMENT Tutorial

1 4. 3 A lte r n at e v i e w e x t e nsi o n e x a mp l e
The following is an example of creating an alternate view extension.
To extend a WCB with an alternate view, the framework interface ViewExtension is provided
by the framework which can be registered by a WCB extension consumer and provider and can be
implemented by the provider extension. The alternate view extension is a subclass of the view
extension. Registration of the ViewExtension interface in the Activator looks like:
// Extensions
ComponentExtensionImpl alternateViewExtension = new
ComponentExtensionImpl();
alternateViewExtension.setServiceName(ViewExtension.class.getName());
The ViewExtension interface must be registered at the extension provider and consumer
component. The consumer component will automatically delegate the MVC call s to the controllers
provided by the view extension points.
The extension provider must provide an implementation of the ViewExtension interface and
return an instance of the implementation through the getServiceInstance() method of the
ExtensionProvider interface:

14.3.1 ExtensionProviderFactory
public

class

ExtensionProviderFactory

extends

SimpleServiceComponent

implements ExtensionProvider {
public Object getServiceInstance(Class interfaceClass) {
return new AlternateViewProvider(this);
}
public String getTargetComponentId() {
return "com.libris4you.books.bookreviewselement";
}
}

e-mail info@gx.nl

http://www.gx.nl/

108/16
1

WCB DEVELOPMENT Tutorial

14.3.2 AlternateViewProvider
The extension should return an implementation of the ViewExtension interface as a result of
the call to the getServiceInstance() method. The component that is extended and consumes
the view extension will delegate the MVC calls to the controller that is returned by the
ViewExtension.getDelegatedController() method:
public class AlternateViewProvider implements ViewExtension {
private Component component;
AlternateViewProvider(Component component) {
this.component = component;
}
public DelegatedController getDelegatedController() {
ComponentController controller = new AlternateViewController();
controller.setComponent(component);
return controller;
}
}

e-mail info@gx.nl

http://www.gx.nl/

109/16
1

WCB DEVELOPMENT Tutorial

14.3.3 AlternateViewController
The controller for the alternate view has access to the parent controllers through getParent and
can be used to manipulate the behavior of this parent controller. To override the complete
default view of an extension consumer

the

createEditViews() method should be

implemented and its implementation should add a default edit view to the parent controller in
order to override the default view of this controller and its component:
public class AlternateViewController extends ElementComponentController
{
@Override
public Object formBackingObject(HttpServletRequest request)
throws ServletException {
super.formBackingObject(request);
Object fbo = getParentController().formBackingObject(request);
setDelegatedControllers(fbo, request);
return fbo;
}
@Override
protected void createEditViews() {
super.createEditViews();
ComponentController controller =
(ComponentController) getParentController();
controller.addEditView("editBookReviewsElement.jspf",
"", this.getComponent());
}
}

14.3.4 Implement alternate view (jsp)


To provide an alternate view for a consumer WCB component a JSP file must be provided in the
src/main/resources/editpresentation directory of the WCB. This JSP view must be added
as a view in the createEditViews() method of the controller as can be seen in the above
example.
To add the view to the controller the method addEditView(String jspLocation, String
viewname, Component component) must be used. The third component argument is need to
tell the parent controller where to the find the exact location of the JSP which is determined by
ID of the component bundle that provides the JSP.

e-mail info@gx.nl

http://www.gx.nl/

110/16
1

WCB DEVELOPMENT Tutorial

1 4. 4 Pu bl i s hi n g a n d S u b scr ib i ng t o E v e nts
The publish and subscribe pattern is a pattern in which publishers broadcast an event which is
received by the subscribers. A subscriber subscribes itself to particular types of events such that
it only receives events in which it has some interest.
WebManager supports the publish and subscribe pattern by the implementation of an Eve nt
Manager service. All event management related interfaces are contained by the package
nl.gx.webmanager.services.event. The most important interfaces of this package are:

EventManagerService. The event manager service; the publisher.

EventHandler. The event handler; the subscriber.

Event. The event broadcasted by the event manager service and received by the event
handler.

There is only one publisher in WebManager, which is the only implementation of the event
manager service. By default this service only broadcasts events on basic operations. The following
table shows the events that are published by this event manager for each action type on each
object type. For example, when creating a new page, a POST event is published, when deleting a
page section, a PRE event is published, the page section is deleted, and then a POST event is
published, and so forth.
Create

Update

Move

Retrieve

Delete

Scope

Page

POST

POST*

PRE, POST

n/a

PRE, POST

Page.class

Page model

POST

n/a

n/a

n/a

PRE, POST

PageModel.class

Page version

POST

POST*

n/a

n/a

PRE, POST

PageVersion.cla

Media item

POST

n/a

n/a

n/a

PRE, POST

MediaItem.class

Element

POST

n/a

n/a

PRE, POST

PRE, POST

Element.class

Object Type

ss

* Only occurs when adding leading text or when redirecting or reordering an element.

Each of the above object types implements an event interface: PageEvent, PageModelEvent,
PageVersionEvent, MediaItemEvent and ElementEvent.

e-mail info@gx.nl

http://www.gx.nl/

111/16
1

WCB DEVELOPMENT Tutorial

14.4.1 WebManager nl.gx.webmanager.services.event Package


The nl.gx.webmanager.services.event package contains the following interfaces:
Interface

Description

EntityEvent

Contains the events specific to CRUD operations. This class is


implemented by the ElementEvent, MediaItemEvent,
PageEvent, PageModelEvent, and PageVersionEvent
interfaces.
Contains methods for retrieving information about the event that

Event

occurred.
EventHandler

Contains a method for reacting to event notifications.

EventManagerService

Contains methods for publishing, subscribing, and unsubscribing to


events.

In addition to the standard actions (create, update, move, retrieve, and delete), you can also
create custom actions that perform other functions. These custom actions can also be published
and subscribed to. Note that if you want to publish custom events, you will need to implement a
custom event and publish this event using the Event Manager service.

14.4.2 Writing an event handler


A

custom

event

handler

should

implement

the

nl.gx.webmanager.services.event.EventHandler interface. This interface contains only


one method, which is onEvent. The onEvent method takes the event that triggered the handler
as input argument. The event contains information about the action that triggered the event, the
component that did throw the event and the event type (PRE or POST). What to do with the event
is up to the implementation of the event handler.
The code snippet on the next page provides an example implementation of a custom event
handler.

e-mail info@gx.nl

http://www.gx.nl/

112/16
1

WCB DEVELOPMENT Tutorial

public class CustomMediaItemEventHandler implements EventHandler {


public CustomMediaItemEventHandler() {
}
public void onEvent(Event event) {
if (event instanceof MediaItemEvent) {
MediaItemEvent mediaItemEvent = (MediaItemEvent) event;
// Get the media item version
MediaItemVersion mediaItemVersion = null;
if (mediaItemEvent.getMediaItem().getCurrent() != null) {
mediaItemVersion = mediaItemEvent.getMediaItem().getCurrent();
} else {
mediaItemVersion = mediaItemEvent.getMediaItem().getPlanned();
}
// Check if this really is our custom article
// media item version
if (!CustomArticleMediaItemVersion.class.isAssignableFrom(
mediaItemVersion.getClass())) {
return;
}
// Handle the event
if (event.getEventAction().equals(EntityEvent. CREATE)) {
handleCreateEvent(mediaItemEvent, mediaItemVersion);
}
if (event.getEventAction().equals(EntityEvent.DELETE)) {
handleDeleteEvent(mediaItemEvent, mediaItemVersion);
}
}
}
/**
* Does something when an article of our custom type is created.
*/
private void handleCreateEvent(MediaItemEvent mediaItemEvent,
MediaItemVersion mediaItemVersion) {
...
}
/**
* Does something when an article of our custom type is deleted.
*/
private void handleDeleteEvent(MediaItemEvent mediaItemEvent,
MediaItemVersion mediaItemVersion) {
...
}
}

e-mail info@gx.nl

http://www.gx.nl/

113/16
1

WCB DEVELOPMENT Tutorial

14.4.3 Subscribing to events


To subscribe the event handler to particular events, the handler must be subscribed by the
publisher; the event manager service. The EventManagerService interface contains the
following methods:
Method

Description

publish

Publishes an event to the framework.


void publish(Event event)
where event is the event to be published.

subscribe

Subscribes to a particular event handler.


void subscribe(EventHandler handler,
Event.Type eventType,
java.lang.Class<?> desiredScope)
where handler specifies the name of the handler to subscribe to,
eventType specifies the type of event you are subscribing to, and
desiredScope specifies the scope class of the event.

unsubscribe

Unsubscribes from a particular event handler.


void unsubscribe(EventHandler handler,
Event.Type eventType,
java.lang.Class<?> desiredScope)
where handler specifies the name of the handler to unsubscribe from,
eventType specifies the type of event you are unsubscribing from, and
desiredScope specifies the scope class of the event you are unsubscribing
from.

So to subscribe the event handler, the subscribe method on the event manager service must be
invoked. The subscribe method takes, next to the event handler, an event type and a desired
scope as input arguments. The event type may be PRE or POST. The scope indicates the object
type on which the event applies. For the basic events the appropriate scopes are mentioned in
the event overview table in the first section of this chapter.
The unsubscribe method can be used to unsubscribe the event handler from events. Usually an
event handler is subscribed in the onStart() method of the component and unsubscribed in the
onStop() method.
Note: It is very important not to forget to unsubscribe. If you dont, the event handler will keep
receiving and handling events even when the WCB is stopped. When a WCB is updated, the event
will be received by an old as well as a new instance of the event handler and thus be handled
twice.

e-mail info@gx.nl

http://www.gx.nl/

114/16
1

WCB DEVELOPMENT Tutorial

The code snippet below shows an example of subscribing an event handler of an event.
public class CustomMediaItemComponent extends SimpleMediaItemComponent {
private CustomEventHandler myEventHandler = null;
public void onStart() {
// Initialize new event handler
myEventHandler = new CustomEventHandler();
// Subscribe to media item post event
myEventManagerService.subscribe(myEventHandler, Event.Type.POST,
MediaItem.class);
}
public void onStop() {
// Unsubscribe to media item post event
myEventHandler.unsubscribe(myEventHandler, Event.Type.POST,
MediaItem.class);
}
}

e-mail info@gx.nl

http://www.gx.nl/

115/16
1

WCB DEVELOPMENT Tutorial

e-mail info@gx.nl

http://www.gx.nl/

116/16
1

WCB DEVELOPMENT Tutorial

15 ADDING A COMPLEX FIEL D TO A COMPONENT


In the first chapter of this tutorial we described how to add simple input fields to the several
component types that WebManager supports. In this chapter we will elaborate on how to add
complex fields to components.

1 5. 1 Ed i ti n g m ul ti v al u e pr o p er ty wi th m ul ti s el e cti o n fi el d
This section discusses how to add a field to a panel component that binds zero or more values to
one single property using a multiple selection field in the User Interface. As an example we define
a hobbies property in the form backing object which must be editable by a multiple selection
field containing 9 fixed possible values, as depicted below.

Figure 16. Example of a multi selection pulldown menu


The first step is to define the property in the Form backing object:
private String[] myHobbies = null;
public String[] getHobbies() {
return myHobbies;
}
public void setHobbies(String[] hobbies) {
myHobbies = hobbies;
}

We need to define what hobbies are available to select from. We define these statically also in
the Form backing object class:
public static String[] HOBBIES = new String[] {"Candlemaking", "Games",
"Gardening", "Collecting", "Juggling", "Photography", "Pottery",
"Scrapbooking", "Writing"};

The next step is to modify the JSP to display the checkboxes. Since the HOBBIES contained by the
Form backing object will not be accessible from the JSP and we did not define a getter for it, we
add this to the reference data in the panel controller.

e-mail info@gx.nl

http://www.gx.nl/

117/16
1

WCB DEVELOPMENT Tutorial

public Map<String, Object> referenceData(HttpServletRequest request, Object


command, Errors errors) throws Exception {
Map<String, Object> data = super.referenceData(request, command,
errors);
data.put("allhobbies", CustomTabFBO.HOBBIES);
return data;
}

The only thing left to do is to add the multiple selection fields in the edit JSP and bind them onto
the hobbies property. Since there is no standard JSP tag for doing this, we bind the value from
the selection field manually onto this property as follows:
<spring:bind path="command.hobbies">
<select name="${status.expression}" multiple>
<c:forEach var="hobby" items="${allhobbies}">
<option value="${hobby}" <c:if test="${wmfn:contains(hobby,
command.hobbies)}"> selected</c:if>> ${hobby}</option>
</c:forEach>
</select>
</spring:bind>

Note that since we use the wmfn tag library, we must explicitly import that library in the JSP:

<%@ taglib uri="http://www.gx.nl/taglib/functions" prefix="wmfn" %>

e-mail info@gx.nl

http://www.gx.nl/

118/16
1

WCB DEVELOPMENT Tutorial

1 5. 2 Ed i ti n g m ul ti v al u e pr o p er ty wi th m ul tipl e c h e c k b o x es
This section discusses how to add a field to a panel component that binds zero or more values to
one single property using a fixed set of checkboxes in the User Interface. As an example we
define a hobbies property in the form backing object which must be editable by checkboxes for
each of the 10 fixed possible values as depicted below.

Figure 17. Example of a multi checkbox selection


In the previous section we added the hobbies property to the Form backing object and wrote all
hobbies to the allhobbies attribute in the reference data. The only difference is that we now
want the hobbies property to use multiple checkboxes instead of one multiple selection field.
Again, there is no standard JSP tag for doing this so we bind the value from the checkboxes
manually onto this property as follows:
<spring:bind path="command.hobbies">
<c:forEach var="hobby" items="${allhobbies}">
<input type="hidden" name="_${status.expression}" value="visible" />
<input type="checkbox" name="${status.expression}" value="${hobby}"
<c:if test="${wmfn:contains(hobby, command.hobbies)}">
checked="checked"
</c:if>
/>
${hobby}<br/>
</c:forEach>
</spring:bind>

As a result on each submit setHobbies will be invoked with only those hobbies that were
selected.

e-mail info@gx.nl

http://www.gx.nl/

119/16
1

WCB DEVELOPMENT Tutorial

1 5. 3 Ed i ti n g m ul ti v al u e pr o p er ty wi th m ul ti s el e cti o n fi el d
a n d mu l ti pl e c h e c k b o x es
A combination of the previous two sections is the approach of using assign and delete. In this case
a multiple selection field is used to assign new values to the property. Below this selection field
the current values of the property are displayed and can removed by enabling the delete
checkbox displayed behind it. An example of this case appears below.

Figure 18. Example of a multiple add/remove field


For the implementation of this approach the same hobbies property of the Form backing object
and allhobbies attribute in the reference data are used (as described in the previous sections).
The only thing that must be changed is the JSP and the way we bind the posted form values onto
the hobbies property.
The first step is to create the multiple selection field from which values can be added to the
hobbies property. Also for this particular case, no standard JSP tag exists and we will have to do

the spring binding manually.


The values of the option fields in the selection field can simply be bound onto the hobbies
property directly. We must take care to filter out the values that have already been assigned. The
code snippet below shows how to bind the values from the multiple selection field onto the
hobbies property.
<spring:bind path="command.hobbies">
<select name="${status.expression}" multiple>
<c:forEach var="hobby" items="${allhobbies}">
<c:if test="${!wmfn:contains(hobby, command.hobbies)}">
<option value="${hobby}"> ${hobby}</option>
</c:if>
</c:forEach>
</select>
<wmedit:button type="submit" key="add"/>
</spring:bind>

Note: The HTML is stripped from this code snippet since it is not the intention of this section to
discuss how to properly render the selection field and corresponding submit button.

e-mail info@gx.nl

http://www.gx.nl/

120/16
1

WCB DEVELOPMENT Tutorial

The next step is to display the current values of the property and add the Delete checkboxes to
delete those values. This is a slightly more complicated process. The Spring binding mechanism
binds values posted from checkboxes only if the checkbox is enabled. Th is works fine if we want
to use the checkbox to assign values, but it is less suitable for this case because we want the
value to be removed if the checkbox is enabled. The way Spring binding deals with checkboxes
must be inverted, that is, values should only be bound onto the hobbies property if the checkbox
is not checked.
In order to do so an additional hidden input field has to be added containing the value to be set
on the hobbies property if the delete checkbox is not selected, like this:
<input type="hidden" name="${status.expression}" value="${hobby}"/>

Subsequently the onchange method of the checkbox must be overridden in order to disable this
hidden input field when the checkbox is selected. This prevents the field from being posted and
thus bound to the hobbies property by Spring. The following JavaScript function is added to
support this conditional disabling of the hidden input field:
<script type="text/javascript" language="javascript">
function disableCheckbox(id) {
var checkbox = document.getElementById(id);
if (checkbox.checked) {
checkbox.disabled=false;
}
else {
checkbox.disabled=true;
}
}
</script>

Finally, the code snippet below creates the hidden input fields for the current values of the
hobbies property and disables each of these values when the corresponding delete checkboxes

are selected.
<spring:bind path="command.hobbies">
<c:forEach var="hobby" items="${command.hobbies}" varStatus="loop">
<input type="hidden" id="${status.expression}_${loop.index}"
name="${status.expression}" value="${hobby}"/>
${hobby}
<input type="checkbox"
onchange="disableCheckbox('${status.expression}_${loop.index}');"/>
<fmt:message key="delete " /><br/>
</c:forEach>
</spring:bind>

e-mail info@gx.nl

http://www.gx.nl/

121/16
1

WCB DEVELOPMENT Tutorial

As a result, only those values are bound by Spring onto the hobbies property which are either
added by the multiple selection field or were already assigned and the corresponding delete
checkbox was not checked.
Note: This approach does not require any specific implementation of the controller or form
backing object.

1 5. 4 Ed i ti n g m ul ti v al u e pr o p er ty wi th si n g le s el e c ti on fi el d
a n d mu l ti pl e c h e c k b o x es
A slightly different variant from the approach described in the previous section i s to use a single
selection field for assigning new values to the hobbies property instead of a multiple selection
field. Although this may seem to be a trivial case of simply removing the multiple from the
selection form field, it is more complex than that. An example of this approach is depicted below:

Figure 19. Example of a single add/multiple remove field


One dummy value > Select a hobby is added to the selection field and this is exactly where
the problem occurs. Even an empty or null value of an option will be posted by the HTML form
and bound onto the hobbies property by Spring. This results in an additional undesired empty
value in the hobbies property each time the form is submitted with no real hobby selected.
To provide a workaround for this a function must be defined that is invoked just before submitting
the form. This function must check whether the value of this selection field is empty and if so
disable it so that the field value is not posted. In case of a panel, the JavaScript function would
look like this:
<script type="text/javascript" language="javascript">
function saveForm() {
var select = document.getElementById('empty_select');
if (select.value == '') {
select.disabled=true;
}
else {
select.disabled=false;
}
formobj = document.getElementById("saveform");
formobj.submit();
}
</script>

e-mail info@gx.nl

http://www.gx.nl/

122/16
1

WCB DEVELOPMENT Tutorial

The code snippet above assumes the empty dummy field of our selection field has the id
empty_select . The code snippet below shows how to render the single selection field:
<select name="${status.expression}">
<option id="empty_select" value="">&gt; Select a hobby</option>
<c:forEach var="hobby" items="${allhobbies}">
<c:if test="${!wmfn:contains(hobby, command.hobbies)}">
<option value="${hobby}"> ${hobby}</option>
</c:if>
</c:forEach>
</select>

e-mail info@gx.nl

http://www.gx.nl/

123/16
1

WCB DEVELOPMENT Tutorial

e-mail info@gx.nl

http://www.gx.nl/

124/16
1

WCB DEVELOPMENT Tutorial

16 CREATING AND USING A TESTBUNDLE


The GX WebManager test framework is based on JUnit 1. See the JUnit website for an introduction
to the framework and documentation (on for example the use of asserts). This chapter focus es
on the WebManager specific topics related to creating a testbundle.

1 6. 1 W h at i s a t est b u n d le?
A testbundle is an executable Java application that is used to test other parts of the application.
A WCB developer can use this framework to create testbundles for the WCBs the developer writes.
The big advantage of testbundles is that it is very easily incorporated into the build process. The
execution of the tests can be automated, which saves a lot of time testing the WCBs functions. It
also contributes to higher quality of the developed WCBs.

1 6. 2 C re a ti n g a t es tb u n dl e
Creating a blank testbundle is done based on an archetype, just like creating a basic element,
panel, etc. The commands below are based on the directory structure as created in the Quickstart
document.
To create a testbundle:

Open a DOS command window.

Go

to

the

directory

in

which

you

already

created

your

helloworld

WCBs

(C:\GX\helloWorldWCBs)

Enter the command (replace the <WebManager Version> with the version number of
WebManager that youre building against. E.g.: 9.7.0):
mvn archetype:generate -DinteractiveMode=false DarchetypeGroupId=nl.gx.webmanager.archetypes DarchetypeArtifactId=webmanager-testbundle-archetype DarchetypeVersion=<WebManager Version> DgroupId=com.gxwebmanager.helloworld DartifactId=helloworldtestbundle -Dclassprefix=HelloWorld -s
..\WebManager9\settings.xml

A new folder called helloworldtestbundle is created.

See: http://junit.sourceforge.net/

e-mail info@gx.nl

http://www.gx.nl/

125/16
1

WCB DEVELOPMENT Tutorial

1 6. 3 B ui l di n g a n d d e pl o yi n g t he t est b u n dl e
To build and deploy the testbundle:

Open a DOS command

Go to the folder C:\GX\helloWorldWCBs\helloworldtestbundle

Execute the command:


mvn -s ..\..\WebManager9\settings.xml clean package

Copy the file helloworldtestbundle-1.0.0.jar from the target directory to


C:\GX\WebManager9\work\deploy

1 6. 4 R u n ni n g t h e t est b u n dl e
The testbundle is now deployed in the GX WebManager release. The easiest way to run the test is
from the Tomcat console. In order to run this test from the console a few extra bundles need to
be present. Therefore, copy the following bundles to C:\GX\WebManager9\work\deploy:

webmanager-webapps\webmanager-backend-webapp\target\webmanager-backendwebapp-9.7.0\WEB-INF\bundles\org.apache.felix.shell.tui-1.0.0.jar

webmanager-webapps\webmanager-backend-webapp\target\webmanager-backendwebapp-9.7.0\WEB-INF\bundles\webmanager-testrunner-bundle-9.7.0.jar

webmanager-webapps\webmanager-backend-webapp\target\webmanager-backendwebapp-9.7.0\WEB-INF\bundles\webmanager-junit-bundle-9.7.0.jar

webmanager-webapps\webmanager-backend-webapp\target\webmanager-backendwebapp-9.7.0\WEB-INF\bundle\webmanager-felix-shellcommands-9.7.0.jar

After adding these jar-files to the deploy folder, enter the following command in Tomcat:
wmtestsuite ALL
This will result in the next lines added in the Tomcat log:

1 6. 5 A d di n g t e sts to t h e d e f au lt t e st b u n dl e
To add tests to the testbundle, only one file needs to be extended: CustomTest.java

e-mail info@gx.nl

http://www.gx.nl/

126/16
1

WCB DEVELOPMENT Tutorial

By default, CustomTest.java will look like this:


public class CustomTest extends TestCase {
private static final Logger LOG =
Logger.getLogger(CustomTest.class.getName());
protected void setUp() {
}
protected void tearDown() {
}
public void testScenarioOne() {
// Place your tests in this method
LOG.log(Level.SEVERE, "Congratulations! The tests in
'ScenarioOne' are running.");
}
}

The JUnit framework will process the methods in the following order:
1.

Run the setUp method;

2.

Run all methods that start with test - the order it will process these tests is not fixed;

3.

After running the tests, the method tearDown will be called.

1 6. 6 A c c essi n g t h e W e b M a n a g er A PI f r om th e t est b u n dl e
To access the GX WebManager APIs, the authorization needs to be set correctly. Typically the
authorization needs to be in place for all the tests that will be run from the testbundle, therefore
it is recommended that you make the WebManager session object available to all the tests by
using private class members.

e-mail info@gx.nl

http://www.gx.nl/

127/16
1

WCB DEVELOPMENT Tutorial

A testbundle that needs GX WebManager authorization will typically look like this:
public class CustomTest extends TestCase {
private static final Logger LOG =
Logger.getLogger(CustomTest.class.getName());
private static final String USERNAME = "Administrator";
private static final String PASSWORDCLEANBUILD = "Administrator";
private static final String PASSWORDAFTERLOGIN = "123456";
private static final String WEBID = "26098";
private Session mySession = null;
private ElementManagementService myElementService = null;
private PageManagementService myPageService = null;
private MediaRepositoryManagementService myMediaRepositoryService =null;

protected void setUp() {


initializeServices();
}
protected void tearDown() {
}
public void testScenarioOne() {
// Place your tests in this method
LOG.log(Level.SEVERE, "Congratulations! The tests in
'ScenarioOne' are running.");
}
private void initializeServices() {
// Contents of this method is presented on the next page
}
}

e-mail info@gx.nl

http://www.gx.nl/

128/16
1

WCB DEVELOPMENT Tutorial

private void initializeServices() {


try {
LOG.log(Level.INFO,
"Retrieving SessionManager from Service Framework");
Framework framework = FrameworkFactory.getInstance().getFramework();
SessionManager sessionManager = null;
AuthorizationService authorizationService = null;
ConfigurationManagement configService = null;
try {
sessionManager = (SessionManager)
framework.getService(SessionManager.class.getName());
authorizationService = (AuthorizationService)
framework.getService(AuthorizationService.class.getName());
configService = (ConfigurationManagement)
framework.getService(ConfigurationManagement.class.getName());
} catch (FrameworkException e) {
LOG.log(Level.SEVERE,
"Error in retrieving the SessionManager from the framework.", e);
}
String portnr =
configService.getParameter("website_settings.frontend_portnr");
String hostname =
configService.getParameter("website_settings.backend_hostname");
MockServletContext context = new MockServletContext();
MockHttpServletRequest request =
new MockHttpServletRequest(context);
request.addParameter("webid", WEBID);
request.setServerName(hostname);
request.setServerPort(Integer.parseInt(portnr));
MockHttpServletResponse response = new MockHttpServletResponse();
mySession = sessionManager.createSession(request, response);
if (!authorizationService.login(USERNAME,
PASSWORDCLEANBUILD, request)) {
LOG.severe("Login failed with username '" + USERNAME
+ "' and password '" + PASSWORDCLEANBUILD
+ "'. Another login attempt is made with password '"
+ PASSWORDAFTERLOGIN + "'.");
if (!authorizationService.login(USERNAME, PASSWORDAFTERLOGIN,
request)) {
LOG.severe("Second login attempt also failed.");
}
}
myElementService = mySession.getElementManagementService();
myPageService = mySession.getPageManagementService();
myMediaRepositoryService =
mySession.getMediaRepositoryManagementService();
} catch (ConfigurationManagementException e) {
LOG.severe(e.toString());
e.printStackTrace();
}
}

e-mail info@gx.nl

http://www.gx.nl/

129/16
1

WCB DEVELOPMENT Tutorial

1 6. 7 Usi n g t h e S pri n g m o c k fr a me w o r k
GX WebManager integrates the Spring mock classes to make it more convenient to write JUnit
testbundles. For example, this can be used to emulate a request and response.
More information about Spring mock can be found here:

http://www.springframework.org/docs/reference/testing.html

http://www.devx.com/Java/Article/30067

e-mail info@gx.nl

http://www.gx.nl/

130/16
1

WCB DEVELOPMENT Tutorial

17 SCHEDULED TASKS
1 7. 1 I ntr o d u cti o n
In some cases a WCB should perform a periodical background operation. Examples are import or
synchronization WCBs that automatically import/update content in WebManager from an external
source. This task should be performed at fixed, configurable, dates and times. CPU-consuming
tasks should be scheduled to run at night. This chapter describes how to write such a scheduled
task.

1 7. 2 Th e s ervi c e
A scheduled service is just a service component which invokes the schedule API. A service
component can be built from the service component archetype and an example of such an
implementation is described in chapter 9. Our basic service component implementation will look
like this:
public class CustomJobServiceImpl extends SimpleServiceComponent
implements CustomJobService {
public void onStart() {
}
public void onStop() {
}
public void run(SchedulerJob job) {
}
}

Note: The CustomJobService is the interface that identifies this service and is registered as
the interface classname in the Activator. The run method is the method that should be invoked
on each moment according to the time schedule. The onStart and onStop methods are invoked
on starting/stopping the WCB and are thus the most suitable locations to schedule/unscheduled
our task.

1 7. 3 Sc h e d ul e a t as k
To schedule tasks the SchedulerService can be used. This service provides the methods
necessary to retrieve scheduled jobs and to add or remove new jobs to the schedule. The
SchedulerService will invoke your service according to the schedule you provided.
To add a new job to the scheduler service, the method addSchedulerJob(SchedulerJob
schedulerJob) can be used. This method takes a SchedulerJob as input argument. An
instance of a default job can be retrieved

from the scheduler service by invoking

e-mail info@gx.nl

http://www.gx.nl/

131/16
1

WCB DEVELOPMENT Tutorial

getSchedulerJob(). This scheduler job subsequently contains methods to set the schedule,
the name of the job and the method to be invoked by the scheduler.
The job is usually scheduled in the onStart() method and removed in the onStop() method.
To cancel the job the removeSchedulerJob(String jobName) method with the name of the
job can be invoked. The code snippet below shows an example on how to schedule our job. Note
that the schedule itself is defined by a crontab expression. More information about the exact
syntax of this expression can be found on the internet or in the JavaDoc of the SchedulerJob.
public

class

CustomJobServiceImpl

extends

SimpleServiceComponent

implements CustomJobService {
// The scheduler service, injected by the framework
private SchedulerService mySchedulerService = null;
// Name of the job
private static String JOBNAME = WCBConstants.WCB_ID + custom job;
public void onStart() {
scheduleJob();
}
public void onStop() {
mySchedulerService.removeSchedulerJob(JOBNAME);

public void run(SchedulerJob job) {


}
private void scheduleJob() {
SchedulerJob job = mySchedulerService.getSchedulerJob();
job.setServiceInterfaceName(CustomJobService.class.getName());
job.setMethodName("run");
job.setName(JOBNAME);
job.setSchedule("0 0/15 * * * ?");
mySchedulerService.addSchedulerJob(job);
}
}

Note: Exception handling is omitted in this code example.


As a result of the code example above the run method will be invoked each 15 minutes.
Note that the name of the job is subject to guidelines since only one job can use a certain name.
Guideline G151 of the WCB Development Guidelines document therefore defines that this name
must be prefixed by the WCB id.

e-mail info@gx.nl

http://www.gx.nl/

132/16
1

WCB DEVELOPMENT Tutorial

1 7. 4 Se ssi o n m an a g e m e nt
If you implement a schedule task, one of the first issues you will face is session management. If
for example you try to create a media item using the MediaRepositoryManagementService
the service will throw a no authorization exception. See chapter 16 for more information on
session management.

1 7. 5 Sc h e d ul e u p d at es
Most likely the crontab schedule used to schedule the job must be configurable. To make the
crontab

expression

configurable,

the

Configuration

management

should

be

used.

The

scheduleJob method in the code example above should thus retrieve the crontab expression
from the Configuration Management service and use that as input argument.
If the crontab expression is managed using the Configuration Management service however, the
schedule can be changed. But in the code example above a change in the schedule will not affect
the already scheduled job, unless the WCB is restarted manually.
For that reason it is a good idea to register yourself on changes in this schedule and reschedule
the job if it is changed. For that purpose the Configuration Management service supports adding
and removing listeners:
void addListener(ConfigurationManagementListener listener,
String relativePath)
void removeListener(ConfigurationManagementListener listener);
The implementation of the ConfigurationManagementListener

can register itself on

changes in the configuration set that contains the schedule and reschedule the job on a change.

e-mail info@gx.nl

http://www.gx.nl/

133/16
1

WCB DEVELOPMENT Tutorial

1 7. 6 Sc h e d ul e d ta s ks i n a d u al m ast e r cl u ste r e d e nvir o n m e n t


In a clustered environment containing two GX WebManager master servers, only one instance of a
server can perform scheduled tasks. This server is referred to as the cluster task master. The
interface nl.gx.webmanager.services.frameworkconfig provides a service interface that allows
other services to access framework (runtime) configuration data from outside the service
framework. Any service accessing such data must do so through this service. The GX Webmanager
API provides a method to determine within a cluster node whether the current node is the cluster
task master. In a failover scenario this cluster node can change, so logic in a component like a
scheduled job should check periodically whether the node is the cluster task master.
The most important methods in the nl.gx.webmanager.services.frameworkconfig are:
Method

Description

getClusterNodeId()

Returns the cluster identifier for the node on which it is


running.

isClusterMasterNode()

Specifies whether the node on which it is running is a master


node.

isClusterTaskMaster()

Specifies whether this instance is responsible for executing


scheduled tasks that should only run on one server at a time.

isShuttingDown()

Indicates whether the Felix framework is shutting down.


Returns TRUE if the Felix framework is shutting down and FALSE
if not. This method must be used to differentiate between logic
that has to be executed on an explicit (WCB Management
Console, etc.) or implicit (server shutdown) WCB stop lifecycle
transition.

For an example of a scheduled job, check the updated scheduled job archetype where this
mechanism is implemented.

public void run(SchedulerJob job) {


// Check whether this instance is the cluster task master
if (!myFrameworkConfig.isClusterTaskMaster()) {
LOG.log(Level.INFO, "Execution of job '" + JOB_NAME + "' skipped on this
server.");
return;
}
...
}

e-mail info@gx.nl

http://www.gx.nl/

134/16
1

WCB DEVELOPMENT Tutorial

18 SESSION MANAGEMENT
1 8. 1 Se ssi o ns
In GX WebManager there are actually three different sessions that play a role:
Session interface

Description

nl.gx.webmanager.fou

WebManager specific session. Acts as a wrapper of WebManager

ndation.Session

specific context information and the JCR and HTTP sessions.

javax.jcr.Session

JCR session, needed to read and write from and to the JCR.

javax.servlet.http.H

Website visitor session (see Java API documentation)

ttpSession
Session Management is handled by the Session Manager service which only provides getters and
instantiate methods for the first session type; the nl.gx.webmanager.foundation.Session.
Since this is a wrapper around the other two sessions you always have access to all three sessions
if you have access to this session.
Note that one of the major purposes of the session is to define authorization. Authorization will
depend on the roles associated with the user stored in the session.

1 8. 2 Se ssi o n s ta c k
If an editor is working in the edit environment, the sessions are created automatically by the
WebManager framework. On each request WebManager identifies the user according to the cookie
sent together with the request and creates a new WebManager session. This WebManager session
is put on the top of the session stack. When the response is sen t back to the client this session
is removed from the stack. So this session exists during the complete lifetime of the request.
Within this lifetime a second session can be created, which is again put on top of the session
stack. Note that the component that created the session is also responsible for closing the
session. The purpose of using nested sessions is that actions performed within a specific session
can be undone separately. The picture below shows an example of a session stack:

Session 1 is created and closed automatically by the framework

WCB 2 creates its own session 2 and closes it afterwards

WCB 3 creates its own session 3 and invokes a service from WCB 4

The service in WCB 4 creates its own session 4 and closes it afterwards

The session 3 is closed by WCB 3

The framework automatically closes session 1

e-mail info@gx.nl

http://www.gx.nl/

135/16
1

WCB DEVELOPMENT Tutorial

Example of a session stack

1 8. 3 R et ri evi n g a n d cr e ati n g se ssi o ns


In some cases you can use an active session from the top of the stack created by another WCB or
by the framework. In that case, simply invoke SessionManager.getActiveSession() to
retrieve that active session. Note that you should never close this session yourself, since you are
not the one who created it. Leave this up to the creator of the session.
Active sessions will usually be available in element, panel and media item components since t he
controllers of these components are triggered by a WebManager editor who is logged in to
WebManager. In other use cases, like test bundles or scheduled jobs, however no a ctive session
will be available. Creating a session is not that straight forward as it may seem, since that
requires logon credentials.
To create a session with login credentials the best way is to define the login credentials in
configuration entries managed by the Configuration Management service. To create the session
those login credentials are used. The administrator can tune the authorization that is actually
needed by that particular user to its needs.
To

create

the

session

the

SessionManager.createSession(HttpServletRequest,

HttpServletResponse) method can be used. The HTTP request and response usually are not
available but you can use mock requests and responses instead, using the Spring mock module
(org.springframework.mock.web). Note that after logging in you should still invoke
AuthorizationService.login(username, password, request) to be granted the proper
authorization. The code example below shows an example of creating a session for user
USERNAMEKEY on webinitiatif with id WEBSITEKEY with password PASSWORDKEY.

e-mail info@gx.nl

http://www.gx.nl/

136/16
1

WCB DEVELOPMENT Tutorial

private Session login() {


try {
String portnr = myConfigService.getParameter(
"website_settings.frontend_portnr", null, "default");
String hostname = myConfigService.getParameter(
"website_settings.backend_hostname", null, "default");
String username = myConfigService.getParameter(USERNAMEKEY);
String password = myConfigService.getParameter(PASSWORDKEY);
String website = myConfigService.getParameter(WEBSITEKEY);
// Create mocks for the servlet request and response
MockServletContext context = new MockServletContext();
MockHttpServletRequest request =
new MockHttpServletRequest(context);
request.addParameter("webid", website);
request.setServerName(hostname);
request.setServerPort(Integer.parseInt(portnr));
MockHttpServletResponse response = new MockHttpServletResponse();
// Create a new session from this mock request
Session session = mySessionManager.createSession(
request, response);
request.setAttribute(Session.WEBMANAGER_SESSION_KEY, session);
// Login using the authorization service
if (!myAuthorizationService.login(username, password, request)) {
LOG.warning("Login failed.");
return null;
}
return session;
} catch (ConfigurationManagementException e) {
LOG.log(Level.SEVERE, "An exception occurred during login()", e);
return null;
}
}

e-mail info@gx.nl

http://www.gx.nl/

137/16
1

WCB DEVELOPMENT Tutorial

e-mail info@gx.nl

http://www.gx.nl/

138/16
1

WCB DEVELOPMENT Tutorial

19 VARIOUS TOPICS
The topics in this section contain useful information for developers who work with WebManager.

1 9. 1 Pr o p erty E di t o rs
Property Editors are used to facilitate conversions between a complex type and String. Posted
values from a HTML form will usually be converted to a String by the servlet engine. Spring MVC
offers a way to convert incoming Strings to complex types; it also converts complex types to
Strings when the response is sent back to the browser. Spring MVC offers this functionality
through the Property Editors. The Spring MVC and the GX WebManager platforms already have a
few default Property Editors for the next types:

All primitive types plus their autoboxed equivalents;

Date (in the format of dd-MM-yyyy or dd/MM/yyyy).

To add a custom Property Editor:


1.

Implement a custom Property Editor;

2.

Register the custom Property Editor in the initBinder method.

In the following sections an example of a custom Property Editor is shown based on the Calendar
type. By default, there is no support for a property of type Calendar although it might be o f
interest for WCB developers.

e-mail info@gx.nl

http://www.gx.nl/

139/16
1

WCB DEVELOPMENT Tutorial

19.1.1 Implement the custom Property Editor


To implement a custom Property Editor, a class is needed that:

Implements the PropertyEditor interface; usually this is done by extending the


PropertyEditorSupport class;

Implements the methods setAsText and getAsText.

The implementation of the CustomCalendarEditor looks like this:


public class CustomCalendarEditor extends PropertyEditorSupport {
private final DateFormat myDateFormat;
private final static Logger LOG =
Logger.getLogger(CustomCalendarEditor.class.getName());
public CustomCalendarEditor(DateFormat dateFormat) {
myDateFormat = dateFormat;
}
public void setAsText(String text) {
if (text == null) {
setValue(null);
} else {
try {
myDateFormat.parse(text);
setValue(myDateFormat.getCalendar());
} catch (ParseException ex) {
LOG.log(Level.WARNING,
"Could not parse the date '" + text + "'" +
"\nThe exception\n: " + ex);
}
}
}
public String getAsText() {
Calendar value = (Calendar) getValue();
if (value == null) {
return "";
} else {
return myDateFormat.format(value.getTime());
}
}
}

e-mail info@gx.nl

http://www.gx.nl/

140/16
1

WCB DEVELOPMENT Tutorial

19.1.2 Register the custom Property Editor


Now that the custom Property Editor is implemented, it has to be registered with Spring MVC.
This is done in the initBinder method; this method is placed in the controller of your
component (e.g. CustomElementController.java). The registration looks like this:
@Override
public void initBinder(HttpServletRequest request,
ServletRequestDataBinder binder) throws Exception {
super.initBinder(request, binder);
if (myValidator == null) {
myValidator = new CustomCalendarEditor(
new SimpleDateFormat("dd/MM/yyyy");
addValidator(myValidator);
}
}

19.1.3 The custom Property Editor in action


Now that the custom Property Editor is created and registered, it is ready to use. This means that
it is now possible to use the Calendar type for properties in the Java code. Below are some code
snippets that illustrate the use of the custom the Calendar type Property Editor that was created
in the above example:
Code in the edit-JSP (e.g. editCustomElement.jspf):
<fmt:message key="helloworldelement.inputfieldLabel.birthdate" />:
<wmedit:datePicker path="birthdate" />

Code in the FormBacking Object (e.g. CustomElementFBO.java):


private Calendar myBirthdate;
public Calendar getBirthdate() {
return myBirthdate;
}
public void setBirthdate(Calendar bdate) {
myBirthdate = bdate;
}

The result in GX WebManager:

Figure 20. Example of a date field in GX WebManager

e-mail info@gx.nl

http://www.gx.nl/

141/16
1

WCB DEVELOPMENT Tutorial

1 9. 2 V al i d at o rs
Validators can be used to validate user input and to generate client side error messages when
invalid input is provided by a user. Validators will prevent users from entering invalid data.
Validators are Java classes that implement the org.springframework.validation.Validator
interface. This interface provides two methods:
boolean supports(Class clazz);
void validate(Object target, Errors errors);

The supports method indicates which classes the validators can handle. The validate method
performs the actual validation. Note that the Sprint MVC framework provides API methods that
make

it

easy

to

perform

this

validation

like
and

org.springframework.validation.ValidationUtils
org.springframework.validation.Errors .

To register the validator it must be added in the initBinder method of the controller using the
addValidator method. Note that you should only add the validator once per lifetime of the

controller instance. So if you have a stateful controller (which is the default), be sure to
instantiate and register the validator only once. If you unconditionally add th e validator in the
initBinder method, the validator will be invoked twice on the second HTTP request, three times

on the third, etc.


The code snippets below provide an example of using a text validator for a custom element that
rejects any empty text value or value that equals not empty.
In the element component controller:
public void initBinder(HttpServletRequest request, ServletRequestDataBinder
binder) throws Exception {
if (myValidator == null) {
myValidator = new TextValidator();
addValidator(myValidator);
}
}

The validator class:


public class TextValidator implements Validator {
public void validate(Object target, Errors errors) {
CustomTextElementFBO element = (CustomTextElementFBO) target;
ValidationUtils.rejectIfEmpty(errors, "text", "maynotbeempty");
if (element.getText() != null
&& element.getText().equals("not empty")) {
errors.rejectValue("text", "maynotbenotempty");
}
}
public boolean supports(Class clazz) {
if (CustomElementFBO.class.isAssignableFrom(clazz)) {
return true;
}
else {
return false;
}
}
}

e-mail info@gx.nl

http://www.gx.nl/

142/16
1

WCB DEVELOPMENT Tutorial

In messages_en_US.properties :
maynotbeempty=Text may not be empty
maynotbenotempty=Text may not be not empty

As a result the message Text may not be empty will be displayed if the user input was
invalid:

1 9. 3 HT TP C l i e nt
The java.net package part of the Java 1.6 API provides a basic set of classes which can be used
to handle HTTP GET and POST requests. However, in some cases a more powerful HTTP client API
is needed. The commons HTTP client (org.apache.commons.httpclient.HttpClient)

for

example may be a better alternative for those cases. This section describes how to use the
commons HTTP client in a WCB.
To incorporate the commons http client jar files into a WCB, modify the pom.xml file to define a
dependency with the commons http client artifact. For example, define this dependency:
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.0</version>
</dependency>

Without further changes however, the WCB will throw a runtime error upon invocation of the
HTTP client because of a conflict in commons logging:
Invalid class loader hierarchy. You have more than one version of
'org.apache.commons.logging.Log' visible, which is not allowed.

The reason for this is that the dependency above will cause the commons-logging artifact to be
included in the WCB since it is required by the HTTP client artifact. However, the GX WebM anager
framework also exports the commons logging package which causes the mismatch.
To resolve this issue an additional dependency with the commons -logging artifact must be defined
with a scope provided so that at runtime the commons-logging classes exported by the framework
will be used instead:
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.0.4</version>
<scope>provided</scope>
</dependency>

e-mail info@gx.nl

http://www.gx.nl/

143/16
1

WCB DEVELOPMENT Tutorial

GX WebManager offers standard HTTP proxy configuration settings. These settings are configured
using the GX WebManager Setup Tool. The settings are made available through the default system
networking properties. For complete information about these properties, go to the URL
http://java.sun.com/j2se/1.4.2/docs/guide/net/properties.html . The example below shows how
you can apply these settings in GX WebManager in combination with HTTP clients version 2 and 3.

private static void configureHttpClient2Proxy(HttpClient httpClient,


HttpMethod method) {
String proxyHost = System.getProperty("http.proxyHost");
if (proxyHost == null || "".equals(proxyHost)) {
return;
}
try {
org.apache.commons.httpclient.URI apacheUri = method.getURI();
java.net.URI javaUri = null;
if (apacheUri.isAbsoluteURI()==false) {
javaUri = new
java.net.URI(httpClient.getHostConfiguration().getHostURL());
} else {
javaUri = new java.net.URI(apacheUri.toString());
}
// ProxySelector
List<Proxy> selectedProxy =
ProxySelector.getDefault().select(javaUri);
if (selectedProxy.size()==0 ||
selectedProxy.get(0).type()==Proxy.Type.DIRECT) {
// No proxy needed.
return;
}
HostConfiguration hc = httpClient.getHostConfiguration();
hc.setHost(javaUri.getHost(), javaUri.getPort(),
Protocol.getProtocol(javaUri.getScheme()));
Proxy proxy = selectedProxy.get(0);
InetSocketAddress proxyAddress = (InetSocketAddress) proxy.address();
hc.setProxy(proxyAddress.getHostName(),proxyAddress.getPort());
if (System.getProperty("http.proxyUserName") != null) {
httpClient.getState().setProxyCredentials(null, null,
new
UsernamePasswordCredentials(System.getProperty("http.proxyUserName"),
System.getProperty("http.proxyPassword")));
httpClient.getState().setAuthenticationPreemptive(true);
}
} catch (URISyntaxException ex) {
LOG.log(Level.WARNING, null, ex);
} catch (URIException ex) {
LOG.log(Level.WARNING, null, ex);
}
}

e-mail info@gx.nl

http://www.gx.nl/

144/16
1

WCB DEVELOPMENT Tutorial

1 9. 4 A d di n g C us t om I n d ic at or s t o th e P er f or m a n c e D as h b o ar d
The WebManager Performance Dashboard provides detailed information about how the parts of
WebManager's infrastructure are functioning. The Performance Dashboard measures the speed and
response time of page requests and internal queries as well as other settings that affect how
WebManager is performing and rates the results according to the optimal expected results. The
Performance Dashboard WCB is extensible, which allows you to create WCBs that add custom
performance indicators. Any custom indicators you create appear on a special tab named Custom
System Performance Indicators (see the figure on the following page).
Use the methods in the following classes to implement your custom performance indicators:

SystemHealthIndicator

SystemHealthIndicator.ValueStatus

Notes:

The

indicators are called System health indicators instead of Performance

indicators. The reason for this is that although the current focus is on implementing
performance indicators, GX WebManager intends to support more generic system health
indicators in the future.

For complete information on the methods in these classes, see the WebManager API
Javadoc, available from http://connect.gxsoftware.com/javadoc/webmanager9/. For
complete information about WCB extensibility, see section 12 of this document
(Extensibility).

e-mail info@gx.nl

http://www.gx.nl/

145/16
1

WCB DEVELOPMENT Tutorial

The following sample adds a category named My Category containing an indicator named test
custom PI to the Custom System Performance Indicators tab.
public class CustomServiceImpl extends SimpleServiceComponent implements
SystemHealthIndicatorExtensionPoint {
// Private logger for this class
private static final Logger LOG =
Logger.getLogger(CustomServiceImpl.class.getName());
public SystemHealthIndicator[] getPerformanceIndicators() {
return new SystemHealthIndicator[] {new CustomPI()};
}
class CustomPI implements SystemHealthIndicator{
public String getId() {return "testPI";};
public String getCategory() {return "my category";}
public String getType() {return TYPE_CUSTOM;};
public String getName(Language language) {return "test custom PI";}
public Object getValue() {return myCache.getCacheRatio;}
public ValueStatus getValueState() {return myValueStatus;}
public String getMessage(Language language) {return "test custom PI";}
public void reset() {}
}
}

The Performance Dashboard WCB extension as seen in GX WebManager:

Figure 21. Custom performance indicator

e-mail info@gx.nl

http://www.gx.nl/

146/16
1

WCB DEVELOPMENT Tutorial

1 9. 5 C re a ti n g a n e xt e ns io n f or th e Us er Pr o fil es co m p o n e nt
The GX WebManager license component User Profiles can be extended via the API in order to
add custom fields and tabs to the default user profile information. In addition to adding
extensions to the user profile information, you can also manage users, groups, newsletter
subscriptions, permissions, and banned and reserved words via the GX WebManager API. This
makes it possible to enhance the functionality contained within the user profile component for
your particular implementation. All data in custom fields and tabs can also be exported together
with data in the default user detail fields using the provided export functionality in the User
Profiles component.
The packages containing the interfaces that manage users, groups, and user profile settings are:

nl.gx.webmanager.services.defaultprofileprovider
Contains the interfaces for managing user profile data in the default user profile as
provided by GX WebManager.

nl.gx.product.wmpuserprofiles.api
Contains the interfaces for managing custom sub-tabs and permissions for the User
Profiles component.

nl.gx.webmanager.services.usermanager
Contains the interfaces for managing users, groups, and newsletter subscriptions.

nl.gx.webmanager.services.usermanager.settings
Contains one interface for managing reserved and banned words.

e-mail info@gx.nl

http://www.gx.nl/

147/16
1

WCB DEVELOPMENT Tutorial

19.5.1 Generating the sample profile extension WCB from the user profiles
archetype
Included with GX WebManager is a sample WCB that extends the user profiles functionality by
adding a field to the [User Details] tab (view extension) as well as a new tab named [MSN Details]
with one field. By generating a sample profile extension WCB from the archetype, you can see a
working example of how extra fields and tabs can be added to the default user profile as well as
how the extra data can be exported.
To generate the sample WCB, execute the following command from a command prompt. In the
example below, the command is issued from the root of the GX WebManager installation (where
the settings.xml file is located). You can also execute this command from a different location as
long as you provide the absolute path to the settings.xml file.

mvn -s settings.xml archetype:generate -DinteractiveMode=false DarchetypeGroupId=nl.gx.webmanager.archetypes -DarchetypeArtifactId=webmanagerprofileprovider-archetype -DarchetypeVersion=9.x.x -DgroupId=nl.gx.product DartifactId=customprofile -DpackageName=nl.gx.product.customprofile Dclassprefix=Custom

where 9.x.x is the version of GX WebManager you are using. Note: The User Profiles archetype
is available in GX WebManager versions 9.8.0 and later.

e-mail info@gx.nl

http://www.gx.nl/

148/16
1

WCB DEVELOPMENT Tutorial

As a result, the following folder structure is created containing the source code for the
customprofile sample WCB:

e-mail info@gx.nl

http://www.gx.nl/

149/16
1

WCB DEVELOPMENT Tutorial

19.5.2 UserManagement interface methods


In order to create a custom extension for the user profiles functionality, you must use the
following GX WebManager API methods to implement the Profile Extension Provider interface. The
following code is taken from the file ProfileExtensionProvider.java:
/**
* Call back method that will be invoked by the framework when the
* given {@link User} is created.
* @param user the {@link User} instance that just has been created
*/
void onCreate(User user);
/**
* Call back method that will be invoked by the framework when the
* given {@link User} is deleted.
*
* @param user the {@link User} instance that just has been deleted
*/
void onDelete(User user);
/**
* Gets the profile for the given user. If the profile part exists,
* then it is returned, otherwise a default profile part is
* returned which can be modified and updated.
*
* @param user the {@link User} to get the profile for
* @return the user's profile
* @throws UserManagementException on error
*/
T getProfileFor(User user) throws UserManagementException;
/**
* Updates the given profile.
*
* @param profile the profile to update
* @throws UserManagementException on error
*/
void update(T profile) throws UserManagementException;

e-mail info@gx.nl

http://www.gx.nl/

150/16
1

WCB DEVELOPMENT Tutorial

The profile extension provider contains the following:

Callback methods that create and delete users

Methods to retrieve and update user profiles

Methods for exporting user profiles

/**
* This method should return a constant list of strings which are
the column headers of the values that
* are exported by this profile extension.
*
* @return the headers of the fields that are present in an export
*/
List<String> getExportColumnHeaders();
/**
* This method should return a list of values whose size is equal to
* the size of the list returned by
* {@link #getExportColumnHeaders()}. Each entry in the returned
* list is a list of strings and represents
* a single profile property. Single-valued profile properties are
* to be represented by a list of length
* 1. Multi-valued profile properties can be represented by a list
* of appropriate size.
*
* @param user the user whose profile to export
* @return the values for the exportable fields of the given profile
* @throws UserManagementException on error
*/
List<List<String>> export(User user) throws UserManagementException;
}

Add your own methods that implement this interface. In the file
src\main\java\nl\gx\product\customprofile\profileprovider\CustomProfileProviderImpl.java, the
following code makes the profile provider available to the framework:
public final class CustomProfileProviderImpl extends
SimpleProfileProviderComponent implements
CustomProfileProvider {

e-mail info@gx.nl

http://www.gx.nl/

151/16
1

WCB DEVELOPMENT Tutorial

19.5.3 Custom profile declarations


In the file src\main\java\nl\gx\product\customprofile\profileprovider\CustomProfileImpl.java, the
following code implements the custom profile and initializes the new fields:
customprfileimpl - extended profile - declaration:
public final class CustomProfileImpl implements CustomProfile {
private final User myUser;
private String myMsnAddress;
private String myIcqAddress;
/**
* @param usr The owner of this profile part
*/
public CustomProfileImpl(User usr) {
myUser = usr;
String userString = "";
if (usr != null) {
userString = "" + usr.getId();
}
myMsnAddress = userString + "msn@hotmail.com";
myIcqAddress = userString + "icq@hotmail.com";

e-mail info@gx.nl

http://www.gx.nl/

152/16
1

WCB DEVELOPMENT Tutorial

The following code sets and retrieves the data in the new custom fields:

/**
* {@inheritDoc}
*/
public void setMsnAddress(String newMsnAddress) {
myMsnAddress = newMsnAddress;
}
/**
* {@inheritDoc}
*/
public String getMsnAddress() {
return myMsnAddress;
}
/**
* {@inheritDoc}
*/
public void setIcqAddress(String newIcqAddress) {
myIcqAddress = newIcqAddress;
}
/**
* {@inheritDoc}
*/
public String getIcqAddress() {
return myIcqAddress;

e-mail info@gx.nl

http://www.gx.nl/

153/16
1

WCB DEVELOPMENT Tutorial

19.5.4 Adding a custom tab to the User Profiles component


The following code shows how to add a custom tab to User Profiles component. The code is
taken from the file src\main\java\nl\gx\product\customprofile\subtab\CustomSubTab.java:

public final Class<?> getDelegatedControllerClass() {


return CustomSubTabController.class;
}
/**
* {@inheritDoc}
*/
public final String getLabelId() {
return "nl.gx.product.customprofile.subtab.msntitle";
}
/**
* {@inheritDoc}
* Note: You must implement your own jspf in order for the presentation
* to be rendered.
*/
public final String getEditPresentation() {
return "customprofilesubtab.jspf";

/**
* {@inheritDoc}
*/
public final void initialize(ComponentController controller,
FormBackingObject fbo,
HttpServletRequest request, String tabId) {
fbo.getNestedPath();
CustomSubTabFBO concreteFBO = (CustomSubTabFBO) fbo;
// Set the nested path
concreteFBO.setTabId(tabId);

e-mail info@gx.nl

http://www.gx.nl/

154/16
1

WCB DEVELOPMENT Tutorial

// Store the selected user in the form backing object


ExtensiblePanelTabParentFBO parentFBO;
try {
parentFBO = (ExtensiblePanelTabParentFBO)
controller.getParentController().formBackingObject(request);
concreteFBO.setSelectedUser(parentFBO.getSelectedUser());
concreteFBO.isNewUserMode(parentFBO.isNewUserMode());
} catch (ServletException e) {
LOG.log(Level.SEVERE, "Failed to retrieve parent fbo.", e);
}
// Store reference to services
if (controller instanceof ExtensionSubTabController) {
ExtensionSubTabController extensionSubTabController =
((ExtensionSubTabController) controller);
extensionSubTabController.setSessionManager(mySessionManager);
extensionSubTabController.setRealmManager(myRealmManager);
}
}
/**
* {@inheritDoc}
*/
public final int getRank() {
return MY_RANK;
}
}

Note: If you add more than one sub-tab, the order that they appear from left to right is
controlled by the custom tabs rank. The sub-tab with the lowest rank value appears first and the
rest follow in order from left to right. For example:
public class CustomSubTab extends SimpleServiceComponent implements
ExtensionSubTab {
private static final int MY_RANK = 10;

e-mail info@gx.nl

http://www.gx.nl/

155/16
1

WCB DEVELOPMENT Tutorial

The following code, taken from the file


src\main\java\nl\gx\product\customprofile\viewextension\impl\CustomSubTabFBO.java, shows how
to extend the panel tabs form backing object to include the new field:

package nl.gx.product.customprofile.subtab;
import nl.gx.product.wmpuserprofiles.api.ExtensionSubTabFBO;
/**
* The FBO for the customprofile sub tab.
*/
public class CustomSubTabFBO extends ExtensionSubTabFBO {
private String myMsnAddress;
/**
* Returns the msn address.
*
* @return the msn address
*/
public String getMsnAddress() {
return myMsnAddress;
}
/**
* Sets the msn address.
*
* @param msnAddress the msn address
*/
public void setMsnAddress(String msnAddress) {
this.myMsnAddress = msnAddress;
}
}

e-mail info@gx.nl

http://www.gx.nl/

156/16
1

WCB DEVELOPMENT Tutorial

The following code, taken from the file


src\main\resources\editpresentation\customprofilesubtab.jspf, implements the rendering of the
new field:
<%@ page language="java" session="false" buffer="none" %>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<%@ taglib prefix="spring"
uri="http://www.springframework.org/taglib/spring" %>
<%@ taglib prefix="wmedit" uri="http://www.gx.nl/taglib/wmedit" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>
<fieldset>
<div class="wm_style">
<table class="widgetgrid">
<tr>
<td class="label_left"><fmt:message
key="nl.gx.product.customprofile.subtab.msnaddress"/>:</td>
<td class="datefield"><wmedit:input path="msnAddress"
size="25" /></td>
</tr>
</table>
</div>
</fieldset>

After implementing a controller for the tab in same way described in this docu ment for panels,
elements, etc., the result in the Edit environment in GX WebManager is the following:

e-mail info@gx.nl

http://www.gx.nl/

157/16
1

WCB DEVELOPMENT Tutorial

19.5.5 Adding a custom field to the user details tab


Custom data fields can be added to the [User Details] tab in order to extend the detailed
information you want to store for each user. The following sample code, taken from the file
src\main\java\nl\gx\product\customprofile\profileprovider\impl\CustomProfileImpl.java,

shows

how to add a new field, ICQ Address to the [User Details] tab. Note: The same custom profile is
used for the custom tab extension and extending the [User Details] tab.

/**
* A custom profile extension which adds a single text field to the
profile.
*/
public final class CustomProfileImpl implements CustomProfile {
private final User myUser;
private String myMsnAddress;
private String myIcqAddress;
/**
* @param usr the owner of this profile part
*/
public CustomProfileImpl(User usr) {
myUser = usr;
String userString = "";
if (usr != null) {
userString = "" + usr.getId();
}
myMsnAddress = userString + "msn@hotmail.com";
myIcqAddress = userString + "icq@hotmail.com";
}
/**
* {@inheritDoc}
*/
public String getIcqAddress() {
return myIcqAddress;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "CustomProfile[User = " + myUser + ", MSN address =
" + myMsnAddress + ", ICQ address = "
+ myIcqAddress + "]";
}
}

e-mail info@gx.nl

http://www.gx.nl/

158/16
1

WCB DEVELOPMENT Tutorial

The following code, taken from the file


src\main\java\nl\gx\product\customprofile\viewextension\impl\CustomFBO.java, shows how to
extend the panel tabs form backing object to include the new field:

/**
* Class customprofileFBO.
*
* Form backing object of the customprofileController
*/
public class CustomFBO extends PanelTabFBO implements
FormBackingObject {
private String myIcqAddress;
/**
* {@inheritDoc}
*/
public final String getUUID() {
return
WCBConstants.USER_PROFILES_PROFILEDETAILS_EXTENSION_PATH;
}
/**
* Returns the icq address.
*
* @return the icq address
*/
public String getIcqAddress() {
return myIcqAddress;
}
/**
* Set the icq address.
*
* @param icqAddress the icq address
*/
public void setMsnAddress(String icqAddress) {
this.myIcqAddress = icqAddress;
}
}

e-mail info@gx.nl

http://www.gx.nl/

159/16
1

WCB DEVELOPMENT Tutorial

The following code, taken from the file


src\main\resources\editpresentation\customprofileview.jspf, implements the rendering of the new
field:

%@ page language="java" session="false" buffer="none" %>


<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<%@ taglib prefix="spring"
uri="http://www.springframework.org/taglib/spring" %>
<%@ taglib prefix="wmedit" uri="http://www.gx.nl/taglib/wmedit" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>
<tr>
<td class="label_left"><fmt:message
key="nl.gx.product.customprofile.profileview.icqaddress"/>:</td>
<td class="datefield"><wmedit:input path="icqAddress" size="20" /></td>
</tr>

After implementing a controller for the tab in same way described in this document for panels,
elements, etc., the result in the Edit environment in GX WebManager is the following:

e-mail info@gx.nl

http://www.gx.nl/

160/16
1

WCB DEVELOPMENT Tutorial

19.5.6 Exporting data from custom fields


In order for GX WebManager to be able to export data from custom fields, you must declare the
new headers that need to be retrieved during the export. For example:

public List<String> getExportColumnHeaders() {


return Collections.unmodifiableList(Arrays.asList(new
String[]{"msn address", "icq address"}));
}

The following code passes the values retrieved from the custom fields to the export function:

public List<List<String>> export(User user) throws


UserManagementException {
return
Collections.unmodifiableList(Arrays.asList((List<String>[]) new
List[]{
Collections.singletonList(getProfileFor(user).getMsnAddress()),
Collections.singletonList(getProfileFor(user).getIcqAddress())

This implementation comes from the CustomProfileProviderImpl interface. The methods are part
of the ProfileExtensionProvider interface.

e-mail info@gx.nl

http://www.gx.nl/

161/16
1

Вам также может понравиться