Академический Документы
Профессиональный Документы
Культура Документы
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
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
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
VERSION CONTROL
Version
Date
1.0
1.01
1.02
1.10
April 1, 2008
1.20
1.21
lifecycle)
1.23
1.24
1.25
1.26
1.30
1.31
Formatting
1.50
1.51
1.52
1.60
April 7, 2011
1.70
appropriate
e-mail info@gx.nl
http://www.gx.nl/
4/161
TABLE OF CONTENTS
1
Introduction ............................................................................................... 11
1.1
1.2
1.3
1.4
1.5
2
2.1
2.2
2.3
2.1.2
2.1.3
2.1.4
2.1.5
2.1.6
Result ............................................................................................ 19
2.2.2
2.2.3
2.2.4
Result ............................................................................................ 21
2.3.2
2.3.3
Result ............................................................................................ 24
3.1
Introduction............................................................................................. 29
3.2
3.3
3.4
3.5
4
4.1
e-mail info@gx.nl
http://www.gx.nl/
5/161
4.2
4.3
4.4
4.5
Controller ............................................................................................... 34
4.6
4.7
OSGi ..................................................................................................... 36
4.8
4.9
WCBs .................................................................................................... 37
4.10
4.11
4.12
Licensing .................................................................................................. 39
5.1
5.2
Authorization ............................................................................................. 41
6.1
6.2
6.3
Permissions ............................................................................................. 42
6.4
6.5
6.6
7.1
7.2
7.3
7.2.1
7.2.2
installBundle() ......................................................................................... 48
update() ................................................................................................ 48
start() ................................................................................................... 48
stop() .................................................................................................... 48
uninstall() .............................................................................................. 49
purge() .................................................................................................. 49
7.3.2
isShuttingDown() ...................................................................................... 49
7.4
7.3.3
7.3.4
e-mail info@gx.nl
http://www.gx.nl/
6/161
7.5
7.6
Service dependencies.................................................................................. 56
7.7
8.1
Introduction............................................................................................. 59
8.1.1
8.1.2
8.1.3
8.1.4
8.1.5
Migration ................................................................................................... 65
9.1
Introduction............................................................................................. 65
9.2
9.3
9.4
9.4.2
Example .......................................................................................... 67
10
10.1
10.1.1
10.1.2
10.1.3
10.2
10.2.1
10.2.2
10.2.3
10.2.5
10.2.6
11
Online help.............................................................................................. 79
11.1
11.2
11.3
11.3.1
11.3.2
11.3.3
e-mail info@gx.nl
http://www.gx.nl/
7/161
11.3.4
11.4
11.5
11.6
12
12.1
12.2
13
13.1
Introduction ......................................................................................... 91
13.2
13.3
13.4
13.5
13.6
13.7
13.7.1
@Property .................................................................................... 98
13.7.2
@Child ........................................................................................ 98
13.7.3
@Collection .................................................................................. 99
13.8
13.8.1
13.8.2
13.8.3
13.8.4
13.9
14
14.1
14.1.1
14.1.2
14.2
14.2.1
14.2.2
14.3
14.3.1
14.3.2
14.3.3
14.3.4
14.4
14.4.1
e-mail info@gx.nl
http://www.gx.nl/
8/161
14.4.2
14.4.3
15
15.1
Editing multi value property with multi selection field ...................................... 117
15.2
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
16.1
16.2
16.3
16.4
16.5
16.6
16.7
17
17.1
17.2
17.3
17.4
17.5
17.6
18
18.1
18.2
18.3
19
19.1
19.1.1
19.1.2
19.1.3
19.2
19.3
19.4
19.5
19.5.1
Generating the sample profile extension WCB from the user profiles archetype . 148
19.5.2
e-mail info@gx.nl
http://www.gx.nl/
9/161
19.5.3
19.5.4
19.5.5
19.5.6
e-mail info@gx.nl
http://www.gx.nl/
10/161
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
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
Description
Element
Panel
MediaItem
Page metadata
Form
With this component type, new Handlers, Validators and Routers can be
added to the Advanced Forms module.
Presentation
Service
Servlet
e-mail info@gx.nl
http://www.gx.nl/
12/161
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
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
1. 5
F urt h e r r e a di n g
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,
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
e-mail info@gx.nl
http://www.gx.nl/
16/161
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
CustomElementController.java
CustomElementFBO.java
CustomElementImpl.java
editCustomElement.jspf
messages_en_US.properties
messages_nl_NL.properties
showCustomElement.xml
showCustomElement.jspf
smallCustomElementIcon.gif
largeCustomElementIcon.gif
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.
2.
3.
Add a label to the language files to translate Author in the supported languages.
4.
5.
e-mail info@gx.nl
http://www.gx.nl/
17/161
2.1.1
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
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
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
2.1.4
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
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
e-mail info@gx.nl
http://www.gx.nl/
19/161
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
CustomPanel.java
CustomTabController.java
CustomTabFBO.java
customTab.jspf
messages_en_US.properties
messages_nl_NL.properties
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.
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
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
2.2.2
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
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
e-mail info@gx.nl
http://www.gx.nl/
21/161
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
CustomMediaItemVersion.java
CustomMediaItemVersionFBO.java
CustomMediaItemVersionImpl.java
editMetadata.xml
editMetadata.jspf
messages_en_US.properties
messages_nl_NL.properties
custommediaitem.gif
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.
2.
3.
Add a label to the language files to translate Author in the supported language s.
4.
e-mail info@gx.nl
http://www.gx.nl/
22/161
2.3.1
2.3.2
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
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
2.3.4
Result
Figure 3. Media item with an additional Author field in the custom metadata
e-mail info@gx.nl
http://www.gx.nl/
24/161
2. 4
C re a ti n g s u bm e n us
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.
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.
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
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;
}
e-mail info@gx.nl
http://www.gx.nl/
26/161
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);
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);
definition.setSubMenuLocationAfter(WCBConstants.ELEMENT_LIKE_COMPONENT_ID.replaceAll(" \\."
, ""));
190
definition.setSubMenuLocationBefore(WCBConstants.ELEMENT_LIKEBOX_COMPONENT_ID.replaceAll("
\\.", ""));
...
e-mail info@gx.nl
http://www.gx.nl/
27/161
setGeneralDefinitionPropertiesAndPermissions(definition,
RecommendationsElementComponent.class.getName(),
RecommendationsElementImpl.class.getName(),
RecommendationsElementController.class.getName(),
WCBConstants.ELEMENT_RECOMMENDATIONS_COMPONENT_ID,
WCBConstants.ELEMENT_RECOMMENDATIONS_COMPONENT_NAME,
RecommendationsElement.class);
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");
e-mail info@gx.nl
http://www.gx.nl/
28/161
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.
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
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
3.2.1
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.
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
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
setProperties
since
this
is
the
servlet
that
e-mail info@gx.nl
http://www.gx.nl/
31/161
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
4. 1
MV C
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
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
e-mail info@gx.nl
http://www.gx.nl/
35/161
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
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
Panel
MediaItem
Form
Presentation
Service
Page metadata
Servlet
e-mail info@gx.nl
http://www.gx.nl/
37/161
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
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
Register the licensed component with GX in order to generate a license file including a
license for this component
2.
e-mail info@gx.nl
http://www.gx.nl/
39/161
e-mail info@gx.nl
http://www.gx.nl/
40/161
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
labelId
showAsComponent
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
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
labelId
permissionCategory
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
e-mail info@gx.nl
http://www.gx.nl/
42/161
EDITOR_PERMISSION_GROUP
MAIN_EDITOR_PERMISSION_GROUP
APPLICATION_MANAGER_PERMISSION_GROUP
DEVELOPER_PERMISSION_GROUP
new PermissionImpl[] {
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
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
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.
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
Purpose
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
Active
Stopping
Uninstalled
7.2.2
The bundle has been uninstalled. It cannot move into any other state.
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
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()
Stop()
Update()
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:
Purge()
e-mail info@gx.nl
http://www.gx.nl/
46/161
e-mail info@gx.nl
http://www.gx.nl/
47/161
7.3.1
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.
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
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.
3.
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
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.
3.
e-mail info@gx.nl
http://www.gx.nl/
48/161
uninstall()
1.
uninstall() is invoked.
2.
3.
4.
purge()
1.
purge() is invoked.
2.
7.3.2
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
7.3.3
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()
onInstall()
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
onUpdate()
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
7.3.4
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.
e-mail info@gx.nl
http://www.gx.nl/
52/161
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.
e-mail info@gx.nl
http://www.gx.nl/
53/161
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.
e-mail info@gx.nl
http://www.gx.nl/
54/161
7.5.1
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.
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]
wmstop [id]
wmupdate [id]
2.
3.
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
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.
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
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
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
e-mail info@gx.nl
http://www.gx.nl/
58/161
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 filters
Adding an arbitrary resource to your WCB which must be accessible from the WCB
8.1.1
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
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
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.
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
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
e-mail info@gx.nl
http://www.gx.nl/
60/161
in
8.1.4
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.
deploy
static
files
to
the
Edit
environment,
simple
copy
the
resources
to
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
public
Map<String,
Object>
referenceData(HttpServletRequest
req,
Object
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/>
in
3.2
should
be
used.
For
this
reason
resources
should
be
placed
in
e-mail info@gx.nl
http://www.gx.nl/
62/161
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
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.
importPackage
in
the
build/plugins/plugin/
exportPackage
in
the
build/plugins/plugin/
importPackage
in
the
build/plugins/plugin/
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
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.
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
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
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
9.4.1
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.2 version adds an additional keywords property is added. The properties keyword
and secondaryKeyword are deprecated
e-mail info@gx.nl
http://www.gx.nl/
67/161
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
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
e-mail info@gx.nl
http://www.gx.nl/
70/161
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.
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
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
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.
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
e-mail info@gx.nl
http://www.gx.nl/
74/161
the
e-mail info@gx.nl
http://www.gx.nl/
75/161
Add this dependency to the definition of a component in the Activator. For more detailed
information concerning service dependencies, see section 0.
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
e-mail info@gx.nl
http://www.gx.nl/
77/161
e-mail info@gx.nl
http://www.gx.nl/
78/161
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
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
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.
e-mail info@gx.nl
http://www.gx.nl/
80/161
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
Create an image and place it in the images subfolder of the help document;
e-mail info@gx.nl
http://www.gx.nl/
82/161
e-mail info@gx.nl
http://www.gx.nl/
83/161
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
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
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
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:
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
The WCB or WCBs contained in the WCA are installed. Once installed, you will see the
new WCB(s) in the list. For example:
e-mail info@gx.nl
http://www.gx.nl/
88/161
e-mail info@gx.nl
http://www.gx.nl/
89/161
e-mail info@gx.nl
http://www.gx.nl/
90/161
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
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
An
example
Entity
implementation
of
an
entity
e-mail info@gx.nl
called
CustomEntityImpl
http://www.gx.nl/
92/161
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
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
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
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
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.
2.
3.
e-mail info@gx.nl
http://www.gx.nl/
95/161
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
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
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
mandatory
multiple
Specifies whether the property may have more than one value. The default is
false.
constraints
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
Description
name
mandatory
allowsSameNameSiblings
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
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
Description
type
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
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
e-mail info@gx.nl
http://www.gx.nl/
100/16
1
e-mail info@gx.nl
http://www.gx.nl/
101/16
1
Description
implementation
MediaItemVersionImpl, i.e.
CustomMediaItemArticleVersionImpl
Custom
element
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
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
The following figure illustrates the basic steps that are required to make a WCB extendable
(consumer) and to create an extension for it (provider):
e-mail info@gx.nl
http://www.gx.nl/
104/16
1
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.
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.
e-mail info@gx.nl
http://www.gx.nl/
105/16
1
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 :
-
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.
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
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:
e-mail info@gx.nl
http://www.gx.nl/
107/16
1
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
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
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
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());
}
}
e-mail info@gx.nl
http://www.gx.nl/
110/16
1
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:
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
Description
EntityEvent
Event
occurred.
EventHandler
EventManagerService
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.
custom
event
handler
should
implement
the
e-mail info@gx.nl
http://www.gx.nl/
112/16
1
e-mail info@gx.nl
http://www.gx.nl/
113/16
1
Description
publish
subscribe
unsubscribe
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
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
e-mail info@gx.nl
http://www.gx.nl/
116/16
1
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.
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
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:
e-mail info@gx.nl
http://www.gx.nl/
118/16
1
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.
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
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.
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
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
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:
e-mail info@gx.nl
http://www.gx.nl/
122/16
1
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="">> 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
e-mail info@gx.nl
http://www.gx.nl/
124/16
1
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:
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
See: http://junit.sourceforge.net/
e-mail info@gx.nl
http://www.gx.nl/
125/16
1
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:
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
The JUnit framework will process the methods in the following order:
1.
2.
Run all methods that start with test - the order it will process these tests is not fixed;
3.
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
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;
e-mail info@gx.nl
http://www.gx.nl/
128/16
1
e-mail info@gx.nl
http://www.gx.nl/
129/16
1
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
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
e-mail info@gx.nl
http://www.gx.nl/
131/16
1
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);
e-mail info@gx.nl
http://www.gx.nl/
132/16
1
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
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
Description
getClusterNodeId()
isClusterMasterNode()
isClusterTaskMaster()
isShuttingDown()
For an example of a scheduled job, check the updated scheduled job archetype where this
mechanism is implemented.
e-mail info@gx.nl
http://www.gx.nl/
134/16
1
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
ndation.Session
javax.jcr.Session
JCR session, needed to read and write from and to the JCR.
javax.servlet.http.H
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:
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
e-mail info@gx.nl
http://www.gx.nl/
135/16
1
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
e-mail info@gx.nl
http://www.gx.nl/
137/16
1
e-mail info@gx.nl
http://www.gx.nl/
138/16
1
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:
2.
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
e-mail info@gx.nl
http://www.gx.nl/
140/16
1
e-mail info@gx.nl
http://www.gx.nl/
141/16
1
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
e-mail info@gx.nl
http://www.gx.nl/
142/16
1
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
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.
e-mail info@gx.nl
http://www.gx.nl/
144/16
1
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. 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
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() {}
}
}
e-mail info@gx.nl
http://www.gx.nl/
146/16
1
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
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
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
e-mail info@gx.nl
http://www.gx.nl/
150/16
1
/**
* 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
e-mail info@gx.nl
http://www.gx.nl/
152/16
1
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
/**
* {@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
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
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
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
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
/**
* 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
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
The following code passes the values retrieved from the custom fields to the export function:
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