1.3.0
Copyright © 2010 edorasframework.org
Legal Notice
Brunnhofweg 41
CH-3007 Bern
Phone:
Phone:
Fax:
Copyright © 2009 by mimacom ag. This copyrighted material is made available to anyone wishing to use, modify, copy, or redistribute it subject to the terms and conditions of the Mozilla Public License Version 1.1.
Working with object-oriented software and persitence...
Read Chapter 1, Architecture for more information.
If you have questions, use the user forum.
Commercial development support, production support, and training is available through
The core modules provide basic functionalities like spring configuration, localization and security. Other modules that provide more concrete services are based on these core modules.

These modules provide concrete services for certain domains. Often, they are splitted into two or three parts:
Core: Contains the services itself but no GUI elements for it.
Web: Contains web GUI components and controllers (JSF/Facelets) for the services.
Icefaces: Contains web GUI components and controllers (Icefaces) for the services.

The portlet modules provide functionalities that are related to portlets or portlet implementations. They are generally not dependent on the edorasframework core modules, so they can be used on their own.

Components are basically configured as spring beans. The configuration file for a module is named like the artifact id with the string "-applicationContext.xml" added (e.g. org.edorasframework.web-applicationContext.xml) and is located in the META-INF folder. The beans are named with a short prefix depending on the component module (e.g. edsCore, edsWeb, etc.). This way, a component or application can overwrite a bean definition of another component.
To use the defined spring beans, a context must be started. The context searches the classpath for all files named META-INF/*-applicationContext.xml and loads them. To ensure that the files are loaded in the right order, the @Id and @DependsOn annotations can be used:
<!---
@Id("org.edorasframework.web")
@DependsOn("org.edorasframework.core")
-->
<beans xmlns="http://www.springframework.org/schema/beans" ...
The code in the example ensures that the web component is loaded after the core component and therefore the web can overwrite bean definitions of the core.
In order for this dependency resolution to work, one has to use
special spring context. See the javadoc for
DependencyClassPathXmlApplicationContext
,
DependencyXmlWebApplicationContext
and
DependencyContextLoader
.
necessary...
| Name | edsAppCtrl |
| Class | org.edorasframework.core.config.ApplicationContextController |
| Implements/Extends | |
| Description | bootstrap bean to access other beans by name or type |
| Name | edsMsg |
| Class | org.edorasframework.core.locale.impl.FallbackResourceBundle |
| Implements/Extends | java.util.ResourceBundle org.edorasframework.core.locale.MessageHandler |
| Description | The global message handler, this must implement MessageHandler and extend ResourceBundle. All strings that must be translated are processed by this bean. |
| Name | edsCoreMsg |
| Class | org.edorasframework.core.locale.impl.PrefixResourceBundle |
| Implements/Extends | java.util.ResourceBundle org.edorasframework.core.locale.MessageHandler |
| Description | The core message handler, delegates to the global message handler but prefixes all keys with 'org.edorasframework.core.' |
| Name | edsLocale |
| Class | org.edorasframework.core.locale.impl.ProviderLocalisationController |
| Implements/Extends | org.edorasframework.core.locale.LocalisationController |
| Description | The global localisation controller, delegates to a bean named 'edsLocaleProvider' which must implement LocalisatorProvider. |
| Name | edsLocaleProvider |
| Class | org.edorasframework.core.locale.impl.DirectLocalisatorProvider |
| Implements/Extends | org.edorasframework.core.locale.LocalisatorProvider |
| Description | The localisator provider used by the localisation controller. |
| Name | edsUser |
| Class | org.edorasframework.core.security.DummyUserInfo |
| Implements/Extends | org.edorasframework.core.security.UserInfo |
| Description | Informations about the current User. |
| Name | edsMetaModel |
| Class | org.edorasframework.core.metamodel.impl.DefaultMetaModel |
| Implements/Extends | org.edorasframework.core.metamodel.MetaModel |
| Description | The meta model implementation. |
| Name | edsMetaDataFinder |
| Class | org.edorasframework.core.metamodel.MetaDataFinder |
| Implements/Extends | |
| Description | This allows to provide additional meta informations about classes and their properties. |
| Name | edsDialogHandler |
| Class | org.edorasframework.core.locale.impl.LogDialogHandler |
| Implements/Extends | org.edorasframework.core.locale.DialogHandler |
| Description | The dialog handler implementation. |
| Name | edsElEnvironment |
| Class | org.edorasframework.core.el.impl.SimpleElEnvironment |
| Implements/Extends | org.edorasframework.core.el.ElEnvironment |
| Description | This provides that global access point for using EL expressions. |
| Name | |
| Class | org.edorasframework.core.description.impl.DescriptionScanner |
| Implements/Extends | |
| Description | Scans classes for Description typed fields and provides description resolvers to for them. |
| Name | edsAddBeanPostProcessor |
| Class | org.edorasframework.core.config.add.AddBeansPostProcessor |
| Implements/Extends | org.edorasframework.core.config.add.AddBeansPostProcessor |
| Description | Bean post processor for add beans that are dynamically added to their target beans. |
| Name | edsAddBeanDescriptionResolver |
| Class | org.edorasframework.core.description.impl.ListDescriptionResolver |
| Implements/Extends | org.edorasframework.core.description.impl.ListDescriptionResolver |
| Description | Default description resolver for descriptions added by the core namespace handler. |
| Name | edsDescriptionProvider |
| Class | org.edorasframework.core.description.impl.DefaultDescriptionProvider |
| Implements/Extends | org.edorasframework.core.description.DescriptionProvider |
| Description | The description provider bean. |
| Name | edsDescriptionProviderDao |
| Class | org.edorasframework.core.description.impl.NullDescriptionProviderDao |
| Implements/Extends | org.edorasframework.core.description.DescriptionProviderDao |
| Description | The description provider dao |
| Name | edsValidator |
| Class | org.edorasframework.core.metamodel.impl.NullValidator |
| Implements/Extends | org.edorasframework.core.metamodel.BeanValidator |
| Description | Validates the contents of beans. |
Enities can be validated based on annotations. Currently, the validation annotations from hibernate and from JSR303 (javax.validation) are supported.
To use hibernate, add a bean to the spring context
<bean
class="org.edorasframework.core.metamodel.impl.hibernate.HibernateValidator"
/> and add hibernate to the classpath.
To use JSR 303 validation, add a bean to the spring
context <bean
class="org.edorasframework.core.metamodel.impl.jsr303.Jsr303Validator"
/> and add some implementation to the classpath, for
example
<dependency>
<groupId>com.agimatec</groupId>
<artifactId>agimatec-validation</artifactId>
<version>0.7.3-beta-1</version>
</dependency>
</programlisting>
To validate an entity, use one of these code snippets:
BeanValidator validator = ApplicationContextController.getRequiredBean(BeanValidator.class);
BeanValidator validator = metaModel.getValidator();
ExpressionFactory factory = elEnvironment.getExpressionFactory();
ELContext ctx = elEnvironment.getCurrentContext();
ELInfo info = new ELInfo(factory.createValueExpression(ctx, "#{person.name}", String.class));
Set<? extends ValidationMessage<?>> validationMessages = info.getValidationMessages(ctx, valueToValidate);
To automatically set the user and current timestamp on entity
creation or modification, the @Creation and @Modification annotations
can be used. Just add these annotations to a field or a getter. If the
type of the annotated element is String, it will be set to the current
user, if it is Date, it will be set to the current time stamp. For
this to work, you have to apply the line
@EntityListeners(MetaModelEntityListener.class) to the
entity containing the creation/modification properties. The current
user is get from the 'edsUser' bean which implements
org.edorasframework.core.security.UserInfo.
Display patterns define alternatives to convert an entity to a
string. A display pattern consists of an EL expression that defines
the conversion. Display patterns are applied to entities using the
DisplayPattern and DisplayPatterns
annotations. @DisplayPattern("Name: #{name}, Age:
#{age}") To get the actual string from an entity using a
display pattern, use this code:
metaModel.display(person, "patternName"));
It can be defined sort orders to sort a collection of entities.
A sort order is basically a list of property names and ascending
flags. Sort orders are applied to entities using the
PropertySortOrder, NamedSortOrder and
SortOrder annotations. To do the actual sort, a
comparator can be retrieved using this code:
ClassMetaData<Person> cmd = metaModel.getClassMetaData(Person.class);
Comparator<Person> comp = cmd.getSortOrderComparator(MetaModel.DEFAULT_CONTEXT);
To allow to use the same logging configuration during development and during maven builds on continuous integration servers edoras framework provides special log appenders / handlers.
The components listed below support the supported configuration possibilities to enable/ disable the logging.
Define the flag by system environment property using the
property key defined in the field
org.edorasframework.core.logging.DevelopmentLoggerHelper.ENABLE_SYSTEM_PROPERTY.
Define the flag by java system property using the property key
defined in the field
org.edorasframework.core.logging.DevelopmentLoggerHelper.ENABLE_SYSTEM_PROPERTY.
(Overwrites if the property is already defined by environmet
variable)
Define the flag by configuration property using the
configuration key
org.edorasframework.core.logging.DevelopmentLoggerHelper.ENABLE_CONF_PROPERTY.
(Overwrites if the property is already defined by environmet or
system variable) For logger implementation specific configuration
see listing below.
# Root logger configuration log4j.rootLogger=INFO, devel # The development console appender log4j.appender.devel=org.edorasframework.core.logging.DevelopmentConsoleAppender # define whether logging is enabled or not log4j.appender.devel.enableLogging=true
# the development console handler
handlers = org.edorasframework.core.logging.DevelopmentConsoleHandler
# Set the default logging level for the root logger
.level = ALL
# define whether logging is enabled or not
org.edorasframework.core.logging.DevelopmentConsoleHandler.enableLogging=true
Edorasframework provides the possibility to add beans to other beans
within the application context configuration. The beans are added before
the @javax.annotation.PostConstruct annotatied methods are
invoked. Using the add bean solution there is no need to completly
redefine a bean wit a list property just to add on single property bean
more. To add a bean to another the add tag of the edorasframework core
module tag add can be used.
<!--needs namespace: xmlns:core="http://schema.edorasframework.org/org.edorasframework.core"--> <core:add beanRef="toBeAdded" targetBeanRef="toRecieve" addMethod="addBean"/>
This
configuration adds the bean toBeAdded to the bean
toReceive by using the method addBean on the
target bean. The addMethod attribute is not mandatory, since the component
searches the method within the target bean and invokes it. This method
must suite the following requirements:
Has to be public.
The name of the method has to start with 'add'.
May have only one attribute.
The attribute type needs to be assignable for the bean to be added.
The description provider is part of the edoras framework core component. Descriptions are used to centralize the information and reduce redundancy within the application. Using a description on a bean allows link a simple link property with a more specific string description.
A description is always assigned to a description type, which is for type safety reasons a class object. The type orders the descriptions into categories to which bean custom properties can be assigned to. The key of the description has to be unique within each type. Type and key are used to identify each description uniquely. The value is the String representation of the description.
If the description object implements the
org.edorasframework.core.description.MutableDescription
interface, some of the description's values may be changed at runtime.
Using a persistent DAO, these changes will be available also after a
restart of the application.
The application context of the edorasframework core module defines the following bewn names for the description provider beans.
edsDescriptionProvider - The description provider bean
edsDescriptionProviderDao - The description provider dao bean
edsAddBeanDescriptionResolver - A description resolver where the dynamically added descriptions are registered (using the core:addDescription tag.)
If single descriptions are needed by configuration, the can be defined as follows:
<!--needs namespace: xmlns:core="http://schema.edorasframework.org/org.edorasframework.core"--> <core:addDescription type="your.description.type.Class" key="1" value="Value" />
By using this tag, the description is automatically added to the description provider.
The description Provider is the main service component which provides all descriptions that are available for the application. By default this bean is already defined by the edorasframework core component and is not needed to be defined within the current implementation.
<bean id="edsDescriptionProvider"
class="org.edorasframework.core.description.impl.DefaultDescriptionProvider">
<property name="dao" ref="edsDescriptionProviderDao" />
<property name="descriptionResolvers">
<set>
<!-- description resolvers go here -->
</set>
</property>
</bean>
The description resolver is used to resolve descriptions and provide them to the description provider dunring its initialisation. By default the following description resolver exist.
The
org.edorasframework.core.description.impl.ListDescriptionResolver
allows to define descriptions within the application context. The
constructor argument of this resolver takes a list of description
beans.
<bean class="org.edorasframework.core.description.impl.ListDescriptionResolver">
<constructor-arg>
<set>
<!-- Description beans here -->
</set>
</constructor-arg>
</bean>
If descriptions are implemented as enumeration, the
org.edorasframework.core.description.impl.EnumDescriptionResolver
may be used to provide these descriptions to the description provider.
As constructor argument the class name of the enumeration description
implementation has to be defined.
<bean class="org.edorasframework.core.description.impl.EnumDescriptionResolver">
<constructor-arg>
<set>
<!-- Enum Description class names here -->
</set>
</constructor-arg>
</bean>
If descriptions are defined as constants, the
org.edorasframework.core.description.impl.ConstantDescriptionResolver
may be used to provide these descriptions to the description provider.
As constructor argument the class name of the enumeration description
implementation has to be defined.
<bean class="org.edorasframework.core.description.impl.ConstantDescriptionResolver">
<constructor-arg>
<set>
<!-- Enum Description class names here -->
</set>
</constructor-arg>
</bean>
The persistence unit post processor parses all classes handled by
the entity manager factory and searches for the
@Descriptions annotation. If such annotations are fount the
resolver processes all defined description type classes and adds all
descriptions automatically that are eighter defined as member variables
or as enumeration of the description type class.
<bean id="entityManagerFactory" ...>
<property name="persistenceUnitPostProcessors">
<list>
<ref bean="jpaDescriptionResolver" />
</list>
</property>
</bean>
<bean id="jpaDescriptionResolver"
class="org.edorasframework.core.description.impl.jpa.PersistenceUnitDescriptionResolver" />
To add description resolvers dynamically to the description provider the following tag can be used.
<!--needs namespace: xmlns:core="http://schema.edorasframework.org/org.edorasframework.core"--> <core:addDescriptionResolver beanRef="resolverBeanId" />
To use persistent descriptions the
org.edorasframework.core.description.impl.jpa.JpaDescriptionProviderDao
provides the needed functionality to persist the descriptions by using
JPA. This dao uses the description implementation
org.edorasframework.core.description.impl.jpa.JpaDescription
to store the description data within a database. To define this dao add
the following bean definition to your spring context:
<bean id="edsDescriptionProviderDao"
class="org.edorasframework.core.description.impl.jpa.JpaDescriptionProviderDao" />
The org.edorasframework.core.description.Descriptions
annotation is used by the persistence unit description resolver. This
annotation can be defined on the class or member field, to define which
description types may be used by this specific class.
The org.edorasframework.web module provides the
following jsf tags an functions which can be used for jsf
applications.
xmlns:eds="http://www.edorasframework.org/taglib/org.edorasframework.web"
Use the following tag to insert the view: The parameter to be
used is the bean name of the descriptionController which needs to
implement the interface
org.edorasframework.web.description.DescriptionController.
<eds:descriptionView descriptionController="#{descriptionController}"/>
<!-- Returns the description for the given type and key. If no description is found <code>null</code> is returned. -->
<function>
<function-name>description</function-name>
<function-class>
org.edorasframework.web.util.ELFunctions
</function-class>
<function-signature>
org.edorasframework.core.description.Description getDescription(java.lang.String, java.lang.Integer)
</function-signature>
</function>
<function>
<function-name>descriptionTypeSelectItems</function-name>
<function-class>
org.edorasframework.web.util.ELFunctions
</function-class>
<function-signature>
java.util.List getDescriptionSelectItems(java.lang.String,java.util.ResourceBundle,boolean)
</function-signature>
</function>
Anyone ever wanted to have more than just three scopes in JSF, here is a possible solution. On top of Spring custom scopes, edoras provides some implementation of additional scopes, however, to use them, Spring has to be used as the managed bean container rather than the JSF bean facility. But this is a good decision anyway.
Currently the custom scopes can only be used with ICEfaces as they are based on the ICEfaces specific extended request scope extensively. Here is an overview of the different scopes offered by this component:(listed from narrower to wider scopes)
prototype (every time such a bean is requested, it is created as a new object and is never shared)
request (standard request scope, lives as long as the underlying request)
extRequest (the same as the extended request scope in ICEfaces, lives between two GET requests, meaning, it does not persist across a refresh (like hitting F5), however it persists across multiple postbacks like Ajax requests)
view (the same as extRequest, but the scope is attached to the view-id, hence it persists across a refresh or even a redirect, as long as the same view is being rendered, even if the component tree was created from scratch, it is created on top of the window scope, so every tab or window running on the same session has its own state, if view scoped)
conversation (in development currently, the conversation scope spans across multiple views and is typically used to map a single use case)
window (basically the same as session, but this scope is attached to the browsers window or tab, hence multiple tabs or windows are supported, having there own state actually since this scope persists as long as the same window or tab is used)
session (standard session scope, persisting its state in the http-session and is shared over multiple tabs or windows in the same browser)
process (attached to a process instance, persists over the lifetime of a process instance, even a persisted one, this scope is provided in the org.edorasframework.process component)
persistentUser (in development currently, this scope is attached to the user-id and is persisted in the database, so it persists over different sessions and even server restart, can be used as an easy way to scope user-related profile beans)
persistent (the same as persistentUser although its state is attached to the application rather than the user in the database)
singleton (like application scope in the JSF bean facility)
Assuming you already set up Spring as being the main bean managed container.
in web.xml:
<!--If you want to have multiple tabs and windows being supported,
turn on the concurrent DOM view in ICEfaces. Otherwise the window-scope
would be the same as the session scope.-->
<context-param>
<param-name>com.icesoft.faces.concurrentDOMViews</param-name>
<param-value>true</param-value>
</context-param>
<!-- The extended request scope has to be turned on by disabling
the standard request scope in ICEfaces (this is default) -->
<context-param>
<param-name>com.icesoft.faces.standardRequestScope</param-name>
<param-value>false</param-value>
</context-param>
in applicationContext.xml (only needed, if you have sub views):
<!-- Registers a list of sub view ids to the view scope. -->
<bean scope="singleton" parent="edsSubViewRegisterer">
<property name="subViewIds">
<list>
<value>/pages/scope/subScopes.jspx</value>
</list>
</property>
</bean>
The ongoing goal of the ExtFaces project is to make it easier to develop JSF web applications. The project home page can be found at http://www.edorasframework.org/web/fw/extfaces. The following is a list of high-level features:
ExtFaces was founded by Joel Kozikowski and Neil Griffin and was originally sponsored by Liferay, Inc. under incubation. Since then, ExtFaces has been adopted by Mimacom AG and is a sub-project of the edoras framework, which is a suite of JARs that compliment the following stack: JSF/ICEfaces, Spring, JPA, and Liferay Portal. The edoras framework also includes a workflow engine and graphical workflow designer for Eclipse. The project home page can be found at http://www.edorasframework.org.
You can obtain the ExtFaces JAR by downloading it from the project website, or if using Maven, include the
following dependency in your pom.xml file.
Example 5.1. Maven pom.xml entries to include ExtFaces JAR
<dependencies> <dependency> <groupId>org.edorasframework</groupId> <artifactId>org.edorasframework.extfaces</artifactId> <version>1.3.0-SNAPSHOT</version> </dependency> </dependencies> <repositories> <repository> <id>edorasframework-releases</id> <name>edorasframework.org releases</name> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> <url>http://repo.edorasframework.org/mvn/maven2/</url> </repository> <repository> <id>edorasframework-snapshots</id> <name>edorasframework.org snapshots</name> <releases> <enabled>false</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> <url>http://repo.edorasframework.org/mvn/maven2-snapshots/</url> </repository> </repositories>
The following diagram illustrates the dependencies for the ExtFaces JAR.
JSF web application developers typically call FacesContext.getCurrentInstance() in order to obtain the ThreadLocal singleton instance associated with the current request. While JSF portlet developers can certainly do the same, it's easier to call ExtFacesContext.getInstance() which returns an application-scoped singleton instance.
Example 5.2. Obtaining the ExtFacesContext Singleton Instance
public class SessionScopedManagedBean {
private ExtFacesContext extFacesContext = ExtFacesContext.getInstance();
public List<File> getDocuments() {
List<File> documents;
try {
documents = DocumentService.getFiles();
}
catch (Exception e) {
LOG.error(e.getMessage(), e);
// Don't have to call ExtFacesContext.getInstance() first since
// a reference to it was obtained when the bean was created.
extFacesContext.addGlobalErrorMessage("An unexpected error occurred");
}
return documents;
}
}
ExtFacesContext is an abstract class that extends FacesContext which means it supplies all the same method signatures and can therefore do anything that FacesContext can do. ExtFacesContext implements the delegation design pattern for methods defined by FacesContext by first calling FacesContext.getCurrentInstance() and then delegating to corresponding methods. The benefit of using this technique is that JSF web application developers only have to call ExtFacesContext.getInstance() once, and can save the singleton object reference for future use.
ExtFaces introduces several variables into the Expression Language (EL).
Table 5.1. PortletFaces EL Variables
| EL Variable | Description |
|---|---|
i18n
|
As an abbreviation for the word "internationalization" the i18n EL
variable enables page authors to declaratively specify message
keys.
Type:
|
As an abbreviation for the word "internationalization" the i18n
EL variable enables page authors to declaratively specify message keys. Currently the MessageContextImpl
class will only get values that are provided the JSF standard message keys. Developers can use Spring to
inject an alternate MessageContextImpl in order to leverage additional values.
Example 5.3. Usage of the i18n EL Variable
<h:outputLabel value="#{The JSF required message is: i18n['javax.faces.component.UIInput.REQUIRED']}" />
ExtFaces provides the following UIComponent tags as part of its component suite.
Table 5.2. UIComponent Tags
| Tag | Description |
|---|---|
ext:inputFile
|
Renders an HTML <input type="file" /> tag which provides file
upload capability.
|
ext:inputRichText
|
Renders a text area that provides the ability to enter rich text such as bold, italic, and underline. |
The ext:inputFile tag renders an HTML <input type="file"
/> tag which enables file upload capability.
Usage of this tag requires a faces-context-factory element to be placed in the
WEB-INF/faces-config.xml file. See the File Upload section for more details.
Table 5.3. Attributes
| Attribute | Type | Description | Required |
|---|---|---|---|
| id | String | The identifier of the component | false |
| rendered | Boolean | Boolean flag indicating whether or not this component is to be rendered during the RENDER_RESPONSE phase of the JSF lifecycle. The default value is “true”. | false |
| value | java.io.File | The value of the component, which will be a file on the server. | true |
Example 5.4. Example usage of ext:inputFile tag
<h:form enctype="multipart/form-data">
<ext:inputFile value="#{modelManagedBean.file}" />
</h:form>
The ext:inputRichText tag renders a text area that provides the ability to enter
rich text such as bold, italic, and underline. The renderer relies on the CKEditorTM to provide the rich
text editing area.
At this time it is the responsibility of the web application developer to include a copy of the CKEditorTM JavaScript and related images in the web application.
Table 5.4. Attributes
| Attribute | Type | Description | Required |
|---|---|---|---|
| autoIncludeJavascript | Boolean | Boolean flag indicating whether or not the Javascript required by the rich text editor will be automatically included in the rendered HTML. The default value is "true". | false |
| id | String | The identifier of the component | false |
| javascriptPath | String | The path to the Javascript resources in the web application. The default value is "/js". | false |
| rendered | Boolean | Boolean flag indicating whether or not this component is to be rendered during the RENDER_RESPONSE phase of the JSF lifecycle. The default value is “true”. | false |
| value | String | The value of the component, the HTML fragment generated by the end-user. | true |
Example 5.5. Example usage of ext:inputRichText tag
<ext:inputRichText id="comments" value="#{modelManagedBean.comments}" />
ExtFaces provides the following Facelet Composite Component tags as part of its component suite.
Table 5.5. Facelet Composite Component Tags
| Tag | Description |
|---|---|
ext:iceInfoDataPaginator
|
Encapsulates an ICEfaces ice:dataPaginator tag that renders pagination information for an associated ice:dataTable. |
ext:iceNavDataPaginator
|
Encapsulates an ICEfaces ice:dataPaginator tag that renders navigation controls for an associated ice:dataTable. The icons will match the current ICEfaces theme. |
The ext:iceInfoDataPaginator encapsulates an ICEfaces ice:dataPaginator
tag that renders pagination information for an associated ice:dataTable.
Table 5.6. Attributes
| Attribute | Type | Description | Required |
|---|---|---|---|
| for | String | Corresponds to the value of an id attribute for an ice:dataTable tag.
|
true |
| value | String | Specifies a string that contains replacement tokens for each piece of pagination information. The default value is "Results {0}-{1} of {2} (Page {3} of {4})". The best practice would be to specify an EL expression that returns an internationalized value that contains the replacement tokens. | true |
Example 5.6. Example usage of ext:iceInfoDataPaginator tag
<f:view xmlns:ext="http://edorasframework.org/extfaces/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component">
<ext:iceInfoDataPaginator for="dataTable1" />
<ice:dataTable id="dataTable1" value="#{modelManagedBean.rows}" var="row">
...
</ice:dataTable>
</f:view>
The ext:iceInfoDataPaginator encapsulates an ICEfaces ice:dataPaginator
tag that renders navigation controls for an associated ice:dataTable.
Table 5.7. Attributes
| Attribute | Type | Description | Required |
|---|---|---|---|
| fastForwardIconRendered | Boolean | Boolean flag indicating whether or not the "Fast Forward" button/icon is rendered. The default value is "false". | false |
| fastRewindIconRendered | Boolean | Boolean flag indicating whether or not the "Fast Rewind" button/icon is rendered. The default value is "false". | false |
| firstIconRendered | Boolean | Boolean flag indicating whether or not the "First" button/icon is rendered. The default value is "true". | false |
| for | String | Corresponds to the value of an id attribute for an ice:dataTable tag.
|
true |
| lastIconRendered | Boolean | Boolean flag indicating whether or not the "Last" button/icon is rendered. The default value is "true". | false |
| nextIconRendered | Boolean | Boolean flag indicating whether or not the "Next" button/icon is rendered. The default value is "true". | false |
| paginator | Boolean | Boolean flag indicating whether or not the page number links will be rendered. This is a pass-through attribute for the encapsulated ice:dataPaginator. The default value is "true". | false |
| paginatorMaxPages | Integer | The maximum amount of pages to be displayed in the paginator. This is a pass-through attribute for the encapsulated ice:dataPaginator. The default value is 7. | false |
| previousIconRendered | Boolean | Boolean flag indicating whether or not the "Previous" button/icon is rendered. The default value is "true". | false |
Example 5.7. Example usage of ext:iceNavDataPaginator tag
<f:view xmlns:ext="http://edorasframework.org/extfaces/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component">
<ext:iceNavDataPaginator for="dataTable1" />
<ice:dataTable id="dataTable1" value="#{modelManagedBean.rows}" var="row">
...
</ice:dataTable>
</f:view>
ExtFaces provides the following converter tags.
Table 5.8. Converter Tags
| Tag | Description |
|---|---|
ext:convertPhoneNumber
|
Converts a string of digits into a formatted phone number. |
ext:convertYesNo
|
Converts input such as "Yes" and "No" to Boolean.TRUE and
Boolean.FALSE respectively.
|
The ext:convertPhoneNumber tag converts a string of digits into a formatted phone
number.
Table 5.9. Attributes
| Attribute | Type | Description | Required |
|---|---|---|---|
| unitedStatesPhoneFormat | String | The format of the 10-digit US phone number, i.e.: ###-###-#### or (###)-###-#### or ###.###.#### | false |
Example 5.8. Example usage of ext:convertPhoneNumber tag
<h:inputText value="#{modelManagedBean.phoneNumber}">
<ext:convertPhoneNumber unitedStatesPhoneFormat="###-###-####" />
</h:inputText>
ExtFaces provides the following validator tags.
The JSF 1.x and 2.x specifications have not implemented support for HTML forms with
enctype="multipart/form-data" and are therefore do not support file uploads. Various
workarounds exist for use with the JSP view-handler, such as introducing a servlet filter or phase listener
to intercept the request. With the advent of the Facelets view-handler, these techniques
have become largely ineffectual. The problem has to do with the FaceletViewHandler
restoreView(FacesContext context, String viewId) method which is unable to retrieve the
javax.faces.ViewState request parameter for HTML forms with
enctype="multipart/form-data". The underlying reason why it can't retrieve the
parameter is because the ExternalContextImpl.getRequestParameterMap() method in the JSF
Reference Implementation is not equipped to handle these types of postback requests.
The ExtFaces project solves this problem by providing wrapper implementations of
FacesContext, FacesContextFactory, and
ExternalContext. These can be activated by placing the following
<faces-context-factory> element inside of your portlet WAR's
WEB-INF/faces-config.xml descriptor:
Example 5.11. Example usage of faces-context-factory element
<factory> <faces-context-factory>org.edorasframework.extfaces.mojarra.FacesContextFactoryImpl</faces-context-factory> </factory>
This will ultimately cause the FacesContext.getExternalContext() method to return an instance of the org.edorasframework.portletfaces.mojarra.ExternalContextImpl class. This class overrides the ExternalContext.getRequestParameterMap() method, which uses Apache Commons FileUpload project to parse the request parameters. When used in conjunction with the ext:inputFile tag, JSF web application developers can provide file upload capability in their Facelet views.
The ongoing goal of the PortletFaces project is to make it easier to develop JSF portlets that run within Liferay Portal. The project home page can be found at http://www.portletfaces.org. PortletFaces contains a wealth of features that expose the standard features of the Portlet 2.0 API and vendor-specific features of Liferay in a way that is natural to JSF development. The following is a list of high level features:
PortletFaces was founded by Joel Kozikowski and Neil Griffin and was originally sponsored by Liferay, Inc. under incubation. Since then, PortletFaces has been adopted by Mimacom AG and is a sub-project of the edoras framework, which is a suite of JARs that compliment the following stack: JSF/ICEfaces, Spring, JPA, and Liferay Portal. The edoras framework also includes a workflow engine and graphical workflow designer for Eclipse. The project home page can be found at http://www.edorasframework.org.
You can obtain the PortletFaces JAR (and runtime dependencies) by downloading it from the project website,
or if using Maven, include the following dependency in your pom.xml file.
Example 6.1. Maven pom.xml entries to include PortletFaces JAR
<dependencies> <dependency> <groupId>org.edorasframework</groupId> <artifactId>org.edorasframework.portletfaces</artifactId> <version>1.3.0-SNAPSHOT</version> </dependency> </dependencies> <repositories> <repository> <id>edorasframework-releases</id> <name>edorasframework.org releases</name> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> <url>http://repo.edorasframework.org/mvn/maven2/</url> </repository> <repository> <id>edorasframework-snapshots</id> <name>edorasframework.org snapshots</name> <releases> <enabled>false</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> <url>http://repo.edorasframework.org/mvn/maven2-snapshots/</url> </repository> </repositories>
The following diagram illustrates the dependencies for the PortletFaces JAR.
The dependency on the MyFaces portlet-bridge-api and the Sun jsf-portlet bridge are scoped as "provided" because the only time they will truly be runtime dependencies is when the pf:inputFile tag is used. The commons-fileupload is scoped as "compile", although it too is not truly a runtime dependency unless the pf:inputFile tag is used. The commons-io dependency is a transitive runtime dependency for commons-fileupload that is not mentioned in its pom.xml file. See the File Upload section for more information.
Portlets are web applications that are designed to run inside a portlet container that implements either the Portlet 1.0 or Portlet 2.0 standard. Portlet containers provide a layer of abstraction over the Java EE Servlet API, and consequently require a servlet container like Apache Tomcat to function. The reference implementation for Portlet 1.0 and 2.0 is the Apache Pluto project: http://portals.apache.org/pluto
Portals are standalone systems that use a portlet container as the runtime engine for executing portlets. When a portal is asked to deliver a portal page to the end-user’s web browser, each portlet is asked to render itself as a fragment of HTML. It is the job of the portal to aggregate these HTML fragments into a complete HTML document.
The Portlet 1.0 standard defines two lifecycle phases for the execution of a portlet that a compliant portlet container must support: The first is the javax.portlet.PortletRequest.RENDER_PHASE, in which the portlet container asks each portlet to render itself as a fragment of HTML. The second is the javax.portlet.PortletRequest.ACTION_PHASE, in which the portlet container invokes actions related to HTML form submission. When the portal receives an HTTP GET request for a portal page, the portlet container executes the portlet lifecycle and each of the portlets on the page undergoes the RENDER_PHASE. When the portal receives an HTTP POST request, the portlet container executes the portlet lifecycle and the portlet associated with the HTML form submission will first undergo the ACTION_PHASE before the RENDER_PHASE is invoked for all of the portlets on the page.
The Portlet 2.0 standard adds two more lifecycle phases that define the execution of a portlet. The first is the javax.portlet.PortletRequest.EVENT_PHASE, in which the portlet container broadcasts events that are the result of an HTML form submission. During this phase, the portlet container asks each portlet to process events that they are interested in. The typical use case for the EVENT_PHASE is to achieve Inter-Portlet Communication (IPC), whereby two or more portlets on a portal page share data in some way. The other new phase added by the Portlet 2.0 standard is the javax.portlet.PortletRequest.RESOURCE_PHASE, in which the portlet container asks a specific portlet to perform resource-related processing. One typical use case for the RESOURCE_PHASE is for an individual portlet to process Ajax requests. Another typical use case for the RESOURCE_PHASE is for an individual portlet to generate non-HTML content (for download purposes) such as a PDF or spreadsheet document.
The Portlet 1.0 and 2.0 standards define three portlet modes that a compliant portlet container must support: javax.portlet.PortletMode.VIEW, javax.portlet.PortletMode.EDIT, and javax.portlet.PortletMode.HELP. Portal vendors and portlet developers may supply custom modes as well. VIEW mode refers to the rendered portlet markup that is encountered by the user under normal circumstances. Perhaps a clearer name would be normal mode or typical mode, because the word view is also used by developers to review to the view concern of the MVC design pattern. EDIT mode refers to the rendered portlet markup that is encountered by the user when selecting custom values for portlet preferences. Perhaps a clearer name would be preferences mode. Finally, HELP mode refers to the rendered portlet markup that is encountered by the user when seeking help regarding the usage and/or functionality of the portlet.
Portals typically manifest the rendered markup of a portlet in a rectangular section of the browser known as a portlet window. The Portlet 1.0 and 2.0 standards define three window states that a compliant portlet container must support: javax.portlet.WindowState.NORMAL, javax.portlet.WindowState.MAXIMIZED, and javax.portlet.WindowState.MINIMIZED. The NORMAL window state refers to the way in which the portlet container displays the rendered markup of a portlet when it can appear on the same portal page as other portlets. The MAXIMIZED window state refers to the way in which the portlet container displays the rendered markup of a portlet when it is the only portlet on a page, or when the portlet is to be rendered more prominently than other portlets on a page. Finally, the MINIMIZED window state refers to the way in which the portlet container displays a portlet when the markup is not to be rendered.
Developers often have the requirement to provide the end-user with the ability to personalize the portlet behavior in some way. To meet this requirement, the Portlet 1.0 and 2.0 standards provide the ability to define preferences for each portlet. Preference names and default values can be defined in the WEB-INF/portlet.xml configuration file. Portal end-users start out interacting with the portlet user interface in portlet VIEW mode but can switch to portlet EDIT mode in order to select custom preference values.
Example 6.2. Specifying preference names and associated default values in the WEB-INF/portlet.xml configuration file
<portlet-app> <portlet> ... <portlet-preferences> <preference> <name>datePattern</name> <value>MM/dd/yyyy</value> </preference> <preference> <name>unitedStatesPhoneFormat</name> <value>###-###-####</value> </preference> </portlet-preferences> ... </portlet> </portlet-app>
Inter-portlet communication (IPC) is a technique whereby two or more portlets on a portal page share data in some way. In a typical IPC use case, user interactions with one portlet affect the rendered markup of another portlet. The Portlet 2.0 standard provides two techniques to achieve IPC: Public Render Parameters and Server-Side Events.
The Public Render Parameters technique provides a way for portlets to share data by setting public/shared parameter names in a URL controlled by the portal. While the benefit of this approach is that it is relatively easy to implement, the drawback is that only small amounts of data can be shared. Typically the kind of data that is shared is simply the value of a database primary key.
The Server-Side Events technique provides a way for portlets to share data using an event-listener design. When using this form of IPC, the portlet container acts as broker and distributes events and payload (data) to portlets. One requirement of this approach is that the payload must implement the java.io.Serializable interface since it might be sent to a portlet in another WAR running in a different classloader.
It could be argued that the Portlet 2.0 approaches for IPC have a common drawback in that they can lead to a potentially disruptive end-user experience. This is because they cause either an HTTP GET or HTTP POST which results in a full page refresh. Technologies such as ICEfaces Ajax Push can be used to solve this problem.
Strictly speaking, Portlet 1.0 and 2.0 only provide the ability for developers to write a Java class that writes HTML markup to the response that is delivered to the web browser. Although it is possible to introduce a markup-based view technology like JSP, portlet developers often choose the JSF framework in order to leverage the MVC design pattern and robust UI component features.
The Portlet 1.0 and JSF 1.0 specifications were formulated during roughly the same timeframe.
Consequently, the JSF expert group was able to design a framework with portlet compatibility in mind.
This is evidenced by methods like ExternalContext.getRequest() which returns a value of type Object,
rather than a value of type javax.servlet.http.HttpServletRequest. When running inside a portlet container, the same
method would return a value of type javax.portlet.PortletRequest. Although the JSF API provides a degree of portlet
compatibility, it is necessary to introduce a bridge between the JSF lifecycle and the Portlet lifecycle
in order to run JSF applications as portlets.
In order to use JSF in a portlet, developers must specify a JSF portlet bridge in the
<portlet-class> element of the
WEB-INF/portlet.xml descriptor. JSF portlet bridge implementations are
either based on the JSR 301 standard, the JSR 329 standard, or were developed as innovative open source
projects prior to the formulation of the standards. The JSR 301 standard defines a bridge API for
Portlet 1.0 + JSF 1.2, whereas the JSR 329 standard defines a bridge API for Portlet 2.0 + JSF 1.2. The
reference implementation for JSR 301 and 329 is the Apache MyFaces Portlet Bridge
project.
At the time of this writing, the Subversion repository for the MyFaces Portlet Bridge provides a branch with prototype support for Portlet 2.0 + JSF 2.0.
JSF portlet bridges are responsible for providing a “bridge” between the portlet lifecycle and the JSF lifecycle. For example, when a portal page that contains a JSF portlet is requested via HTTP GET, then the RENDER_PHASE of the portlet lifecycle should in turn execute the RESTORE_VIEW and RENDER_RESPONSE phases of the JSF lifecycle. Similarly, when the user submits a form contained within a JSF portlet via HTTP POST, then the ACTION_PHASE of the portlet lifecycle should execute the complete JSF lifecycle of RESTORE_VIEW, APPLY_REQUEST_VALUES, PROCESS_VALIDATIONS, UPDATE_MODEL_VALUES, INVOKE_APPLICATION, and RENDER_RESPONSE.
Since the portal is in full control of managing URLs, JSF portlet bridges are also responsible for asking the portal to generate URLs that are compatible with actions that invoke JSF navigation rules. Consequently, JSF portlets may not perform a redirect. If a different JSF view is to be rendered as a result of a JSF navigation-rule, then the JSF portlet bridge simply displays the new JSF view in the same portlet window.
Table 6.1. JSF Portlet Bridges
| Bridge Name | Description |
|---|---|
| MyFaces Portlet Bridge (JSR 301 Reference Implementation) |
Project Website: http://myfaces.apache.org/portlet-bridge/1.0 |
| MyFaces Portlet Bridge (JSR 329 Reference Implementation) |
Project Website: http://myfaces.apache.org/portlet-bridge/2.0 |
| Sun OpenPortal JSF Portlet Bridge |
Project Website: https://jsfportletbridge.dev.java.net |
| ICEfaces 1.x Portlet Bridge |
Project Website: http://www.icefaces.org |
| Apache Portals-Bridges JSF Portlet Bridge |
Project Website: http://portals.apache.org/bridges/multiproject/portals-bridges-jsf |
| MyFacesGenericPortlet |
Project Website: http://myfaces.apache.org/core11 |
| JBoss Portlet Bridge |
Project Website: http://www.jboss.org/portletbridge |
| JBoss Portlet Bridge |
Project Website: http://www.jboss.org/portletbridge/ |
Example 6.3. Specifying the JSR 301 or JSR 329 JSF Portlet Bridge in the WEB-INF/portlet.xml configuration file, as well as default Facelet views that are to be rendered for VIEW mode, EDIT mode, and HELP mode
<portlet-app> <portlet> <portlet-name>my_portlet</portlet-name> <display-name>My Portlet</display-name> <portlet-class> javax.portlet.faces.GenericFacesPortlet </portlet-class> <init-param> <name>javax.portlet.faces.defaultViewId.view</name> <value>/xhtml/applicantForm.xhtml</value> </init-param> <init-param> <name>javax.portlet.faces.defaultViewId.edit</name> <value>/xhtml/edit.xhtml</value> </init-param> <init-param> <name>javax.portlet.faces.defaultViewId.help</name> <value>/xhtml/help.xhtml</value> </init-param> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> <portlet-mode>help</portlet-mode> </supports> ... </portlet> </portlet-app>
Example 6.4. Specifying the Sun OpenPortal JSF Portlet Bridge in the WEB-INF/portlet.xml configuration file, as well as default JSF views that are to be rendered for VIEW mode, EDIT mode, and HELP mode
<portlet-app> <portlet> <portlet-name>my_portlet</portlet-name> <display-name>My Portlet</display-name> <portlet-class> com.sun.faces.portlet.FacesPortlet </portlet-class> <init-param> <name>com.sun.faces.portlet.INIT_VIEW</name> <value>/xhtml/applicantForm.xhtml</value> </init-param> <init-param> <name>com.sun.faces.portlet.INIT_EDIT</name> <value>/xhtml/edit.xhtml</value> </init-param> <init-param> <name>com.sun.faces.portlet.INIT_HELP</name> <value>/xhtml/help.xhtml</value> </init-param> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> <portlet-mode>help</portlet-mode> </supports> ... </portlet> </portlet-app>
The default view-handler for JSF 1.x is designed for JSP technology. With the advent of the Facelets view-handler, JSF developers were able to migrate away from JSP-based views to XHTML-based views and take advantage innovative features such as Composite Components and Page Templates. The Facelets project can be found at: https://facelets.dev.java.net
Although the com.sun.facelets.FaceletViewHandler class is not compatible with JSF portlets, the Facelets project supplies the com.sun.facelets.FaceletPortletViewHandler class in the “demo/portlet” folder of its source code distribution. Although this class is not contained in the jsf-facelets.jar binary distribution, the FaceletPortletViewHandler.java file can be coped into the Java source code folder of the portlet so that the Facelets view-handler can be used instead of JSP.
Example 6.5. Specifying the portlet-compatible version of the Facelets view-handler in the WEB-INF/faces-config.xml configuration file
<faces-config> <application> <view-handler> com.sun.facelets.FaceletPortletViewHandler </view-handler> </application> </faces-config>
The JSR 301 and JSR 329 standards only officially support JSP-based views with the default JSP view-handler provided by the JSF implementation. However, it is possible to use the FaceletPortletViewHandler in an unofficial manner.
Facelets is the premiere view technology for JSF 2.0 and the Mojarra MultiViewHandler automatically detects if JSF views are designed for Facelets or JSP. When portlet bridges officially support JSF 2.0, there will be no more need to include the FaceletPortletViewHandler source in the Java source code folder of the portlet when using Mojarra.
JSF portlet developers often have the requirement to provide the end-user with the ability to
personalize the portlet in some way. To meet this requirement, the Portlet 2.0 specification provides
the ability to define portlet preferences for each portlet. Preference names and default values can be
defined in the WEB-INF/portlet.xml descriptor. Portal end-users start out
interacting with the portlet user interface in portlet VIEW mode but switch to
portlet EDIT mode in order to select custom preference values.
Example 6.6. Portlet Preferences in WEB-INF/portlet.xml
<portlet-preferences> <preference> <name>unitedStatesPhoneFormat</name> <value>###-###-####</value> </preference> <preference> <name>datePattern</name> <value>MM/dd/yyyy</value> </preference> <preference> <name>recipientEmailAddress</name> <value>humanresources@some-company-domain.com</value> </preference> </portlet-preferences>
Additionally, Portlet 2.0 provides the ability to specify support for EDIT
mode in the WEB-INF/portlet.xml descriptor.
Example 6.7. Enabling Support for Portlet EDIT Mode in WEB-INF/portlet.xml
<supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> </supports>
However, just because support portlet EDIT mode has been specified, it
doesn't mean that the portlet container knows which JSF view should be rendered when the user enters
portlet portlet EDIT mode. JSF portlet developers must specify the Facelet view
that is to be displayed for each supported portlet mode. While JSR 301/329 defines a standard way of
specifying views, non-standard bridges each have their own way.
Example 6.8. Specifying a Facelet View for EDIT mode with a JSR 301/329 JSF Portlet Bridge
<init-param> <name>javax.portlet.faces.defaultViewId.edit</name> <value>/edit.xhtml</value> </init-param>
Example 6.9. Specifying a Facelet View for EDIT mode with the Sun OpenPortal JSF Portlet Bridge
<init-param> <name>com.sun.faces.portlet.INIT_EDIT</name> <value>/edit.xhtml</value> </init-param>
Example 6.10. Specifying a Facelet View for EDIT mode with the ICEfaces 1.x Portlet Bridge
<init-param> <name>com.icesoft.faces.EDIT</name> <value>/edit.iface</value> </init-param>
Facelet views that are designed to be used in portlet EDIT mode are typically
forms that contain JSF component tags that enable the portlet end-user to select custom preference
values that override the default values specified in the WEB-INF/portlet.xml
descriptor. JSR 301/329 bridge implementations are required to provide an EL resolver that introduces
the portletPreferences variable into the EL, which is a mutable
java.util.Map that provides read/write access to each portlet preference. By
utilizing the JSR 301/329 portletPreferences variable within an EL
ValueExpression, portlet developers can declaratively bind the Facelet view to
the portlet preference model data. In order to save the preferences, a backing bean must call the PortletPreferences.store() method.
Example 6.11. EDIT Mode with a JSR 301/329 Portlet Bridge
<!--
This is a file named edit.xhtml that can be used for portlet EDIT mode.
It utilizes the JSR 301/329 portletPreferences EL variable for gaining
read/write access to javax.portlet.PortletPreferences.
-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:view xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:form>
<h:messages globalOnly="true" />
<h:outputLabel for="datePattern" />
<h:inputText id="datePattern" required="true" value="#{portletPreferences['datePattern'].value}" />
<h:message for="datePattern" />
<h:commandButton actionListener="#{backingManagedBean.savePreferences}" value="Save Preferences" />
</h:form>
</f:view>
/**
* This is a JSF backing managed-bean that has a savePreferences action-listener.
*/
public class BackingManagedBean {
public void savePreferences(ActionEvent actionEvent) {
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
PortletRequest portletRequest = (PortletRequest) externalContext.getRequest();
PortletPreferences portletPreferences = portletRequest.getPreferences();
portletPreferences.store();
}
}
Just as JSF web application developers rely on ExternalContext in order to get access to the Servlet API, JSF portlet developers also rely on ExternalContext in order to get access to the Portlet API. The two most common tasks that JSF portlet developers need to perform is to obtain an instance of the javax.portlet.PortletRequest or javax.portlet.PortletResponse objects.
Example 6.12. Getting the PortletRequest and PortletResponse objects from within a JSF backing managed-bean action method
public class BackingBean {
public String submit() {
FacesContext facesContext =
FacesContext.getCurrentInstance();
ExternalContext externalContext =
facesContext.getExternalContext();
PortletRequest portletRequest =
(PortletRequest) externalContext.getRequest();
PortletResponse portletResponse =
(PortletResponse) externalContext.getResponse();
return “success”;
}
}
Facelet views that are designed to be used in portlet EDIT mode are typically forms that enable the portlet end-user to select custom preference values that override the default values specified in the WEB-INF/portlet.xml configuration file. JSR 301/329 bridge implementations are required to provide an EL resolver that introduces the portletPreferences variable into the EL, which is a mutable java.util.Map that provides read/write access to each portlet preference. By utilizing the JSR 301/329 portletPreferences variable within an EL ValueExpression, portlet developers can declaratively bind the Facelet view to the portlet preference model data. In order to save the preferences, a backing bean must call the javax.portlet.PortletPreferences.store() method.
Example 6.13. Developing a Facelet view and associated backing managed-bean to support portlet EDIT mode with a JSR 301/329 Portlet Bridge
<!--
This is a file named edit.xhtml that can be used
for portlet EDIT mode. It utilizes the JSR 301/329
portletPreferences EL variable for gaining read/write
access to javax.portlet.PortletPreferences.
-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:view xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:form>
<h:messages globalOnly="true" />
<h:outputLabel for="datePattern" />
<h:inputText id="datePattern"
required="true"
value="#{portletPreferences['datePattern'].value}" />
<h:message for="datePattern" />
<h:commandButton
actionListener="#{backingBean.savePreferences}"
value="Save Preferences" />
</h:form>
</f:view>
/**
* This is a JSF backing managed-bean that has a
* savePreferences action-listener.
*/
public class BackingBean {
public void savePreferences(ActionEvent actionEvent) {
FacesContext facesContext =
FacesContext.getCurrentInstance();
ExternalContext externalContext =
facesContext.getExternalContext();
PortletRequest portletRequest =
(PortletRequest) externalContext.getRequest();
PortletPreferences portletPreferences =
portletRequest.getPreferences();
portletPreferences.store();
}
}
The only JSF portlet bridges that can technically support Portlet 2.0 style IPC are those that subclass the Portlet 2.0 version of javax.portlet.GenericPortlet class. At the time of this writing, version 1.2.3 of the Sun OpenPortal JSF Portlet Bridge supports Public Render Parameters, and Server-Side Events will be supported in a subsequent version. Also at the time of this writing, the JSR 329 standard supports both Public Render Parameters and Server-Side Events. As the JSR 329 specification is currently only at Early Draft Review 2 (EDR2) status, please refer to the following sections of the specification for the latest details on how to leverage Portlet 2.0 IPC with JSF:
Perhaps the most natural approach for a JSF developer to try for IPC is to specify session scope on a JSF managed-bean. Surprisingly, this approach doesn’t work. To understand the reason why, it is necessary to discuss the fact that the Portlet 1.0 and 2.0 standards make a distinction between two kinds of session scopes: javax.portlet.PortletSession.APPLICATION_SCOPE and javax.portlet.PortletSession.PORTLET_SCOPE. The former can be used for sharing data between portlets packaged in the same WAR, but the latter cannot. The reason why JSF session scope can’t be used to share data between portlets is because all JSF portlet bridges use PortletSession.PORTLET_SCOPE.
In order to share data with PortletSession.APPLICATION_SCOPE, the JSF portlet developer can place a JSF model managed-bean in request scope and use the getter/setter as a layer of abstraction.
Example 6.14. Developing a request-scoped JSF managed-bean that has a getter and setter that serves as a layer of abstraction over PortletSession.APPLICATION_SCOPE
public class ModelManagedBean {
public static final String
SHARED_STRING_KEY = “sharedStringKey”;
public String getSharedString() {
return PortletSessionUtil.getSharedSessionAttribute(
SHARED_STRING_KEY);
}
public void setSharedString(String value) {
PortletSessionUtil.setSharedSessionAttribute(
SHARED_STRING_KEY, value);
}
}
public class PortletSessionUtil {
public static Object getSharedSessionAttribute(
String key) {
FacesContext facesContext =
FacesContext.getCurrentInstance();
ExternalContext externalContext =
facesContext.getExternalContext();
PortletSession portletSession =
(PortletSession) externalContext().getSession(false);
return portletSession.getAttribute(
key, PortletSession.APPLICATION_SCOPE);
}
public static void setSharedSessionAttribute(
String key, Object value) {
FacesContext facesContext =
FacesContext.getCurrentInstance();
ExternalContext externalContext =
facesContext.getExternalContext();
PortletSession portletSession =
(PortletSession)externalContext().getSession(false);
portletSession.setAttribute(
key, value, PortletSession.APPLICATION_SCOPE);
}
}
Alternatively, if using the Spring Framework to replace or augment the JSF Managed Bean Facility, then developers can store data in PortletSession.APPLICATION_SCOPE using the globalSession scope keyword
Example 6.15. Specifying a Spring bean that is to be stored in PortletSession.APPLICATION_SCOPE by registering it in the WEB-INF/applicationContext.xml configuration file
<bean id="sharedManagedBean" class="com.sample.jsf.SharedManagedBean" scope="globalSession"/>
ICEfaces is an open source extension to JSF that enables developers with Java EE application skills to build Ajax-powered Rich Internet Applications (RIA) without writing any JavaScript code. The product contains a robust suite of Ajax-enabled JSF UI components, and also supports a broad array of Java application servers, IDEs, third party components, and JavaScript effect libraries. The project home page can be found at http://www.icefaces.org
Because of its integrated Ajax framework, ICEfaces is a particularly good choice for developing RIA portlets. Consider a portal page that contains two portlets: Portlet A and Portlet B. When submitting a form in Portlet A, an HTTP POST takes place and the entire portal page is refreshed. This can result in a disruptive end-user experience if the user had entered data in Portlet B prior to submitting Portlet A. ICEfaces allows you to combat this disruptive experience with RIA features in your portlets.
ICEfaces can be used in a variety of portal products including Liferay Portal. Since July of 2007, Liferay, Inc. and ICEsoft Technologies, Inc. have had a technology partnership in place to support customers that want to develop and deploy ICEfaces portlets within Liferay Portal.
When a portal page is requested for the first time via HTTP GET, portlets built with ICEfaces undergo the RENDER_PHASE of the portlet lifecycle just like any other portlet. From that point on, ICEfaces circumvents normal interaction through the portlet container. When doing a form submission from an ICEfaces portlet, an HTTP POST will not be performed; instead, form submission takes place via Ajax
There are two techniques that developers can use in order to enable Ajax in a JSF view built with ICEfaces component tags:
Note that if the first technique is used, then ICEfaces component tags that are used to perform full form submissions (such as ice:commandButton) must specify partialSubmit=”false”.
Example 6.16. Enabling Ajax in a Facelet view built with ICEfaces component tags
<?xml version="1.0" encoding="UTF-8"?>
<f:view xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/1999/xhtml
http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd">
<ice:portlet>
<ice:form partialSubmit="true">
<ice:panelGrid columns=”2”>
<ice:outputLabel
for=”firstName” value=”First Name” />
<ice:inputText
value=”#{modelManagedBean.firstName}” />
<ice:message for=”firstName” />
<ice:outputLabel
for=”lastName” value=”Last Name” />
<ice:inputText
value=”#{modelManagedBean.lastName}” />
<ice:message for=”lastName” />
<ice:commandButton
action=”#{backingManagedBean.submit}”
partialSubmit=”false” />
</ice:panelGrid>
</ice:form>
</ice:portlet>
</f:view>
Rather than writing markup directly to the response, ICEfaces components render themselves into a server-side Document Object Model (DOM) via the ICEfaces Direct-to-DOM (D2D) RenderKit. When a JSF view is requested for the first time, the markup inside the server-side DOM is delivered to the browser as part of the response. As the user interacts with the UI of the portlet, ICEfaces transparently submits user actions via Ajax and executes the JSF lifecycle. When the RENDER_RESPONSE phase of the JSF lifecycle completes, ICEfaces will compare the previous server-side DOM with the latest server-side DOM and send the incremental page updates back to the browser via the ICEfaces Ajax Bridge. In order to improve the application experience of the end-user, ICEfaces will only permit fields that have been visited by the user to undergo validation during the PROCESS_VALIDATIONS phase of the JSF lifecycle.
This approach is sometimes referred to as “dom-diffing” and provides the following benefits for portlets:
ICEfaces provides the ice:portlet component tag that developers should use to wrap the entire content of each portlet. It implements the javax.faces.component.NamingContainer interface so that it can apply the portlet namespace as the top level of the JSF ID hierarchy. Doing this makes the ID hierarchy more efficient and helps the ICEfaces framework uniquely identify components on the page, which is important when more than one ICEfaces portlet is placed on the same portal page.
ICEfaces 1.x ships with a Portlet 1.0 compliant portlet bridge for deployment of ICEfaces portlets. Note that ICEfaces 2.x will use a Portlet 2.0 compliant bridge that will include the ability to channel the ICEfaces Ajax requests through the Portlet 2.0 RESOURCE_PHASE of the portlet lifecycle.
Example 6.17. Specifying the ICEfaces 1.x Portlet Bridge in the WEB-INF/portlet.xml configuration file, as well as default JSF views that are to be rendered for VIEW mode, EDIT mode, and HELP mode
<portlet-app> <portlet> <portlet-name>my_portlet</portlet-name> <display-name>My Portlet</display-name> <portlet-class> com.icesoft.faces.webapp.http.portlet.MainPortlet </portlet-class> <init-param> <name>com.icesoft.faces.portlet.viewPageURL</name> <value>/xhtml/applicantForm.xhtml</value> </init-param> <init-param> <name>com.icesoft.faces.portlet.editPageURL</name> <value>/xhtml/edit.xhtml</value> </init-param> <init-param> <name>com.icesoft.faces.portlet.helpPageURL</name> <value>/xhtml/help.xhtml</value> </init-param> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> <portlet-mode>help</portlet-mode> </supports> ... </portlet> </portlet-app>
In order to use Facelets with the ICEfaces 1.x Portlet Bridge, it is necessary to use the ICEfaces com.icesoft.faces.facelets.D2DFaceletViewHandler class which is designed to provide support for Facelet views and the D2D RenderKit
Example 6.18. Specifying the ICEfaces D2D view-handler in the WEB-INF/faces-config.xml configuration file
<faces-config> <application> <view-handler> com.icesoft.faces.facelets.D2DFaceletViewHandler </view-handler> </application> </faces-config>
Since ICEfaces 1.x portlets never perform an HTTP post, they do not participate in the ACTION_PHASE of the portlet lifecycle. It is therefore not possible for ICEfaces 1.x portlets to programmatically change the portlet window state. However, ICEfaces portlets can respond accordingly when the user clicks on links provided by the portal that control portlet window states.
ICEfaces provides a feature called Concurrent DOM Views that controls whether or not the ICEfaces framework supports multiple views of a single application from the same browser. When running in a portlet container, ICEfaces needs to treat the separate portlets on a single portal page as distinct views so it is almost always necessary (and therefore safest) to have this parameter set to true.
Example 6.19. Enabling the ICEfaces Concurrent DOM Views feature in the WEB-INF/web.xml configuration file so that separate portlets on the same portal page are treated as distinct views
<context-param> <param-name> com.icesoft.faces.concurrentDOMViews </param-name> <param-value>true</param-value> </context-param>
When developers specify a value of request for the scope a JSF managed-bean, the scope is understood to be very short-lived as it lasts for the duration of a request. When a developer specifies a value of request when using ICEfaces, then the ICEfaces Extended Request scope is applied by default. As an added benefit, the ICEfaces Extended Request scope lends itself quite well to portlets.
ICEfaces 1.x Extended Request scope was one of the inspirations for a new feature in JSF 2.0 called View scope. ICEfaces 2.x applications will use the new JSF 2.0 View scope in place of ICEfaces Extended Request scope.
The ICEfaces Ajax Bridge is responsible for dispatching Ajax requests as a result of user-initiated actions and monitoring the status of each Ajax request. Developers can specify the com.icesoft.faces.connectionTimeout context parameter in the WEB-INF/web.xml configuration file to change the length of time (in milliseconds), that the ICEfaces Ajax Bridge will wait before declaring the connection lost. The default value is 60000 (60 seconds).
Example 6.20. Specifying the length of time in the WEB-INF/web.xml configuration file that the ICEfaces Ajax Bridge will wait before declaring the connection lost
<context-param> <param-name> com.icesoft.faces.connectionTimeout </param-name> <param-value>80000</param-value> </context-param>
The duration of the ICEfaces Extended Request scope begins when the view is first requested, and stays active until one of the following conditions occurs:
To disable the ICEfaces Extended Request scope, developers can specify the com.icesoft.faces.standardRequestScope context parameter in the WEB-INF/web.xml configuration file and set it to false.
Example 6.21. Disabling ICEfaces Extended Request scope and restore standard request scope in the WEB-INF/web.xml configuration file
<context-param> <param-name> com.icesoft.faces.standardRequestScope </param-name> <param-value>true</param-value> </context-param>
While the Portlet 2.0 standard defines techniques for performing inter-portlet communication (IPC), they cause either an HTTP GET or HTTP POST which results in a full page reload and a disruptive end-user experience. ICEfaces provides a natural way for portlets to perform IPC via ICEfaces Ajax Push.
ICEfaces pioneered Ajax Push, which is sometimes referred to as Reverse Ajax or Comet. The technology provides the ability for server-initiated events to cause incremental page updates to be sent to the browser. With ICEfaces Ajax Push, developers can create collaborative and dynamic enterprise applications like never before.
Because the mechanism facilitates asynchronous updates from the server to the client, interaction with one ICEfaces portlet can trigger communication with other ICEfaces portlets by changing values in JSF backing and model managed-beans. This mechanism is not restricted to IPC among portlets on the same portal page in a single browser, but can include updating other browsers that are interacting with the same portal page. The result is not just inter-portlet communication, but inter-portlet, inter-browser communication. Additionally, ICEfaces Ajax Push solves the potentially disruptive end-user experience associated with the Portlet 2.0 standard IPC techniques.
The following is a list of guidelines for achieving IPC with ICEfaces Ajax Push
Example 6.22. Developing a JSF managed-bean in application scope that maintains a chat log that participates in ICEfaces Ajax Push using the SessionRenderer
/*
* This is a file named ChatRoomManagedBean.java
* that is registered as a JSF managed-bean in
* application scope.
*/
import com.icesoft.faces.async.render.SessionRenderer;
import java.util.ArrayList;
import java.util.List;
import javax.faces.event.ActionEvent;
public class ChatRoomManagedBean {
private String messageText;
private List<String> messages = new ArrayList<String>();
private static final String
AJAX_PUSH_GROUP_NAME = "chatRoom";
public ChatRoomsModel() {
SessionRenderer.addCurrentSession(
AJAX_PUSH_GROUP_NAME);
}
public void addMessage(ActionEvent actionEvent) {
messages.add(messageText);
SessionRenderer.render(AJAX_PUSH_GROUP_NAME);
}
public List<String> getMessages() {
return messages;
}
public String getMessageText() {
return messageText;
}
public void setMessageText(String messageText) {
this.messageText = messageText;
}
}
<!-- This is a Facelet view named chatRoom.xhtml -->
<?xml version="1.0" encoding="UTF-8"?>
<f:view xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/1999/xhtml
http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd">
<ice:portlet>
<ice:form>
<ice:dataTable
value=”#{chatRoomManagedBean.messages}”
var=”message”>
<ice:column>
<ice:outputText value=”#{message}” />
</ice:column>
</ice:dataTable>
<ice:inputText
value=”#{chatRoomManagedBean.messageText}” />
<ice:commandButton
actionListener=”#{chatRoomManagedBean.addMessage}” />
</ice:form>
</ice:portlet>
</f:view>
The TritonSource project contains open source demonstration portlets that focus on ICEfaces portlets for Liferay Portal. Specifically, TritonSource has a demonstration portlet featuring an ICEfaces chat room that integrates with Liferay Portal’s “Friends” Social Networking services. When friends sign-in to Liferay Portal, a server-initiated event triggers ICEfaces Ajax Push so that other friends that are online become aware of their friend’s online presence. Additionally, when the user clicks on a “Chat” icon, ICEfaces Ajax Push is used for IPC to begin a new chat room in a Chat Portlet. The TritonSource project website can be found at: http://www.tritonsource.org
The ICEfaces Component Suite fully supports consistent component styling via a set of predefined CSS style classes and associated images. Changing the component styles for a web application developed with the ICEfaces Component Suite is as simple as changing the style sheet used. ICEfaces ships with a set of predefined style sheets are available to be used as-is, or customized to meet the specific requirements of the application. There are five predefined ICEfaces style sheets included, two of which are designed to be used inside a portlet container:
Example 6.23. Specifying the portlet-compatible version of the ICEfaces "XP" theme
<?xml version="1.0" encoding="UTF-8"?> <f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:ice="http://www.icesoft.com/icefaces/component" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3.org/1999/xhtml http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd"> <ice:portlet> <ice:outputStyle href="/xmlhttp/css/xp/xp-portlet.css" /> <ice:form> ... </ice:form> </ice:portlet> </f:view>
The Portlet 1.0 and 2.0 standards document a set of common CSS class names that should be applied to specific page elements in order to integrate with the portlet container’s theme mechanism. When running in a portlet container, ICEfaces components will automatically render the following subset of Portlet 1.0 CSS class names where appropriate
To disable this feature, developers can specify the com.icesoft.faces.portlet.renderStyles context parameter in the WEB-INF/web.xml configuration file and set its value to false.
Example 6.24. Disabling automatic rendering of Portlet 1.0 / 2.0 standard CSS class names in the WEB-INF/web.xml configuration file
<context-param> <param-name> com.icesoft.faces.portlet.renderStyles </param-name> <param-value>false</param-value> </context-param>
Liferay Portal supports styling for Portlet 1.0 and 2.0 standard CSS class names as well as a set of vendor-specific CSS class names within the context of a Liferay theme. However, since Liferay themes do not contain styling for the ICEfaces Component Suite, it is necessary to select an ICEfaces style sheet that is visually compatible with the Liferay theme.
On some occasions, it becomes necessary to override some of the styling in a Liferay theme in order to make it more visually compatible with an ICEfaces portlet. For example, Liferay themes typically render spans of class portlet-msg-error with a margin that has too much space to be placed alongside a rendered ice:inputText component tag.
Example 6.25. Overriding styling in a Liferay theme from within a Facelet view so that rendered output from ice:messages and ice:message have a more narrow margin
<!--
This is a file named my-portlet-view.xhtml
-->
<?xml version="1.0" encoding="UTF-8"?>
<f:view xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/1999/xhtml
http://www.w3.org/2002/08/xhtml/xhtml1-transitional.xsd">
<ice:portlet>
<ice:outputStyle
href="/xmlhttp/css/xp/xp-portlet.css" />
<ice:outputStyle
href="liferay-theme-override.css" />
<ice:form styleClass=”my-portlet-view”>
<ice:messages globalOnly=”true” />
...
</ice:form>
</ice:portlet>
</f:view>
/*
* This is a separate file named liferay-theme-override.css
*/
.my-portlet-view .portlet-msg-error {
margin: 1px 0px 0px 0px;
padding: 1px 5px 1px 24px;
}
When an ICEfaces portlet is added to a portal page at runtime by the end-user, the ICEfaces Ajax Bridge’s window.onload() logic will not be executed unless there is a full page refresh.
As a workaround, Liferay Portal provides configuration parameters that allow the developer to specify that a full page refresh is required. Doing this ensures that the ICEfaces bridge is properly initiated. The required parameters, render-weight and ajaxable, are specified in the WEB-INF/liferay-portlet.xml configuration file
Example 6.26. Specifying that a full page refresh should take place after the ICEfaces portlet is first added to the portal page
<liferay-portlet-app> <portlet> <portlet-name>my_portlet</portlet-name> <instanceable>false</instanceable> <render-weight>1</render-weight> <ajaxable>false</ajaxable> </portlet> </liferay-portlet-app>
In order to ensure compatibility with the Portlet 2.0 Technology Compatibility Kit (TCK), Liferay’s implementation of the javax.portlet.PortletRequest.getAttributeNames() method does not return a complete list of attribute names that are truly present in the PortletRequest object. Although certain attribute names are hidden, if they are known by the portlet developer, their respective values can be retrieved by calling Liferay’s implementation of the javax.portlet.PortletRequest.getAttribute(String name) method.
In order for the ICEfaces 1.x portlet bridge to maintain compatibility with the ICEfaces Extended Request scope, it needs to make a copy of all of the request attributes. In order to ensure that ICEfaces copies the necessary hidden attributes, developers must specify the com.icesoft.faces.portlet.hiddenAttributes context parameter in the WEB-INF/web.xml configuration file.
Example 6.27. Specifying a space-delimited list of hidden request attributes in the WEB-INF/web.xml configuration file
<context-param> <param-name> com.icesoft.faces.portlet.hiddenAttributes </param-name> <param-value>COMPANY_ID LAYOUT RENDER_PORTLET THEME_DISPLAY</param-value> </context-param>
In order to demonstrate usage of PortletFaces in a portlet, Mimacom AG has developed the following example portlets.
Table 6.2. Example Portlets
| Name | Description |
|---|---|
|
Example Liferay Portlet using JSF 1.2 and the Sun OpenPortal JSF Portlet Bridge |
This example shows how to use features of PortletFaces with standard JSF 1.2 component tags. It utilizes the Sun OpenPortal JSF Portlet Bridge in order to run as a portlet within Liferay. Subversion repository URL: http://repo.edorasframework.org/svn/examples/trunk/org.edorasframework.example.portlet.liferay.jsf.1.2/ |
|
Example Liferay Portlet using ICEfaces 1.x and the ICEfaces 1.x Portlet Bridge |
This example shows how to use features of PortletFaces with ICEfaces 1.x component tags. It utilizes the ICEfaces 1.x Portlet Bridge in order to run as a portlet within Liferay. ICEfaces partialSubmit is enabled so that form validation failures are reported back to the user without having to submit the form. Additionally, the ICEfaces ice:inputFile tag is used in conjunction with an ice:outputProgress tag to support file uploads with a progress indicator. Subversion repository URL: http://repo.edorasframework.org/svn/examples/trunk/org.edorasframework.example.portlet.liferay.icefaces.1.x/ |
JSF web application developers typically call FacesContext.getCurrentInstance() in order to obtain the ThreadLocal
singleton instance associated with the current request. While JSF portlet developers can certainly do the
same, it's easier to call PortletFacesContext.getInstance() which returns an application-scoped singleton instance.
Example 6.28. Obtaining the PortletFacesContext Singleton Instance
public class SessionScopedManagedBean {
private PortletFacesContext portletFacesContext = PortletFacesContext.getInstance();
public List<DlFileEntry> getDocuments() {
List<DLFileEntry> documents;
try {
documents = DLFileEntryLocalServiceUtil.getFileEntries(folderId);
}
catch (Exception e) {
LOG.error(e.getMessage(), e);
// Don't have to call PortletFacesContext.getInstance() first since
// a reference to it was obtained when the bean was created.
portletFacesContext.addGlobalUnexpectedErrorMessage();
}
return documents;
}
}
PortletFacesContext is an abstract class that extends the edoras framework's ExtFacesContext abstract class, which in turn extends the JSF FacesContext abstract class. Since PortletFacesContext ultimately extends FacesContext, it supplies all the same method signatures and can therefore do anything that FacesContext can do. Both PortletFacesContext and ExtFacesContext implement the delegation design pattern for methods defined by FacesContext by first calling FacesContext.getCurrentInstance() and then delegating to corresponding methods. The benefit of using this technique is that JSF portlet developers only have to call PortletFacesContext.getInstance() once, and can save the singleton object reference for future use.
PortletFaces introduces several variables into the Expression Language (EL).
Table 6.3. PortletFaces EL Variables
| EL Variable | Description |
|---|---|
i18n
|
As an abbreviation for the word "internationalization", the i18n EL
variable enables page authors to declaratively specify message keys that hook into Liferay's
Language Utility.
Type:
|
liferay
|
Utility managed-bean that is designed to be kept in JSF request scope.
Its purpose is to introduce some Liferay-specific variables into the JSF
EL.
Type:
|
liferay.companyId
|
The Liferay companyId primary key value associated with the community/organization portal
page that the current portlet is placed upon.
Type:
|
liferay.documentLibraryURL
|
The absolute URL for the Liferay Document Library Struts action
path.
Type:
|
liferay.imageGalleryURL
|
The absolute URL for the Liferay Image Gallery Struts action
path.
Type:
|
liferay.imageURL
|
The absolute URL for the Liferay Image Servlet.
Type:
|
liferay.groupUser
|
The Liferay User that owns the Liferay community/organization portal
page that the current portlet is placed upon.
Type:
|
liferay.layout
|
The Liferay Layout associated with the community/organization portal
page that the current portlet is placed upon.
Type:
|
liferay.permissionChecker
|
The Liferay PermissionChecker associated with the current request and
Liferay User.
Type:
|
liferay.portalURL
|
The absolute URL for the portal.
Type:
|
liferay.portlet
|
the containing Liferay Portlet associated with the
PortletRequest.
Type:
|
liferay.portraitURL
|
Designed to be called from the EL by passing a Liferay User or
userId as an array index, returns the absolute URL to the user's
portrait.
Type:
|
liferay.service
|
Designed to be called from the EL by passing a Liferay service name String (bean id) as
an array index, returns the instance of the service class.
Type:
|
liferay.theme
|
The Liferay Theme associated with the Liferay
Layout.
Type:
|
liferay.themeDisplay
|
The Liferay ThemeDisplay associated with the
PortletRequest.
Type:
|
liferay.themeImageURL
|
Designed to be called from the EL by passing a relative path to a theme image as an array
index, returns the absolute URL to the theme image.
Type:
|
liferay.themeImagesURL
|
The absolute URL for the image path associated with the current Liferay
Theme.
Type:
|
liferay.user
|
The Liferay User associated with the
PortletRequest.
Type:
|
liferay.userHasPortletPermission
|
Designed to be called from the EL by passing an action-key as an array
index, returns a Boolean indicating whether or not the Liferay
User associated with the PortletRequest has permission to
execute the specified action-key on the current
portlet.
Type:
|
As an abbreviation for the word "internationalization", the i18n
EL variable enables page authors to declaratively specify message keys that are provided by one of the
following:
The Liferay Language Utility is typically accessed by portlet developers by calling static Java
methods found in the LanguageUtil class. The utility operates by reading the
locale-specific version of the portal's Language.properties file, which contains thousands of keys and
internationalized messages.
Portlet developers can extend the Liferay Language Utility by creating a file within the portlet WAR
named WEB-INF/liferay-hook.xml that points to locale-specific resource bundles
that are in the runtime classpath of the portlet.
Example 6.29. WEB-INF/liferay-hook.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 5.2.0//EN" "http://www.liferay.com/dtd/liferay-hook_5_2_0.dtd"> <hook> <language-properties>Language_en_US.properties</language-properties> </hook>
Example 6.30. Contents of Language_en_US.properties
add-new-entry=Add New Entry save-entry=Save Entry
When using JBoss EL, page authors can take advantage of the i18n.replace() method
in order to substitute values into the text of the message.
Example 6.32. Usage of the i18n EL Variable with JBoss EL
<!--
Note: The US English translation of the x-has-x-friends key would look like the following:
x-has-x-friends={0} has {1} friends.
-->
<h:outputText value="#{i18n.replace('x-has-x-friends', liferay.groupUser.fullName, friendsModel.dataModel.rowCount)}" />
This is a utility managed-bean that is designed to be kept in request scope. Its purpose is to introduce some Liferay-specific variables into the JSF EL. The reason why this is implemented as a managed-bean (and not as an ELResolver) is because of the way ICEfaces 1.x handles Ajax requests. When an ICEfaces portlet is first rendered to the portal page, the ICEfaces portlet bridge participates in a normal manner with the portlet lifecycle. However, all user interactions that trigger an Ajax XMLHttpRequest (like partialSubmit) bypass the portlet lifecycle and go directly to the ICEfaces PersistentFacesServlet. While this has great benefits for speed, it introduces problems when ICEfaces portlets need to interact with Liferay resources that are scoped to the portlet lifecycle. For example, the Liferay ThemeDisplay object is available as a request attribute in the portlet lifecycle, but when the lifecycle is complete, Liferay's ServicePostAction will call the ThemeDisplay.recycle() method which invalidates all of the properties. If the portlet utilizing this managed bean is an ICEfaces portlet, then ICEfaces will place the instance of this class in its "extended" request scope, which will keep the Liferay-specific values in existence long after the portlet lifecycle has completed. If the portlet is a standard JSF portlet, then the JSF managed-bean facility will place the instance of this class in standard request scope. This introduces a small amount of overhead for standard JSF portlets, but is necessary in order to have PortetFaces supply a standard API for both ICEfaces portlets and JSF portlets.
The Liferay companyId primary key value associated with the community/organization portal page that the current portlet is placed upon.
Example 6.33. EL Usage of liferay.companyId
<h:outputText value="#{liferay.companyId} is the companyId associated with this set of Liferay Portal pages." />
The absolute URL for the Liferay Document Library Struts action path prefix. The most common use case is to append the /get_file suffix and some additional request parameters in order to provide a hyperlink to a document in the Liferay Document Library. See the Liferay struts-config.xml file for a complete list of available suffixes.
Example 6.34. EL Usage of liferay.documentLibraryURL
<h:dataTable id="documents" value="#{documentModelBean.dataModel}" var="dlFileEntry">
<h:column>
<f:facet name="head">
<h:outputText value="#{i18n['file-name']}" />
</f:facet>
<h:outputLink
target="_blank"
value="#{liferay.documentLibraryURL}/get_file?p_l_id=#{liferay.themeDisplay.plid}&folderId=#{dlFileEntry.folderId}&name=#{dlFileEntry.name}">
<h:outputText value="#{dlFileEntry.title}" />
</h:outputLink>
</h:column>
</h:dataTable>
The Liferay User that owns the Liferay community/organization portal page that the
current portlet is placed upon.
Example 6.35. EL Usage of liferay.groupUser
<h:outputText
value="The user named #{liferay.groupUser.fullName} owns this set of Liferay Portal pages." />
The absolute URL for the Liferay Image Gallery Struts action path prefix. See the Liferay struts-config.xml file for a complete list of available suffixes.
The absolute URL for the Liferay Image Servlet. Although this can be used to construct a URL that
points a Liferay user's portrait/photo, for performance reasons, it is better to use the portraitURL EL variable instead.
The Liferay Layout associated with the community/organization portal page that the
current portlet is placed upon.
Example 6.36. EL Usage of liferay.layout
<h:outputText
value="The name of this portal page is #{liferay.layout.name}" />
The Liferay PermissionChecker associated with the current request and Liferay
User.
Example 6.37. EL Usage of liferay.permissionChecker
<h:commandButton actionListener="#{backingBean.save}" rendered="#{liferay.permissionChecker.companyAdmin}" value="#{i18n['save']}" />
The containing Liferay Portlet associated with the
PortletRequest.
Example 6.38. EL Usage of liferay.portlet
<h:outputText
value="The name of this portlet is #{liferay.portlet.displayName}" />
Designed to be called from the EL by passing a Liferay User or
userId as an array index, returns the absolute URL to the user's portrait.
Example 6.39. EL Usage of liferay.portraitURL
<h:graphicImage value="#{liferay.portraitURL[liferay.group.user]}" />
Designed to be called from the EL by passing a Liferay service name String (bean id) as an array
index, returns the instance of the service class. Liferay manages instances of services using
Liferay-extended versions of the Spring XmlWebApplicationContext BeanFactory class named PortalApplicationContext
and PortletApplicationContext. The liferay.service extension to
the EL is meant to be used to resolve Liferay services so that they can be injected into JSF managed
beans via the managed-property feature inside of the portlet's
WEB-INF/faces-config.xml file.
Example 6.40. EL Usage of liferay.service (WEB-INF/faces-config.xml)
<managed-bean>
<managed-bean-name>modelManagedBean</managed-bean-name>
<managed-bean-class>mypackage.ModelManagedBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<!-- Inject the service into the model managed bean. -->
<managed-property>
<property-name>myService</property-name>
<value>#{liferay.service['myservice.bean.id']}</value>
</managed-property>
</managed-bean>
The Liferay Theme associated with the Liferay Layout.
Example 6.41. EL Usage of liferay.theme
<h:outputText value="The name of the Liferay theme applied to this portal page is #{liferay.theme.name}" />
The Liferay ThemeDisplay associated with the PortletRequest.
Perhaps it is easier to think of the Liferay ThemeDisplay as a "display context" which provides access
to a wealth of information including the current Company, User, Layout, Theme, PermissionChecker, and
more.
Example 6.42. EL Usage of liferay.themeDisplay
<link href="#{liferay.themeDisplay.uRLSignIn}">#{i18n['sign-in']}</link>
Designed to be called from the EL by passing a relative path to a theme image as an array index, returns the absolute URL to the theme image.
Example 6.43. EL Usage of liferay.themeImageURL
<h:graphicImage value="#{liferay.themeImageURL['/common/delete.png']}" />
Returns the absolute URL for the image path associated with the current Liferay Theme. For example: http://localhost:8080/image/image_gallery.
Example 6.44. EL Usage of liferay.themeImagesURL
<h:graphicImage value="#{liferay.themeImagesURL}/common/delete.png" />
the Liferay User associated with the PortletRequest.
Example 6.45. EL Usage of liferay.user
<h:outputText value="#{i18n['welcome']}, #{liferay.user.firstName}" />
Designed to be called from the EL by passing an action-key as an array index, returns a
Boolean indicating whether or not the Liferay User associated with the
PortletRequest has permission to execute the specified action-key on the
current portlet. The action-key is typically defined in a Liferay
resource-action-mapping XML file that defines the
<portlet-resource/> and <model-resource/>
permissions associated with a Liferay portlet. Please refer to the
portal-impl/classes/resource-actions/messageboards.xml file in the Liferay Portal source code
distribution for an example of how to write a Liferay resource-action-mapping XML
file.
Example 6.46. EL Usage of liferay.userHasPortletPermission
<h:dataTable
rendered="#{liferay.userHasPortletPermission['VIEW']}"
value="#{modelManagedBean.users}"
var="user">
</h:dataTable>
PortletFaces provides the following UIComponent tags as part of its component suite.
Table 6.4. UIComponent Tags
| Tag | Description |
|---|---|
pf:inputFile
|
Renders an HTML <input type="file" /> tag which provides file
upload capability.
|
pf:inputRichText
|
Renders a text area that provides the ability to enter rich text such as bold, italic, and underline. |
pf:permissionsLink
|
Renders an HTML anchor tag (hyperlink) that the user can click on in order to see the Liferay Permissions screen for the associated resource. |
The pf:inputFile tag renders an HTML <input type="file"
/> tag which enables file upload capability.
Usage of this tag requires a faces-context-factory element to be placed in the
WEB-INF/faces-config.xml file. See the File Upload section for more details.
Table 6.5. Attributes
| Attribute | Type | Description | Required |
|---|---|---|---|
| id | String | The identifier of the component | false |
| rendered | Boolean | Boolean flag indicating whether or not this component is to be rendered during the RENDER_RESPONSE phase of the JSF lifecycle. The default value is “true”. | false |
| value | java.io.File | The value of the component, which will be a file on the server. | true |
Example 6.47. Example usage of pf:inputFile tag
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:view xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:pf="http://portletfaces.org/facelets">
<h:form enctype="multipart/form-data">
<pf:inputFile value="#{managedBean.file}" />
<h:commandButton action="#{fileUploadManagedBean.submit}" value="Upload File" />
</h:form>
</f:view>
public class FileUploadManagedBean {
private File file;
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public String submit( {
System.out.println("Uploaded file: " + file);
return "filesUploaded";
}
}
The pf:inputRichText tag renders a text area that provides the ability to enter
rich text such as bold, italic, and underline. The renderer relies on the CKEditorTM to provide the rich
text editing area.
Since Liferay bundles the CKEditorTM JavaScript and related images with the portal, the portlet developer does not need to include it with the portlet.
Table 6.6. Attributes
| Attribute | Type | Description | Required |
|---|---|---|---|
| id | String | The identifier of the component | false |
| rendered | Boolean | Boolean flag indicating whether or not this component is to be rendered during the RENDER_RESPONSE phase of the JSF lifecycle. The default value is “true”. | false |
| value | String | The value of the component, the HTML fragment generated by the end-user. | true |
Example 6.48. Example usage of pf:inputRichText tag
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:view xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:pf="http://portletfaces.org/facelets">
<h:form>
<pf:inputRichText id="comments" value="#{modelManagedBean.comments}" />
</h:form>
</f:view>
The pf:permissionsLink tag renders an HTML anchor tag (hyperlink) that the user can
click on in order to see the Liferay Permissions screen for the associated resource.
Table 6.7. Attributes
| Attribute | Type | Description | Required |
|---|---|---|---|
| id | String | The identifier of the component | false |
| modelResource | String | The fully qualified Java class of the model resource. For example:
MyModelResource.class.getName()
|
true |
| modelResourceDescription | String | The description of the model resource. | false |
| redirect | Boolean | The redirect URL. | false |
| rendered | Boolean | Boolean flag indicating whether or not this component is to be rendered during the RENDER_RESPONSE phase of the JSF lifecycle. The default value is “true”. | false |
| resourcePrimKey | String | The primary key value of the model resource. | false |
| value | String | The text of the hyperlink that the user will see and click on. | false |
Example 6.49. Example usage of pf:permissionsLink tag
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:view xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:pf="http://portletfaces.org/facelets">
<h:form>
<pf:permissionsLink
modelResource="org.portletfaces.myproject.model.Book"
modelResourceDescription="Book"
resourcePrimKey="#{modelManagedBean.book.bookId} />
</h:form>
</f:view>
PortletFaces provides the following Facelet Composite Component tags as part of its component suite.
Table 6.8. Facelet Composite Component Tags
| Tag | Description |
|---|---|
pf:iceInfoDataPaginator
|
Encapsulates an ICEfaces ice:dataPaginator tag that renders pagination information for an associated ice:dataTable. The navigation information will match the internationalized Liferay "showing-x-x-of-x-results" message. |
pf:iceNavDataPaginator
|
Encapsulates an ICEfaces ice:dataPaginator tag that renders navigation controls for an associated ice:dataTable. The icons will match the current Liferay theme. |
pf:icon
|
Encapsulates an HTML img tag whose src attribute contains a
fully qualified URL to an icon in the Liferay theme.
|
pf:messages
|
Encapsulates the h:messages tag and automatically applies the JSR 286 standard class names. |
pf:message
|
Encapsulates the h:message tag and automatically applies the JSR 286 standard class names. |
The pf:iceInfoDataPaginator encapsulates an ICEfaces ice:dataPaginator
tag that renders pagination information for an associated ice:dataTable. The
navigation information will match the internationalized Liferay "showing-x-x-of-x-results"
message.
Table 6.9. Attributes
| Attribute | Type | Description | Required |
|---|---|---|---|
| for | String | Corresponds to the value of an id attribute for an ice:dataTable tag.
|
true |
| value | String | Specifies a string that contains replacement tokens for each piece of pagination information. The default value is the internationalized Liferay "showing-x-x-of-x-results" message. | true |
Example 6.50. Example usage of pf:iceInfoDataPaginator tag
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:view xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:pf="http://portletfaces.org/facelets">
<pf:iceInfoDataPaginator for="dataTable1" />
<ice:dataTable id="dataTable1" value="#{modelManagedBean.rows}" var="row">
...
</ice:dataTable>
</f:view>
The pf:iceInfoDataPaginator encapsulates an ICEfaces ice:dataPaginator
tag that renders navigation controls for an associated ice:dataTable. The
icons will match the current Liferay theme.
Table 6.10. Attributes
| Attribute | Type | Description | Required |
|---|---|---|---|
| fastForwardIconRendered | Boolean | Boolean flag indicating whether or not the "Fast Forward" button/icon is rendered. The default value is "false". | false |
| fastRewindIconRendered | Boolean | Boolean flag indicating whether or not the "Fast Rewind" button/icon is rendered. The default value is "false". | false |
| firstIconRendered | Boolean | Boolean flag indicating whether or not the "First" button/icon is rendered. The default value is "true". | false |
| for | String | Corresponds to the value of an id attribute for an ice:dataTable tag.
|
true |
| lastIconRendered | Boolean | Boolean flag indicating whether or not the "Last" button/icon is rendered. The default value is "true". | false |
| nextIconRendered | Boolean | Boolean flag indicating whether or not the "Next" button/icon is rendered. The default value is "true". | false |
| paginator | Boolean | Boolean flag indicating whether or not the page number links will be rendered. This is a pass-through attribute for the encapsulated ice:dataPaginator. The default value is "true". | false |
| paginatorMaxPages | Integer | The maximum amount of pages to be displayed in the paginator. This is a pass-through attribute for the encapsulated ice:dataPaginator. The default value is 7. | false |
| previousIconRendered | Boolean | Boolean flag indicating whether or not the "Previous" button/icon is rendered. The default value is "true". | false |
Example 6.51. Example usage of pf:iceNavDataPaginator tag
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:view xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:pf="http://portletfaces.org/facelets">
<pf:iceNavDataPaginator for="dataTable1" />
<ice:dataTable id="dataTable1" value="#{modelManagedBean.rows}" var="row">
...
</ice:dataTable>
</f:view>
The pf:icon tag encapsulates an HTML img tag whose src
attribute contains a fully qualified URL to an icon image in the current Liferay theme.
Table 6.11. Attributes
| Attribute | Type | Description | Required |
|---|---|---|---|
| alt | String | Corresponds to the value of the alt attribute for the embedded
img tag. The default value is Liferay's internationalized message
for the key named view.
|
false |
| image | String | The name of the theme image icon, which can be the prefix of any image filename in
the Liferay theme's "common" image folder. The default value is
view.
|
false |
Example 6.52. Example usage of pf:icon tag
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:view xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:pf="http://portletfaces.org/facelets">
<pf:icon alt="#{i18n['delete']}" image="delete" />
</f:view>
The pf:messages tag encapsualtes the h:messages tag and automatically applies the JSR 286 standard class names. See Liferay Theme Integration for more
information.
Table 6.12. Attributes
| Attribute | Type | Description | Required |
|---|---|---|---|
| globalOnly | Boolean | Boolean flag indicating whether or not only global messages should be rendered. | false. |
Example 6.53. Example usage of pf:messages tag
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:view xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:pf="http://portletfaces.org/facelets">
<h:form>
<pf:messages globalOnly="true" />
<h:panelGrid columns="3">
<h:outputLabel for="dateOfBirth" />
<h:inputText id="dateOfBirth" value="#{modelManagedBean.dateOfBirth}" />
<pf:messages for="dateOfBirth" />
</h:panelGrid>
<h:commandButton action="#{backingManagedBean.submit}" />
</h:form>
</f:view>
The pf:message tag encapsualtes the h:message tag and automatically applies the JSR 286 standard class names. See Liferay Theme Integration for more
information.
Table 6.13. Attributes
| Attribute | Type | Description | Required |
|---|---|---|---|
| for | String | Corresponds to the value of the id attribute of an associated JSF
component tag such as h:inputText or h:selectOneMenu.
|
true |
Example 6.54. Example usage of pf:message tag
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:view xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:pf="http://portletfaces.org/facelets">
<h:form>
<pf:messages globalOnly="true" />
<h:panelGrid columns="3">
<h:outputLabel for="dateOfBirth" />
<h:inputText id="dateOfBirth" value="#{modelManagedBean.dateOfBirth}" />
<pf:messages for="dateOfBirth" />
</h:panelGrid>
<h:commandButton action="#{backingManagedBean.submit}" />
</h:form>
</f:view>
PortletFaces provides the following converter tags.
Table 6.14. Converter Tags
| Tag | Description |
|---|---|
pf:convertDateTime
|
Extends the default JSF date/time converter to support the output of Calendar, and takes the TimeZone and Locale of the current Liferay user into account. |
The pf:convertDateTime tag extends the default JSF date/time converter to support
the output of Calendar, and takes the TimeZone and Locale of the current Liferay user into
account.
Example 6.55. Example usage of pf:convertDateTime tag
<h:inputText value="#{modelManagedBean.calendar}">
<pf:convertDateTime />
</h:inputText>
The JSF 1.x and 2.x specifications have not implemented support for HTML forms with
enctype="multipart/form-data" and are therefore do not support file uploads. Various
workarounds exist for use with the JSP view-handler, such as introducing a servlet filter or phase listener
to intercept the request. With the advent of the Facelets view-handler, these techniques
have become largely ineffectual. The problem has to do with the FaceletViewHandler
restoreView(FacesContext context, String viewId) method which is unable to retrieve the
javax.faces.ViewState request parameter for HTML forms with
enctype="multipart/form-data". The underlying reason why it can't retrieve the
parameter is because the ExternalContextImpl.getRequestParameterMap() method in the JSF
Reference Implementation is not equipped to handle these types of postback requests.
The PortletFaces project solves this problem by providing wrapper implementations of
FacesContext, FacesContextFactory, and
ExternalContext. These can be activated by placing the following
<faces-context-factory> element inside of your portlet WAR's
WEB-INF/faces-config.xml descriptor:
Example 6.56. Example usage of faces-context-factory element
<factory> <faces-context-factory>org.edorasframework.portletfaces.bridge.sun.FacesContextFactoryImpl</faces-context-factory> </factory>
This will ultimately cause the FacesContext.getExternalContext() method to return an instance of the org.edorasframework.portletfaces.bridge.sun.ExternalContextImpl class. This class overrides the ExternalContext.getRequestParameterMap() method, which uses Apache Commons FileUpload project to parse the request parameters. When used in conjunction with the pf:inputFile tag, JSF portlet developers can provide file upload capability in their Facelet views.
Simiar to the JSR 301/329 portletPreferences EL variable, PortletFaces introduces an EL
variable named portletPreference (singlular, not plural). Although the EL usage is
similar in syntax, the implementation is different so that it is compatible with ICEfaces paritalSubmit
(Ajax). One drawback of JSR 301/329 portletPreferences EL variable is that when used with
ICEfaces partialSubmit, user preference selections from the EDIT mode view will be
directly set inside the underlying PortletPreferences object. Even though the user might not click a "Save Preferences" button to
store the preferences permanently, the preference values might be active for the remainder of the PortletSession. The PortletFaces portletPreference EL variable does not store submitted
values directly inside of the underlying PortletPreferences object. Instead, the submitted values are stored in a temporary copy, which
solves the problem related to ICEfaces partialSubmit. Additionally, PortletFaces provides a convenience
backing managed-bean named PortletPreferencesForm that has an action-listener named PortletPreferencesForm.submit() that will permanently store the portlet preferences.
Example 6.57. EDIT Mode with PortletFaces and ICEfaces Partial Submit
<!--
This is a file named edit.xhtml that can be used for portlet EDIT mode.
It utilizes the PortletFaces portletPreference EL variable for gaining
read/write access to javax.portlet.PortletPreferences. Unlike the
JSR 301/329 approach, there is no need to write a backing managed-bean
because PortletFaces already provides one named portletPreferencesForm.
-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<f:view xmlns:f="http://java.sun.com/jsf/core"
xmlns:ice="http://www.icesoft.com/icefaces/component">
<ice:messages globalOnly="true" />
<ice:form partialSubmit="true">
<ice:outputLabel for="datePattern" />
<ice:inputText id="datePattern" required="true" value="#{portletPreference['datePattern']}" />
<ice:message for="datePattern" />
<ice:commandButton actionListener="#{portletPreferencesForm.submit}" partialSubmit="false" value="Save Preferences" />
</ice:form>
</f:view>
PortletFaces offers several features that help integrate JSF portlets with the current Liferay theme.
PortletFaces provides the PortletFacesContext.getThemeDisplay() method at the Java level and also the liferay.themeDisplay EL variable for getting access to the Liferay ThemeDisplay object.
PortletFaces provides the pf:icon Facelet composite component tag
that encapsulates an HTML img tag whose src attribute contains a fully
qualified URL to an icon image in the current Liferay theme. Additionally, PortletFaces provides the
liferay.themeImagesURL and liferay.themeImageURL Facelet composite component tags
for gaining access to theme image icons.
Most of the standard JSF HTML component tags render themselves as HTML markup such as
<label />, <input />, <span
/>, etc. and assume the current Liferay theme thanks to the power of CSS. However, the
h:messages and h:message tag will not assume the current Liferay theme unless the following JSR 286
standard CSS class names are applied:
portlet-msg-error
portlet-msg-info
portlet-msg-warn
Example 6.58. JSR 286 standard CSS class names applied to the h:messages tag
<h:messages errorClass="portlet-msg-error" fatalClass="portlet-msg-error" infoClass="portlet-msg-info" warnClass="portlet-msg-warn" />
As a convenience, PortletFaces provides the pf:messages and pf:message Facelet composite component tags that encapsulate the h:messages and h:message tags respectively, and automatically apply the JSR 286 standard class names as shown above.
When running as a portlet, the ICEfaces ice:messages and ice:message component tags automatically apply the JSR 286 standard class names as shown above. Additionally the ice:dataTable component tag will apply the following JSR 286 standard class names for alternating table rows:
portlet-section-alternate
portlet-section-body
In a normal JSF web application, the Locale that is used
to display internationalized values is dictated by the locale specified in the end-user's web-browser.
However, Liferay Portal permits the user to select a different locale (language) using the Language Portlet.
The user's choice is ultimately saved as a languageId in the Liferay User object and is persisted to the database.
In order to provide seamless integration between JSF portlets and the language selected by the user,
PortletFaces provides the LiferayLocalePhaseListener. The listener monitors the RESTORE_VIEW phase of the JSF lifecycle
and will automatically set the locale inside the UIViewRoot according to the value specified by Liferay's User.getLocale() method, which is aware of the selected languageId. This in
turn causes internationalization techniques such as the f:loadBundle tag and the i18n EL keyword to
automatically translate message keys into the language selected by the user with the Liferay Language
Portlet.
The ICEfaces 1.x extended-request scope is very similar to what JSF 2.0 now calls "view" scope. While normal JSF request scope keeps a managed-bean in memory for the duration of a request, the ICEfaces extended-request scope typically keeps a managed-bean in memory for a longer duration. The scope begins when a JSF view is first requested, and terminates under one of the following conditions:
When an ICEfaces portlet is rendered on a portal page, the ICEfaces 1.x portlet bridge will participate in the Portlet RenderRequest phase just like any other portlet. However, from that time on the ICEfaces 1.x bridge will bypass the portlet lifecycle and perform all subsequent interactions with the server via Ajax by hitting the ICEfaces PersistentFacesServlet. The ICEfaces 1.x portlet bridge is based on the JSR 168 (Portlet 1.0) API and therefore cannot take advantage of the new ResourceRequest phase of the JSR 286 (Portlet 2.0) API. The benefit of hitting the PersistentFacesServlet directly is that ICEfaces partial submits will execute faster than if they went through the the Portlet 2.0 ResourceRequest phase. The drawback is that certain Liferay objects are unusable after the initial RenderRequest phase. Specifically, the Liferay PermissionChecker and ThemeDisplay objects get "recycled" at then end of the RenderRequest phase by Liferay's ServicePostAction.
PortletFaces works around this problem by making copies of Liferay's PermissionChecker and ThemeDisplay objects and keeping them in "request" scope, which will default to the ICEfaces extended-request scope when ICEfaces is used in a portlet. These objects are available to JSF portlet developers by calling the PortletFacesContext.getPermissionChecker() and PortletFacesContext.getThemeDisplay() methods respectively.
Example 6.59. Getting the Liferay PermissionChecker and ThemeDisplay objects from the PortletFacesContext
import com.liferay.portal.security.permission.PermissionChecker;
import com.liferay.portal.theme.ThemeDisplay;
public class BackingManagedBean {
PortletFacesContext portletFacesContext =
PortletFacesContext.getInstance();
public String submit() {
PermissionChecker permissionChecker =
PortletFacesContext.getPermissionChecker();
ThemeDisplay themeDisplay =
PortletFacesContext.getThemeDisplay();
}
}
Sometimes it is very usefull to serialize simple java objects to an XML stream and to deserialize them later back from the stream. XStream offers very powerfull functionality for serializing objects to XML and back again, however, if the attributes and types of such a serialized class change, existing XML strings might not be parsed back to objects again.
The xmlpersister within the edoras framework offers a simple, annotation driven way of using XStream in any java class with no such limitations. As you add a new attribute to your class or remove an existing one, it does not matter, even handling of existing streams based on the old class is possible without failure.Basically, where a simple XML serialization of java objects is needed where the XML stream stays longer as the lifetime of the appropriate virtual machine creating them (e.g. the XML stream is persisted somehow), the xmlpersister is a good way to deal with.
It all starts with an XmlPersister whose default implementation is the DefaultXmlPersister. There is no need of any configuration for this component being used, except that every Java class used with the xmlpersister for serializing must be registered in order to let the persister scan its annotations.
There are two ways to do that, either register all classes statically within the DefaultXmlPersister somewhere:
static {
// register all classes to be serialized using the xmlpersister
DefaultXmlPersister.registerPersistableClass(PersistableClass1.class);
DefaultXmlPersister.registerPersistableClass(PersistableClass2.class);
}or subclass the DefaultXmlPersister with your own class, registering the classes as a static initialization:
public class YourXmlPersister extends DefaultXmlPersister {
static {
// all your static registrations go in here
DefaultXmlPersister.registerPersistableClass(PersistableClass.class);
}
public ProcessXmlPersister() {
// do some more initialization, if needed
}
}
In order to tell the xml persister which fields to persist, there are a couple of annotations available. Unlike the basic XStream framework, the xmlpersister does not persist fields automatically unless they have an annotation on it.
The first annotation to be placed on the class itself is the @XmlPersistable annotation to tell the persister that objects of that class are persistable and how their alias should be in the XML stream.
@XmlPersistable
public class SamplePersistableClass {
}
Optionally, the alias to be used by the xml persister can be provided, otherwise, the convention is used instead where the persister is taking the simple class name as the alias.
@XmlPersistable("Alias")
public class SamplePersistableClass {
}
Any field needed to be streamed into an attribute of the xml node being created for the object needs to be annotated with the @XmlPersistedAttribute annotation:
@XmlPersistable
public class SamplePersistableClass {
/** This field is serialized as an attribute using name "description". */
@XmlPersistedAttribute
private String description;
/** This field is not serialized as it does not have the @XMLPersitedAttribute annotation. */
private int number;
}
A field which itself is an annotated class can be written to the stream as a sub-node using the @XmlPersistedObject annotation:
@XmlPersistable
public class SamplePersistableClass {
/** This object is recursively serialized to XML using a sub-node. */
@XmlPersistedObject
private SamplePersistableClass parent;
}
@XmlPersistable
| Parameter | Description | optional? |
|---|---|---|
| value | The name of the alias used for persisting the object as a node in the XML stream. | yes |
@XmlPersistedAttribute
| Parameter | Description | optional? |
|---|---|---|
| value | The name of the attribute in the XML node. If not provided, the name of the field is taken as the convention. | yes |
| defaultValue | Optionally used to define the default value for the annotated attribute. This will be used if either the attribute was not found or if parsing the value found was leading into an exception. This will avoid the deserialization to fail, even if attribute values are not parsable. | yes |
| delimiter | The optional delimiter to be used, if the attribute type is a java.util.List containing String elements. Defaults to DefaultXmlPersistingHandler.STRING_LIST_DELIMITER. | yes |
| converter | If the attribute value is a list of elements T, the converter must be provided, implementing the interface org.edorasframework.xmlpersister.ListElementConverter for the same type T. | yes |
@XmlPersistedObject
| Parameter | Description | optional? |
|---|---|---|
| value | The name of the attribute in the XML node for this object. If not provided, the name of the field is taken as the convention. | yes |
| implementation | If this annotation is placed on a collection field, this parameter must be provided as the implementation to be created while deserializing, if the interface type is used as the field type. For instance, if the field type is java.util.List, the implementation might be java.util.ArrayList. | no for a Collection based field type |
| accessor | If this annotation is placed on a field having an implementation with a collection of elements, this parameter must be provided, if the collection does not implement the java.util.Collection interface. The class provided with this parameter must implement the org.edorasframework.xmlpersister.CollectionAccessor interface. | yes |
Assume the following simple Java class:
@XmlPersistable
public class SamplePersistableClass {
/** This field is serialized as an attribute using name "description". */
@XmlPersistedAttribute
private String description;
/** This field is not serialized as it does not have the @XMLPersitedAttribute annotation. */
private int number;
/** This object is recursively serialized to XML using a sub-node. */
@XmlPersistedObject
private SamplePersistableClass parent;
/** A list of strings persisted as a single attribute having delimiter ';'. */
@XmlPersistedAttribute(delimiter = ";")
private List<String> messages;
// ... followed by getters and setters ...
The following code would produce an XML:
SamplePersistableClass parent = new SamplePersistableClass();
parent.setDescription("Parent Object");
parent.setNumber(10);
SamplePersistableClass child = new SamplePersistableClass();
child.setDescription("Child Object");
child.setParent(parent);
parent.addMessage("message 1");
parent.addMessage("message 2");
parent.addMessage("message 3");
DefaultXmlPersister persister = new DefaultXmlPersister();
String xml = persister.persistObject(child);
System.out.println(xml);
The output of this code snippet would be:
<SamplePersistableClass description="Child Object"> <SamplePersistableClass objectName="parent" description="Parent Object" messages="message 1;message 2;message 3"/> </SamplePersistableClass>
Here are some more examples on how to use the annotations:
@XmlPersistable
public class SamplePersistableClass {
// this will convert the list of objects to a single, delimited string
// using the converter, stored in a single attribute of the XML node
@XmlPersistedAttribute(converter = PersistetObjectConverter.class)
private List<PersistetObject> objects;
The converter could be as simple as the following:
public class PersistetObjectConverter implements ListElementConverter<PersistetObject> {
public String convertToString(PersistetObject element) {
StringBuilder buf = new StringBuilder();
buf.append(element.getTest());
buf.append(";");
buf.append(element.getNumber());
return buf.toString();
}
public String getElementDelimiter() {
return "|";
}
public PersistetObject parseFromString(String element) {
String[] splits = element.split(";");
return new PersistetObject(splits[0], Integer.parseInt(splits[1]));
}
}
Using default values allows you to add a new field to a class, annotate it with a default value to be able to read in existing XML streams without that specific field. If there is no such default value specified, the field is left untouched (null) while deserializing. So setting a default value prevents the field from being left to null.
@XmlPersistable
public class SamplePersistableClass {
@XmlPersistedAttribute(defaultValue="default description")
private String description;
Sometimes it is not enough to have a set of annotations to be placed on fields of an object to persist it into an XML stream. The component itself is easy to be extended (see the javadoc for more information about that), however, there is also a simple hook interface available to be implemented by persistable classes to hook into the persistence mechanism.
@XmlPersistable()
public class SamplePersistableClass implements XmlPersistableHooks {
// ... implementation of the hooks goes in here ...
}
See the javadoc of that interface for a detailed description about the hook methods provided and how to extend the persistence of the object being serialized / deserialized to / from XML. Basically, there are hooks provided for pre and post loading, writing dynamic attribute values as well as writing object and object lists to the stream.
The org.edorasframework.test.common.executor.TestExecutor
is a can execute JUnit tests by returning the results as
org.edorasframework.test.common.executor.report.TestCaseReport
objects which can be used to present test results at runtime.
The org.edorasframework.test.common.executor.web.StartupTestServlet uses a test case executor to execute unit tests in an application server environment. This allows to run automated integration tests against productive databases using all layers of the application.
The classes the test servlet needs to execute need to be defined
colon separated within the init-param with name
test.classes.
<!-- The servlet configuration -->
<servlet>
<servlet-name>StartupTestServlet</servlet-name>
<servlet-class>org.edorasframework.test.common.executor.web.StartupTestServlet</servlet-class>
<init-param>
<param-name>test.classes</param-name>
<param-value>org.xyz.Test1;org.xyz.Test2;org.xyz.Test3</param-value>
</init-param>
<load-on-startup>10</load-on-startup>
</servlet>
<!-- The mapping for the test servlet -->
<servlet-mapping>
<servlet-name>StartupTestServlet</servlet-name>
<url-pattern>*.test</url-pattern>
</servlet-mapping>
By calling the servlet on the pammed URL, the test results are presented on a simple web page.
The edoras framework event module can be used to raise events from beans which will be observed by other beans. The events can be raised either by using the API of by adding an annotation on the method that should raise an event. To observe events one possibility is to add an ObserveEvent annotation on the method that should be called if the event is raised. Another way is to add an EventListener to the EventBus.
To use edoras framework the following maven artifact has to be used:
<groupId>org.edorasframework.event</groupId> <artifactId>org.edorasframework.event</artifactId>
This
artifact contains the application context file
org.edorasframework.event-applicationContext.xml which
needs to be loaded in addition to the custom context. This context file
contains the following sections:
Spring aop configuration; initializes Spring aop which will load the raise event pointcut advisor.
<!-- configure spring aop --> <aop:config />
EventBus implementation; Singleton scoped bean to handle the events. The event bus has to be defined with the name "edsEventBus".
<!-- The default event bus implementation -->
<bean id="edsEventBus"
class="org.edorasframework.event.impl.DefaultEventBus"
lazy-init="true" />
RaiseEventAdvisor implementation; defines the pointcut for the raise event annotated methods.
<!-- The raise event advice which will be automatically found and applied by Spring aop -->
<bean id="edsRaiseEventPointcutAdvisor" lazy-init="true"
class="org.edorasframework.event.
annotation.impl.RaiseEventPointcutAdvisor" />
EventBusBeanPostProcessor; bean factory post processor and bean post processor that handles observe event beans.
<!-- The event bus bean post processor -->
<bean id="edsEventBusBeanPostProcessor" lazy-init="true"
class="org.edorasframework.event.
impl.EventBusBeanPostProcessor" />
An event can be raised by two different ways.
The event bus interface defines several methods to raise an event from your java code.
void raiseEvent(Event event) throws EventException; void raiseEvent(String eventId, EventType type, Object bean, Object[] parameters) throws EventException; void raiseEvent(String eventId, EventType type, Object bean, Object[] parameters, Object returnValue) throws EventException;
To access the event bus bean the module provides a convenience method:
org.edorasframework.event.config.EventBeans.getEventBus();
An event can be raised from any public method of any configured
bean class by just adding the annotation
org.edorasframework.event.annotation.RaiseEvent to
it. This annotation defines two parameters where both are
optional:
value: Defines the string id of the event that will be risen
from this method. If no Id is defined, a default id is generated.
This default id consists of the simple class name and the name of
the method that will raise the event.
SimpleClassName.methodName
type: Defines when the event will be raised. The following
possibilities are defined within the enum
org.edorasframework.event.EventType.
SUCCESS: the event is raised only if the method is successful. Default value.
FAILURE: the event is only raised if an exception occurred.
ALWAYS: the event is raised if the method is successful or if an exception occurred.
@RaiseEvent(value = "demoEventId", type = EventType.ALWAYS)
public void demoMethod() {
// your implementation here ...
}
To enable event raising by anntotation the only thing that has to be done is that the bean with the annotated methods is configured within the application context.
To observe events this module provides two possibilities.
To listen for events you can always register an event listener within the event bus by using the following method. The listener is registered for one single event id on which it is listening to. If no longer needed the event listener can also be removed from the event bus component using one of its remove methods.
a void addEventlistener(String eventId, org.edorasframework.event.EventListener listener);
The
org.edorasframework.event.EventListener interface
defines one method, which will be called if an event for this listener
was raised.
void onEvent(org.edorasframework.event.Event event);
Observing events by annotation can be used on all public methods
of any singleton bean. The annotation
org.edorasframework.event.annotation.ObserveEvent
defines two parameters of which the value is mandatory.
value: Defines the string id of the events that will be provided to this observer method. The value is mandatory.
create: Defines whether the bean has to be created if an
event for the defined is raised or not. By default the create flag
is set to true.
The observe event annotation supports three different method signatures:
no arguments: The method is called with no additional information.
@ObserveEvent("anEventId")
public void observeMethodA() {
// observer code here ...
}
event as argument: The event object contains all information about the event and its source.
@ObserveEvent("anEventId")
public void observeMethodB(org.edorasframework.event.Event event) {
// observer code here ...
}
same signature as raise event method: The method gets the same parameters as the raise method got.
@ObserveEvent(EVENT_ID_D)
public void observeMethodD(String string, Integer integer, Date date) {
// observer code here ...
}
To enable event observe event by anntotation the bean with the annotated methods has to be configured as singleton within the application context.
The web part of the edoras framework is based on JSF and facelets as the main web technologies. It provides some useful functions that extend these base frameworks and other components that allow to access the edoras core functions from within the web part.
To configure an application to use the edoras framework, it is mainly necessary to add some parameters to the main configuration files of any JSF application. With the following configuration files, a web application with the edoras framework is set up and ready to develop.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.edorasframework.web.config.DependencyXmlWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:/META-INF/*-applicationContext.xml,
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>
javax.faces.webapp.FacesServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- bootstrap spring framework -->
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>
org.springframework.web.context.ContextLoaderServlet
</servlet-class>
<!-- load the context servlet after the faces servlet -->
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<!-- spring support for request and session scope -->
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
</web-app>
It basically defines the spring context, where to search the spring bean definitions and the servlets for JSF and spring.
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
version="1.2">
<factory>
<application-factory>
org.edorasframework.web.util.JBossELApplicationFactory
</application-factory>
</factory>
<application>
<view-handler>
com.icesoft.faces.facelets.D2DFaceletViewHandler
</view-handler>
<el-resolver>
org.springframework.web.jsf.el.SpringBeanFacesELResolver
</el-resolver>
</application>
</faces-config>
It defines a special application factory that causes the JBoss EL implementation to be used, the facelets view handler and the spring bean EL resolver so that all spring beans can be accessed through EL expressions.
Definitions of the spring beans in the web part of the edoras framework.
| Name | edsAppCtrl |
| Class | org.edorasframework.core.config.ApplicationContextController |
| Overrides definition from | edsCore |
| Implements/Extends | |
| Description | Set the application context control into web mode. |
| Name | edsWebMsg |
| Class | org.edorasframework.core.locale.impl.PrefixResourceBundle |
| Implements/Extends | java.util.ResourceBundle org.edorasframework.core.locale.MessageHandler |
| Description | The web message handler, delegates to the global message handler but prefixes all keys with 'org.edorasframework.web.' |
| Name | edsLocale |
| Class | org.edorasframework.web.locale.JsfLocalisationController |
| Overrides definition from | edsCore |
| Implements/Extends | org.edorasframework.core.locale.LocalisationController |
| Description | Set the global localisation controller to use JSF and to delegate to a bean named 'edsLocaleProvider'. |
| Name | edsPhaseListenerBeanPostProcessor |
| Class | org.edorasframework.web.util.PhaseListenerBeanPostProcessor |
| Implements/Extends | |
| Description | Register a phase listener post processor, this causes spring beans which implement PhaseListener to be automatically registered in JSF to be a phase listener. |
| Name | edsWebHtmlSec |
| Class | org.edorasframework.websecurity.impl.NullHtmlSecurisator |
| Implements/Extends | org.edorasframework.websecurity.HtmlSecurisator |
| Description | Set the HTML securisator which removes any potentially dangerous tags and attributes from user provided HTML code. |
| Name | edsWebLayoutManager |
| Class | org.edorasframework.web.entity.LayoutManager |
| Implements/Extends | org.edorasframework.web.entity.LayoutManager |
| Description | The web layout manager that saved the available layouts. |
| Name | edsWebCurrentLayout |
| Class | org.edorasframework.web.entity.CurrentLayout |
| Implements/Extends | org.edorasframework.web.entity.CurrentLayout |
| Description | The web layout controller that saves the current layout during page creation. |
| Name | edsDialogHandler |
| Class | org.edorasframework.web.locale.JsfMessageDialogHandler |
| Overrides definition from | edsCore |
| Implements/Extends | org.edorasframework.core.locale.DialogHandler |
| Description | A dialog handler that shows dialogs using the JSF message functionality. |
| Name | edsElEnvironment |
| Class | org.edorasframework.web.util.JsfFactories |
| Overrides definition from | edsCore |
| Implements/Extends | org.edorasframework.core.el.ElEnvironment |
| Description | An EL environment that bases on the EL implementation provided by JSF. |
| Name | edsWebSecurity |
| Class | org.edorasframework.web.security.impl.DefaultAccessSecurisator |
| Implements/Extends | |
| Description |
One main goal of the web part of edoras framework is to facilitate the handling of entities. Default use cases should be as easy as possible. It provides some functions to use the entity handling of the core (see Section 2.3.1, “Entity handling” ).
After we have created an entity with @Label and validation annotations we can go on and create a view for it:
<html xmlns:web="http://www.edorasframework.org/taglib/org.edorasframework.web"
...
<web:panel layout="simple">
<web:textProperty value="#{person.firstName}" />
<web:textProperty value="#{person.lastName}" />
<web:property value="#{person.gender}">
<h:selectOneRadio>
<f:selectItem itemValue="MALE" itemLabel="male" />
<f:selectItem itemValue="FEMALE" itemLabel="female" />
</h:selectOneRadio>
</web:property>
<web:property value="#{person.birthday}">
<h:inputText style="#{property.valid ? 'border: 1px solid green' : 'border: 1px solid red'}">
<f:convertDateTime type="date"/>
</h:inputText>
</web:property>
</web:panel>
...Several things are going on here:
The namespace 'web' is defined.
A panel with layout 'simple' is opened. This defines how the
three parts of a property (label, input field, message) are layed
out on the screen. There are two predefined layouts: 'table' and
'simple'. You can define your own layouts, see
org.edorasframework.web.entity.LayoutManager
and the sources of the predefined layouts in
META-INF/org/edorasframework/web/tag of the
org.edorasframework.web.jar
Two text properties for the first and last name are defined. These are rendered as input fields with the correct label (defined with @Label). User input is automatically validated (based on the validation annotations of the entity) and if validation fails, appropriate messages are shown.
A property tag for the gender is opened. This adds a
org.edorasframework.web.entity.PropertyInfo
with the name 'property' to the context inside the property tag.
This provides informations about the property like the label, the
HTML id, and the validation state of the property.
A selectOneRadio tag is added to display two checkboxes for the gender. Notice that there is no 'value' attribute on the tag. This is done automagically by the property tag. It adds 'value', 'id' and 'required' attributes to all its child tags that are EditableValueHolders and that do not provide these attributes by themselves.
As last thing, a property for the birthday is added. The input field is displayed with a green or red border depending whether the date is valid or not.
Displaying a list of entities in a combobox is a painful thing in JSF. You have to provide a list of SelectItems which have a value and a label. The value is used a key to find out which item the user has selected and must be a string. So if you set the value to be the entity itself, you get back the result of the toString method instead of your entity. If you set the value to be the ID of the entity, you get it back, but have to convert the ID back to the corresponding entity.
To make this task easier, you can use the
propertyConverter
and the
entityConverter
. With these, the SelectItem's values may be the entities itself.
The converter takes a given property or the ID from the entity as
key and automatically converts the key back to the corresponding
entity.
In the preceding sections, several tags provided by the edorasframework where shown. For a complete list of the available tags, see the taglibdoc section of the maven site of the web project.
The viewManager is used to manager the views of an application. It can handle the view hierarchy and create generic view menus.
The viewManager module the following two beans automatically to the application context. If a custom implementation is needed the beans can be overwritten by defining an own bean with the same name in the application's application context configuration.
<!-- The default view manager bean --> <bean id="edsViewManager" class="org.edorasframework.viewmanager.core.impl.DefaultViewManager" /> <!-- The default view manager controller bean --> <bean id="edsViewManagerController" class="org.edorasframework.viewmanager.core.impl.DefaultViewManagerController" />
Within the web module the view manager controller is by default session scoped.
<!-- Register the default view manager controller session scoped. --> <bean id="edsViewManagerController" scope="session" class="org.edorasframework.viewmanager.core.impl.DefaultViewManagerController"/> <!-- Register the view manager phase listener --> <bean id="edsViewManagerPhaseListener" class="org.edorasframework.viewmanager.web.impl.ViewManagerPhaseListener" />
This
module registers also the viewManager phase listener automatically. The
viewManager phase lsitener activates an deactivates the views as they
are loaded within the session. To work properly the phase listener needs
an the
org.edorasframework.web.faces.listener.PhaseListenerBeanPostProcessor
bean within the application context. This bean is by default initialized
by the org.edorasframework.web module and needs only to be
defined if the org.edorasframework.web is not used within
the application.
The views can be defined as property within the view manager
configuration. A view is defined throught its interface
org.edorasframework.viewmanager.core.View.
<bean id="edsViewManager" class="org.edorasframework.viewmanager.core.impl.DefaultViewManager">
<property name="views"
<list>
<bean class="org.edorasframework.viewmanager.core.impl.DefaultView">
<property name="viewId" value="viewA" />
<property name="label" value="View A" />
</bean>
<bean class="org.edorasframework.viewmanager.core.impl.DefaultView">
<property name="viewId" value="viewB" />
<property name="label" value="View B" />
<property name="parentViewId" value="viewA" />
</bean>
</list>
</property>
</bean>
The viewmanager allows to define actions that are invoked of a view
is initialized or disposed. These actions have to implement the interface
org.edorasframework.viewmanager.core.InitializingAction for
initializing actions or for
org.edorasframework.viewmanager.core.DisposingAction
disposing actions.
The actions are defined within the view manager controller bean.
<bean id="edsViewManagerController" class="org.edorasframework.viewmanager.core.impl.DefaultViewManagerController">
<property name="initializingActions">
<list>
<!-- initializing actions here -->
</list>
</property>
<property name="disposingActions">
<list>
<!-- disposing actions here -->
</list>
</property>
</bean>
Filestore is a simple component to store files on the database and read them again. The content is not stored in a byte array but directly streamed into / out of the database.
application context
<!-- Service configuration -->
<bean class="org.edorasframework.filestore.core.impl.DefaultFilestoreService">
<property name="mimeTypeResolver">
<bean class="org.edorasframework.filestore.core.mime.impl.MagicMimeTypeResolver" />
</property>
<property name="dao" ref="edsFileStoreDao" />
</bean>
<!-- The dao configuration -->
<bean id="edsFileStoreDao" class="org.edorasframework.filestore.core.impl.hibernate.HibernateFilestoreDao">
<!-- define the lob handler to be uesd -->
<property name="lobHandler" ref="${filestore.lob.handler}" />
</bean>
<!-- LobHandler for well-behaved JDBC drivers -->
<bean id="edsFileStoreDefaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" lazy-init="true" />
<bean id="edsFileStoreOracleLobHandler" class="org.springframework.jdbc.support.lob.OracleLobHandler" lazy-init="true">
<!--
define a nativeJdbcExtractor that matches to the connection pool you are using.
Below the dbcp connection pool implementation is used.
-->
<property name="nativeJdbcExtractor">
<bean class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor" />
</property>
</bean>
<!-- The filestore controller -->
<bean id="edsFileStoreController" scope="session"
class="org.edorasframework.filestore.icefaces.impl.IcefacesFilestoreController" >
<property name="filestoreService" value="edsFileStoreService" />
</bean>
<!-- The filestore download handler is used within the download servlet. See web configuration -->
<bean id="edsFileStoreDownloadHandler"
class="org.edorasframework.filestore.web.impl.FilestoreDownloadHandler" />
web.xml
<servlet> <servlet-name>downloadServlet</servlet-name> <servlet-class>org.edorasframework.web.download.DownloadServlet</servlet-class> <load-on-startup>5</load-on-startup> </servlet> !-- cleans the uploaded files when the session is closed --> <listener> <listener-class>org.edorasframework.filestore.icefaces.impl.InputFileSessionCleaner</listener-class> </listener>
The default implementation of the
org.edorasframework.filestore.core.FilestoreService is
org.edorasframework.filestore.core.impl.DefaultFilestoreService.
The
org.edorasframework.filestore.core.mime.MimeTypeResolver is
a component to resolve the mime type of a given file which is used by
the file store. The mime type resolver can be injected into the default
filestore service implementation, but is not needed. If no resolver is
set the, mime type used will always be
application/x-unknown.
Since the filestore dao is streaming the file content directly into
and out of the database a lob handler has to be defined within the dao.
The lob handler is responsible for the correct handling of the data
streams. By default the
org.springframework.jdbc.support.lob.DefaultLobHandler can be
used. If you are using an oracle database you have to use the
org.springframework.jdbc.support.lob.OracleLobHandler and
don't forget to set the nativeJdbcExtractor according to your
chosen connection pool implementation.
The default file store controller implementation is
org.edorasframework.filestore.web.impl.DefaultFilestoreController.
This controller defines methods to access the file store service.
The
org.edorasframework.filestore.icefaces.impl.IcefacesFilestoreController
uses the ICEfaces ICEfaces
ice:inputFile component to enable the file upload to the
server. Check the ICEfaces documentation to include and configure this
component.
Enable file download, the
org.edorasframework.web.download.DownloadServlet has to be
configured within the web.xml of your application. This servlet needs the
spring application context to be available during initialization. Be sure
to set the load-on-startup priority accordingly.
The org.edorasframework.filestore.web.impl.FilestoreDownloadHandler needs to be configured as spring bean. It is responsible that the requests of the downloadservlet are delegated to the filestoer service.
The filestore web module provides el functions to generate links
for stored files. The links can be generated by using the hash id of the
document. To use this functions, the namespace
http://www.edorasframework.org/taglib/org.edorasframework.filestore.web
has to be difined within your web template. The generated links include
alreay the context name of the current application.
The function filestoreHashDownloadLink takes the
String hash id of a document and retuns the the sownload link for the
given document.
Archetypes are maven project templates that can be used to create a new projects very fast. The archetypes of edorasframework create maven projects having the eclipse project configuration.
The archetype catalog is located within the edorasframework maven2 repository of http://repo.edorasframework.org/mvn/maven2/archetype-catalog.xml.
To create a new project by using an archetype, use the following command from your console. (Currently the m2 eclipse plugin does not support archetypes built with maven archetype plugin 2.0-alpha-4)
mvn archetype:generate -DarchetypeGroupId=archetype.groupId.here -DarchetypeArtifactId=archetype.artifactId.here -DarchetypeCatalog=http://repo.edorasframework.org/mvn/maven2/archetype-catalog.xml
Replace the placeholders archetype.groupId.here and archetype.artifactId.here with one of group and artifact ids of one of the following archetypes.
groupId: org.edorasframework.archetype
artifactId: org.edorasframework.archetype.edoras.module
This archetype creates a simple eclipse web project with three modules:
common: The common components of the project. Such as entities, utilities, ...
service: The service layer module. All service implementations for the project are here.
web: The presentation layer, where the gui templates, controller- /backing-beans, are located.
groupId: org.edorasframework.archetype
artifactId: org.edorasframework.archetype.edoras.module
This archetype creates a default edoras module containing the following three sub projects:
core: The common components of the project. Such as entities, utilities, ...
web: The service layer module. All service implementations for the project are here.
icefaces: The presentation layer, where the gui templates, controller- /backing-beans, are located.
edorasframework provides no database scripts by default. Nevertheless they can be created by using Hibernate Tools hbm2ddl which exists as ant and as maven2 plugin hibernate3-maven-plugin The properties to be used is documented within the Hibernate tools Reference Guide.
The edorasframework provides its own version of the icefaces libraries. They are based on the original icefaces sources and include some patches that fix known issues or add some (rather small) new features. The intension of these patch projects is to provide a fast solution to inconveniences using icefaces. All patches are also submitted to icesoft to include them in future versions of icefaces. So the number of patches should always be relatively small and not grow from release to release.
There are two icefaces patches atrifacts:
org.edorasframework.icefaces.<icefaces-version>.core.jar which patches the
icefaces.jar of the corresponding version.
org.edorasframework.icefaces.<icefaces-version>.comps.jar which patches the
icefaces-comps.jar of the corresponding version.
| Name | edsAppCtrl |
| Class | org.edorasframework.core.config.ApplicationContextController |
| Implements/Extends | |
| Description | bootstrap bean to access other beans by name or type |
| Name | edsMsg |
| Class | org.edorasframework.core.locale.impl.FallbackResourceBundle |
| Implements/Extends | java.util.ResourceBundle org.edorasframework.core.locale.MessageHandler |
| Description | The global message handler, this must implement MessageHandler and extend ResourceBundle. All strings that must be translated are processed by this bean. |
| Name | edsCoreMsg |
| Class | org.edorasframework.core.locale.impl.PrefixResourceBundle |
| Implements/Extends | java.util.ResourceBundle org.edorasframework.core.locale.MessageHandler |
| Description | The core message handler, delegates to the global message handler but prefixes all keys with 'org.edorasframework.core.' |
| Name | edsLocale |
| Class | org.edorasframework.core.locale.impl.ProviderLocalisationController |
| Implements/Extends | org.edorasframework.core.locale.LocalisationController |
| Description | The global localisation controller, delegates to a bean named 'edsLocaleProvider' which must implement LocalisatorProvider. |
| Name | edsLocaleProvider |
| Class | org.edorasframework.core.locale.impl.DirectLocalisatorProvider |
| Implements/Extends | org.edorasframework.core.locale.LocalisatorProvider |
| Description | The localisator provider used by the localisation controller. |
| Name | edsUser |
| Class | org.edorasframework.core.security.DummyUserInfo |
| Implements/Extends | org.edorasframework.core.security.UserInfo |
| Description | Informations about the current User. |
| Name | edsMetaModel |
| Class | org.edorasframework.core.metamodel.impl.DefaultMetaModel |
| Implements/Extends | org.edorasframework.core.metamodel.MetaModel |
| Description | The meta model implementation. |
| Name | edsMetaDataFinder |
| Class | org.edorasframework.core.metamodel.MetaDataFinder |
| Implements/Extends | |
| Description | This allows to provide additional meta informations about classes and their properties. |
| Name | edsDialogHandler |
| Class | org.edorasframework.core.locale.impl.LogDialogHandler |
| Implements/Extends | org.edorasframework.core.locale.DialogHandler |
| Description | The dialog handler implementation. |
| Name | edsElEnvironment |
| Class | org.edorasframework.core.el.impl.SimpleElEnvironment |
| Implements/Extends | org.edorasframework.core.el.ElEnvironment |
| Description | This provides that global access point for using EL expressions. |
| Name | |
| Class | org.edorasframework.core.description.impl.DescriptionScanner |
| Implements/Extends | |
| Description | Scans classes for Description typed fields and provides description resolvers to for them. |
| Name | edsAddBeanPostProcessor |
| Class | org.edorasframework.core.config.add.AddBeansPostProcessor |
| Implements/Extends | org.edorasframework.core.config.add.AddBeansPostProcessor |
| Description | Bean post processor for add beans that are dynamically added to their target beans. |
| Name | edsAddBeanDescriptionResolver |
| Class | org.edorasframework.core.description.impl.ListDescriptionResolver |
| Implements/Extends | org.edorasframework.core.description.impl.ListDescriptionResolver |
| Description | Default description resolver for descriptions added by the core namespace handler. |
| Name | edsDescriptionProvider |
| Class | org.edorasframework.core.description.impl.DefaultDescriptionProvider |
| Implements/Extends | org.edorasframework.core.description.DescriptionProvider |
| Description | The description provider bean. |
| Name | edsDescriptionProviderDao |
| Class | org.edorasframework.core.description.impl.NullDescriptionProviderDao |
| Implements/Extends | org.edorasframework.core.description.DescriptionProviderDao |
| Description | The description provider dao |
| Name | edsValidator |
| Class | org.edorasframework.core.metamodel.impl.NullValidator |
| Implements/Extends | org.edorasframework.core.metamodel.BeanValidator |
| Description | Validates the contents of beans. |
Definitions of the spring beans in the web part of the edoras framework.
| Name | edsAppCtrl |
| Class | org.edorasframework.core.config.ApplicationContextController |
| Overrides definition from | edsCore |
| Implements/Extends | |
| Description | Set the application context control into web mode. |
| Name | edsWebMsg |
| Class | org.edorasframework.core.locale.impl.PrefixResourceBundle |
| Implements/Extends | java.util.ResourceBundle org.edorasframework.core.locale.MessageHandler |
| Description | The web message handler, delegates to the global message handler but prefixes all keys with 'org.edorasframework.web.' |
| Name | edsLocale |
| Class | org.edorasframework.web.locale.JsfLocalisationController |
| Overrides definition from | edsCore |
| Implements/Extends | org.edorasframework.core.locale.LocalisationController |
| Description | Set the global localisation controller to use JSF and to delegate to a bean named 'edsLocaleProvider'. |
| Name | edsPhaseListenerBeanPostProcessor |
| Class | org.edorasframework.web.util.PhaseListenerBeanPostProcessor |
| Implements/Extends | |
| Description | Register a phase listener post processor, this causes spring beans which implement PhaseListener to be automatically registered in JSF to be a phase listener. |
| Name | edsWebHtmlSec |
| Class | org.edorasframework.websecurity.impl.NullHtmlSecurisator |
| Implements/Extends | org.edorasframework.websecurity.HtmlSecurisator |
| Description | Set the HTML securisator which removes any potentially dangerous tags and attributes from user provided HTML code. |
| Name | edsWebLayoutManager |
| Class | org.edorasframework.web.entity.LayoutManager |
| Implements/Extends | org.edorasframework.web.entity.LayoutManager |
| Description | The web layout manager that saved the available layouts. |
| Name | edsWebCurrentLayout |
| Class | org.edorasframework.web.entity.CurrentLayout |
| Implements/Extends | org.edorasframework.web.entity.CurrentLayout |
| Description | The web layout controller that saves the current layout during page creation. |
| Name | edsDialogHandler |
| Class | org.edorasframework.web.locale.JsfMessageDialogHandler |
| Overrides definition from | edsCore |
| Implements/Extends | org.edorasframework.core.locale.DialogHandler |
| Description | A dialog handler that shows dialogs using the JSF message functionality. |
| Name | edsElEnvironment |
| Class | org.edorasframework.web.util.JsfFactories |
| Overrides definition from | edsCore |
| Implements/Extends | org.edorasframework.core.el.ElEnvironment |
| Description | An EL environment that bases on the EL implementation provided by JSF. |
| Name | edsWebSecurity |
| Class | org.edorasframework.web.security.impl.DefaultAccessSecurisator |
| Implements/Extends | |
| Description |