JBoss.orgCommunity Documentation
WebUI is the name of GateIn's own webframework. GateIn Portal is built with it and also many applications available in the GateIn platform suites. In its concepts, the WebUI framework is similar to JSF as it is a component tree based framework. The key aspects of WebUI are :
It is very easy to create and manage Ajax calls in our framework. Just a few lines to write in your template file and your java class. For simple Ajax update of a component, you don't even have to write any line of JavaScript.
Our portlets can use specific <code>ActionListener</code>s to receive and process Ajax calls. To do that, you must create an inner static class named following this convention : action name followed by ActionListener
Example : <code>ParentClass</code> is the class in which you are writing.
static public class SaveActionListener extends EventListener<ParentClass>
Don't forget to declare this listener in the configuration of your portlet, with this :
listeners = ParentClass.SaveActionListener.class{code}
in the correct annotation <code>ComponentConfig</code>, <code>EventConfig</code>, etc.,
For example, the configuration of <code><strong>UIAccountForm</strong></code>:
... @ComponentConfig( lifecycle = UIFormLifecycle.class, template = "system:/groovy/webui/form/UIFormTabPane.gtmpl", initParams = { @ParamConfig( name = "AccountTemplateConfigOption", value = "app:/WEB-INF/conf/uiconf/account/webui/component/model/AccountTemplateConfigOption.groovy" ), @ParamConfig( name = "help.UIAccountFormQuickHelp", value = "app:/WEB-INF/conf/uiconf/account/webui/component/model/UIAccountFormQuickHelp.xhtml" ) }, events = { @EventConfig(listeners = UIAccountForm.SaveActionListener.class ), @EventConfig(listeners = UIAccountForm.ResetActionListener.class, phase = Phase.DECODE), @EventConfig(listeners = UIAccountForm.SearchUserActionListener.class, phase = Phase.DECODE) } ) ...
Inside this class, you will have to create an <code>execute</code> method like this :
public void execute(Event<ParentClass> event) throws Exception
This method is called every time the listener gets an event from the client, hence you can process this event in it, using the {style:type=span|font-family=courier new,courier}event {style}attibute. Use it to get parameters from a form in your client, to modify the status of your portlet, etc.,
Possible ways to use the event attribute : {code}
If your action has to update an element on your client's interface, you must call addUIComponentToUpdateByAjax() at the end of the <code>execute</code> method:
event.getRequestContext().addUIComponentToUpdateByAjax(uicomponent) ;
The target component must be provided as parameter (the component that will be updated). We will come back on this later.
You must create one inner action listener class for each Ajax call you want to handle on this portlet. All these classes must be declared in the configuration annotations of the main class, otherwise you will get an error.
It's done, your portlet is ready to accept Ajax calls from your client.
Your server being configured to receive Ajax calls, you must configure the client interface to make these calls.
In the groovy template file associated with your portlet class (<code>ParentClass</code> here), you just have to add :
uicomponent.event("YourOperation"); // YourOperation is the same as in the ActionListener class (Save in our example above)
in a groovy code block. The event function will create an url starting with {style:type=span|font-family=courier new,courier}javascript:{style} so you have to make sure this code can be executed in your environment.
If your operation must update the content of a component, you have to make sure that the target component is well rendered. Basically, just type this :
uicomponent.renderChild(UITargetComponent.class) ;
in a groovy code block. <code>UITargetComponent</code> is the class of the component that will be updated when
event.getRequestContext().addUIComponentToUpdateByAjax(uicomponent) ;
is called. Hence, <code>uicomponent</code> must be of type <code>UITargetComponent</code>. If this component is not rendered by default, when the portlet loads, don't forget to set its <code>rendered</code> attribute to false :
mycomponent.setRendered(false);
in the constructor of your portlet.
All the javascript is managed by the file GateIn.portal.PortalHttpRequest.js in the portal project.
In this class, you will find 4 functions/classes (detailed below):
* PortletResponse * PortalResponse * AjaxRequest * HttpResponseHandler
and 6 functions : {code}
This class doesn't contain any method. On creation, it just gets the response elements from the xml returned by Ajax, and store them in the corresponding attributes : {code}
You can access these attributes just by calling them from your <code>PortletResponse</code> instance.
Contains an array of <code>PortletResponse</code>s (<code>portletResponses</code>) and two other attributes : {code}
By far the most important class of this file. Wraps the XMLHttpRequest object with some functions and attributes, to make it easier to use. You can find the complete documentation here : http://www.ajaxtoolbox.com/request/documentation.php
This class provides methods to handle the Ajax response. {code}
{PortalResponse} | |--->{PortletResponse} | |--->{PortletResponse} | |-->{portletId} | |-->{portletTitle} | |-->{portletMode} | |-->{portletState} | | | |-->{Data} | | | | | |--->{BlockToUpdate} | | | |-->{blockId} | | | |-->{data} | | | | | |--->{BlockToUpdate} | |--->{Script} | |--->{Data} | | | |--->{BlockToUpdate} | | |-->{blockId} | | |-->{data} | | | |--->{BlockToUpdate} |--->{Script}
If you have several actions that need to appear in a popup, you can use this technique to manage the different popup windows easily:
addChild(UIPopupAction.class, null, null);
and render it in your template file:
uicomponent.renderChild(UIPopupAction.class) ;
By default, this just create an empty container (popup) that will receive the new content by Ajax.
UIPopupAction uiPopupAction = uiMainPortlet.getChild(UIPopupAction.class) ; uiPopupAction.activate(UIReferencesList.class, 600) ;
UIReferenceList is the component that will appear in the popup. You don't have to declare it in the main portlet class. The activate method takes care of the creation of the component, and its rendering in the popup window. See the javadoc for more information on this class.
event.getRequestContext().addUIComponentToUpdateByAjax(uiPopupAction) ;
For each component that you want that component to appear in a popup window, add a action listener class and repeat the steps above with the appropriate component type.
This article gives a glance at the Groovy language, and explains how to configure the portlet and and the groovy template.
It's recommended to read also AJAX in GateIn Framework in order to understand better the communication between the Groovy Template and the portlet.
The structure of a template is very easy :
The HTML code in the template doesn't have to contain the <code>html</code>, or <code>body</code> tags. Hence, you can use a groovy template for a component that will be rendered in another component.
Example : UIPortalApplication.gtmpl template (/GateInProjects/portal/trunk/web/portal/src/main/webapp/groovy/portal/webui/workspace/)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <% import org.exoplatform.webui.core.UIComponent; def currentPage = uicomponent.getCurrentPage(); ... %> ... <div class="$uicomponent.skin" id="UIPortalApplication"> <%uicomponent.renderChildren();%>
Groovy is a scripting language for Java. Here are a few examples on how to use it, but you can find more information in the full documentation.
This language looks like Java a lot, so it's very easy to use. Examples :
Variables definition : {code} int min = 1; def totalPage = uicomponent.getAvailablePage(); String name = "uiPortlet"; categories = uicomponent.getItemCategories(); String ??? columns = uicomponent.getColumns(); {code} Other expressions : {code} for(category in categories) { ... } // easy to use for loop for(i in min..max) { ... } // min and max are int variables println "</div>" ; println """ <div class="Item"> <div class="OverflowContainer"> """; <%=uicomponent.getToolbarStyle();%> // <%= to avoid a call of println method import org.exoplatform.portal.config.model.PageNode; {code}
The configuration of a portlet is partly made with {style:type=span|font-family=courier new,courier}ComponentConfig {style}annotations (others are ComponentConfigs, EventConfig, etc). One of the parameters of this annotation is called {style:type=span|font-family=courier new,courier}template{style}, where you can define the path to the template file associated with this portlet.
To specify this parameter to your portlet, just add this statement to your configuration annotation, for example in /GateInProjects/portal/trunk/portlet/exoadmin/src/main/java/org/exoplatform/applicationregistry/webui/component/ you find UIApplicationForm.java:
@ComponentConfig( lifecycle = UIFormLifecycle.class, template = "system:/groovy/webui/form/UIFormWithTitle.gtmpl", events = { @EventConfig(listeners = UIApplicationForm.SaveActionListener.class), @EventConfig(phase = Phase.DECODE, listeners = UIApplicationForm.CancelActionListener.class) } )
You see that the path is in the namespace called "system", "system" is a reference to the portal webapp. In this webapp you find some reusable groovy templates, just open the folder /GateInProjects/portal/trunk/web/portal/src/main/webapp/groovy/webui/form/ to see them.
As you want to create your own template, create a groovy file in your webbapp and refer to it. Please use the namespace "app" for refering to the same webapp as your component. GateIn always puts the component templates in a folder like "/webapp/groovy/yourportletname/webui/component".
template = "app:/groovy/your_portlet_name/webui/component/your_component.gtmpl"
You can now edit your template file.
As we said before, the template file is composed of HTML code and groovy code blocks. There are a few things more that you need to know to fully link your portlet with your template.
If your template defines the UI of a component, you have an access to this component instance (the java object) using the variable {style:type=span|font-family=courier new,courier}uicomponent{style}. This should be the case almost all the time, but we recommend that you check that your java class inherits from UIComponent before you use this variable. With this {style:type=span|font-family=courier new,courier}uicomponent {style}variable, you can access all the attributes and functions of your component, to use them in your template. Example : UIPageIterator.gtmpl {code} <% def currentPage = uicomponent.getCurrentPage(); %> ... <a href="<%=uicomponent.event("ShowPage","$currentPage")%>" class="Icon LastTopPageIcon"><span></span></a> {code}
This example shows that {style:type=span|font-family=courier new,courier}uicomponent {style}can be used to make Ajax calls, thanks to the {style:type=span|font-family=courier new,courier}event {style}method. See AJAX in GateIn Framework for more details.
Another variable that you can use is {style:type=span|font-family=courier new,courier}{style}ctx. It gives access to the context in which the template is processed. Hence, you can get some elements like the request, the Javscript manager, or the resource resolver (ctx.appRes). Examples : {code} <% def rcontext = ctx.getRequestContext() ; rcontext.getJavascriptManager().importJavascript('GateIn.webui.UIPopupWindow');ctx.appRes(popupId + ".title."+ title); %> {code}
If you use your template to define the user interface of a component that includes a form, you can access the instance of UIForm in a variable named {style:type=span|font-family=courier new,courier}uiform{style}. The UIForm class provides the methods, {style:type=span|font-family=courier new,courier}begin(){style} and {style:type=span|font-family=courier new,courier}end(){style}, that write the HTML tags of the form. Your form class must inherit from UIForm
, in this class you add the input elements (fields, checkboxes, lists) which you wish to use in your form. In your groovy template you can render your input elements using {style:type=span|font-family=courier new,courier}{style}{code}{ uiform.renderField(field) }{code}
This example is based on the testPortlet in portal/trunk/portlet/test.
On Eclipse, create a new Java Project, and create this folder tree :
<pre> src | main | |- java | |- resources | |- webapp </pre>
Create the pom.xml, at root level of the project, like this :
<project> <parent> <groupId>org.exoplatform.portal</groupId> <artifactId>config</artifactId> <version>trunk</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>exo.portal.portlet.testRomain</artifactId> <packaging>war</packaging> <version>${org.exoplatform.portal.version}</version> <name>exo-portal.portlets.test Romain</name> <url>http://www.exoplatform.org</url> <description>Romain Test Portlet</description> <dependencies> <dependency> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.webui.portal</artifactId> <version>${org.exoplatform.portal.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.exoplatform.portal</groupId> <artifactId>exo.portal.webui.GateIn</artifactId> <version>${org.exoplatform.portal.version}</version> <scope>provided</scope> </dependency> </dependencies> <build> <finalName>testRomain</finalName> </build> </project>
1.1 UITestRomainPortlet.java
In java/testRomain/portlet/component/, we will create the UITestRomainPortlet.java file of the portlet :
package testRomain.portlet.component; import org.exoplatform.webui.config.annotation.ComponentConfig; import org.exoplatform.webui.core.lifecycle.UIApplicationLifecycle; import org.exoplatform.webui.core.UIPortletApplication; //this part is configuration of the portlet, we set the path to the template groovy. @ComponentConfig( lifecycle = UIApplicationLifecycle.class, template = "app:/groovy/testRomain/portlet/UITestRomainPortlet.gtmpl" ) public class UITestRomainPortlet extends UIPortletApplication { public UITestRomainPortlet() throws Exception { } }
In src/main/resources/tomcat/, create a testRomain.xml file : {code} <Context path="/test" docBase="../../../GateInProjects/portal/trunk/portlet/testPortletRomain/src/main/webapp" debug="0" reloadable="true" /> {code}
docBase must be set to webapp path of the portlet when you are in the tomcat bin directory.
In src/main/webapp, create the groovy template for the portlet. The path to this file must match the path you set in the java file, in our case : groovy/testRomain/portlet/UITestRomainPortlet.gtmpl
<div id="<%=uicomponent.getId();%>"> HelloWorld!images/!! </div>
Create the folder skin in src/main/webapp. We don't fill it now, but in this folder, you can put css stylesheet and images.
Create the folder WEB-INF/classes/locale in src/main/webapp. We don't fill it now, but in this folder, you can put language properties files. See Internationalization Configuration.
Create the file configuration.xml in WEB-INF/conf/portlet/testPortletRomain/. Content of tag <ui-component-root> must match your package organization.
<webui-configuration> <application> <ui-component-root>testRomain.portlet.component.UITestRomainPortlet</ui-component-root> <state-manager>org.exoplatform.webui.application.portlet.ParentAppStateManager</state-manager> </application> </webui-configuration>
1.1 portlet.xml
In WEB-INF, create file portlet.xml :
<?xml version="1.0" encoding="UTF-8"?> <portlet-app version="1.0" xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"> <portlet> <description xml:lang="EN">Test Portlet Romain</description> <portlet-name>TestRomain</portlet-name> <display-name xml:lang="EN">Test Portlet Romain</display-name> <portlet-class>org.exoplatform.webui.application.portlet.PortletApplicationController</portlet-class> <init-param> <name>webui.configuration</name> <!-- must match the path to configuration file --> <value>/WEB-INF/conf/portlet/testPortletRomain/configuration.xml</value> </init-param> <expiration-cache>0</expiration-cache> <supports> <mime-type>text/html</mime-type> <portlet-mode>help</portlet-mode> </supports> <supported-locale>en</supported-locale> <resource-bundle>locale.testRomainPortlet</resource-bundle> <portlet-info> <title>TestPortletRomain</title> <short-title>TestPortlet</short-title> <keywords>test</keywords> </portlet-info> </portlet> </portlet-app>
In WEB-INF, create file web.xml :
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <!-If define the Portlet Application name MUST end with .par-> <display-name>test</display-name> <description> This application is a portlet. It can not be used outside a portal. This web.xml file is mandatory in each .par archive file. </description> <listener> <listener-class>org.exoplatform.services.portletcontainer.impl.servlet.PortletApplicationListener</listener-class> </listener> <servlet> <servlet-name>PortletWrapper</servlet-name> <servlet-class>org.exoplatform.services.portletcontainer.impl.servlet.ServletWrapper</servlet-class> </servlet> <servlet-mapping> <servlet-name>PortletWrapper</servlet-name> <url-pattern>/PortletWrapper</url-pattern> </servlet-mapping> </web-app>
1 Use the Portlet
Compile your portlet, deploy it, and add it to the portal.
Now, we will add a button in the portlet. This button will open a popup with a message inside.
1.1 Add a button In the groovy template, add this code :
<div class="UIAction"> <div class="ActionContainer"> <div class="ActionButton"> <div class="LightBlueStyle"> <div class="ButtonLeft"> <div class="ButtonRight"> <div class="ButtonMiddle"> <a href="<%=uicomponent.event("OpenPopup", "")%>">Open Popup</a> </div> </div> </div> </div> </div> </div> </div>
1.1 Add a listener In the java file, in @ComponentConfig, add this code :
events = { @EventConfig(listeners = UITestRomainPortlet.OpenPopupActionListener.class) }
Remark : XXXActionLister.class XXX must match the name you set for the event in the groovy.
static public class OpenPopupActionListener extends EventListener<UITestRomainPortlet> { public void execute(Event<UITestRomainPortlet> event) throws Exception { System.out.println("HelloWorld"); } }
1.1 Redeploy
Redeploy the portlet and click on the button. You will see "HelloWorld" in your console. If you don't change in the portlet, try to redeploy and reboot the tomcat server.
1 Add a "HelloWorld" popup
Now, we will add a popup which say "HelloWorld" when you click on the button.
<div id="<%=uicomponent.getId();%>"> HelloWorld in a popup!images/!! </div>
import org.exoplatform.webui.config.annotation.ComponentConfig; import org.exoplatform.webui.core.UIComponent; import org.exoplatform.webui.core.lifecycle.UIApplicationLifecycle;
@ComponentConfig( lifecycle = UIApplicationLifecycle.class, template = "app:/groovy/testRomain/portlet/UIHelloWorldPopupContent.gtmpl" )
public class UIHelloWorldPopupContent extends UIComponent
public UIHelloWorldPopupContent() throws Exception { }{ } {code}
{UIHelloWorldPopupContent popupContent = createUIComponent(UIHelloWorldPopupContent.class, null, null); popup.setUIComponent(popupContent); popup.setRendered(false); } {code}
At the beginning, we set the popup not visible. As you see, we add a children to the Portlet. So, if we want to see the content of it, we must add this in UITestPortletRomain.gtmpl :
<% uicomponent.renderChildren(); %>
This makes the portlet generate the content of all child components.
When user clicks on the button, the popup is shown.
The goal of this chapter is not to talk about the Portlet API specification lifecycle but more about GateIn UI framework to easily develop portlets.
The web framework used here has been completely developed by GateIn and perfectly suits the portal environment, it even allows to send events from the portlet UIComponents to the Portal ones.
Of course using the GateIn web framework to build portlets is not necessary and any other web framework that supports portlet environment can be used. But all GateIn portlets that are part of GateIn products are developed using that framework and we provide several UI components that can be used in different abstracted contexts such as the portal itself or some portlets.
This chapter is not a tutorial on how to write portlets, it will go in the details of the code implementation and logic; hence it is intended for advanced developers. It is also advised to read the Portal Lifecycle article before as the that article explains concepts that are similar and top hierarchy classes that are shared.
The main entry point for configuring a portlet is in the portlet.xml file located in the portlet application WAR. Every portlet that shall be built using the GateIn web framework must reference the PortletApplicationController . The portlet configuration such as the root component is defined in a configuration.xml file. The path to this configuration.xml file is defined in the init-param "webui.configuration" of porlet.xml.
<portlet> <description xml:lang="EN">Content Portlet</description> <portlet-name>ContentPortlet</portlet-name> <display-name xml:lang="EN">Content Portlet</display-name> <portlet-class>org.exoplatform.webui.application.portlet.PortletApplicationController</portlet-class> <init-param> <name>webui.configuration</name> <value>/WEB-INF/conf/portlet/content/ContentPortlet/webui/configuration.xml</value> </init-param>
The structure of the configuration.xml file is exactly the same as the webui-configuration.xmlwhich we have already introduced in the Portal Lifecycle article. In the case of the content portlet it looks like:
<webui-configuration> <application> <ui-component-root>org.exoplatform.content.webui.component.UIContentPortlet</ui-component-root> <state-manager>org.exoplatform.webui.application.portlet.ParentAppStateManager</state-manager> </application> </webui-configuration>
The PortletApplicationController class extends the GenericPortlet class defined in the Portlet API specification.
All methods like processAction() or render() are delegated to the PortletApplication. The creation and caching inside the WebController object is described in the following method:
/** * try to obtain the PortletApplication from the WebAppController. * * If it does not exist a new PortletApplication object is created, init and cached in the * controller */ private PortletApplication getPortletApplication() throws Exception { PortalContainer container = PortalContainer.getInstance() ; WebAppController controller = (WebAppController)container.getComponentInstanceOfType(WebAppController.class) ; PortletApplication application = controller.getApplication(applicationId_) ; if(application == null) { application = new PortletApplication(getPortletConfig()) ; application.onInit() ; controller.addApplication(application) ; } return application ; }
When a portlet, that is deployed in GateIn Portal, is using the GateIn web framework then all methods calls go through the PortletApplication object which extends the WebuiApplication.
The code of the method in PortletApplication is described here. The business logic is shown in the javadoc:
/** * The processAction() method is the one modelled according to the Portlet API specification * * The process is quite simple and here are te different steps done in the method: * * 1) The current instance of the WebuiRequestContext (stored in a ThreadLocal in the class) is referenced * 2) A new request context of type PortletRequestContext (which extends the class WebuiRequestContext) is * created as a child of the current context instance * 3) The new context is place inside the ThreadLocal and hence overides its parent one there, * only for the portlet request lifeciclye * 4) The method onStartRequest() is called in all the ApplicationLifecycle objects referenced in the webui * configuration XML file * 5) The StateManager object (in case of portlet it is an object of type ParentAppStateManager) is used to get the RootComponent * also referenced in the XML configuration file * 6) The methods processDecode(UIApplication, WebuiRequestContext) and processAction(UIApplication, WebuiRequestContext) * are then called * 7) Finally, a flag, to tell that the processAction phase was done, in the context is set to true and the parent * context is restored in the Threadlocal */ public void processAction(ActionRequest req, ActionResponse res) throws Exception { WebuiRequestContext parentAppRequestContext = WebuiRequestContext.getCurrentInstance() ; PortletRequestContext context = createRequestContext(req, res, parentAppRequestContext) ; WebuiRequestContext.setCurrentInstance(context) ; try { for(ApplicationLifecycle lifecycle : getApplicationLifecycle()) { lifecycle.onStartRequest(this, context) ; } UIApplication uiApp = getStateManager().restoreUIRootComponent(context) ; context.setUIApplication(uiApp) ; processDecode(uiApp, context) ; if(!images/context.isResponseComplete() &&!images/ context.getProcessRender()) { processAction(uiApp, context) ; } } finally { context.setProcessAction(true) ; WebuiRequestContext.setCurrentInstance(parentAppRequestContext) ; } }
The PortletRequestContext extends WebuiRequestContext class and acts as a wrapper on top of all the portlet request information:
/** * In this method we try to get the PortletRequestContext object from the attribute map of the parent * WebuiRequestContext. * * If it is not cached then we create a new instance, if it is cached then we init it with the correct * writer, request and response objects * * We finally cache it in the parent attribute map * */ private PortletRequestContext createRequestContext(PortletRequest req, PortletResponse res, WebuiRequestContext parentAppRequestContext) throws IOException { String attributeName = getApplicationId() + "$PortletRequest" ; PortletRequestContext context = (PortletRequestContext) parentAppRequestContext.getAttribute(attributeName) ; Writer w = null ; if(res instanceof RenderResponse){ RenderResponse renderRes = (RenderResponse)res; renderRes.setContentType("text/html; charset=UTF-8"); w = renderRes.getWriter() ; } if(context!images/= null) { context.init(w, req, res) ; } else { context = new PortletRequestContext(this, w, req, res) ; parentAppRequestContext.setAttribute(attributeName, context) ; } context.setParentAppRequestContext(parentAppRequestContext) ; return context; }
In the PortletApplication, the line
public class ParentAppStateManager extends StateManager { /** * This method simply delegate the call to the same method of the parent WebuiRequestContext */ @SuppressWarnings("unchecked") public UIApplication restoreUIRootComponent(WebuiRequestContext context) throws Exception { WebuiRequestContext pcontext = (WebuiRequestContext) context.getParentAppRequestContext() ; return pcontext.getStateManager().restoreUIRootComponent(context) ; }
Hence this is the PortalStateManager that will also handle the extraction of the root component.
public UIApplication restoreUIRootComponent(WebuiRequestContext context) throws Exception { context.setStateManager(this) ; WebuiApplication app = (WebuiApplication)context.getApplication() ; /* * If the request context is of type PortletRequestContext, we extract the parent context which will * allow to get access to the PortalApplicationState object thanks to the session id used as the key for the * syncronised Map uiApplications */ if(context instanceof PortletRequestContext) { WebuiRequestContext preqContext = (WebuiRequestContext) context.getParentAppRequestContext() ; PortalApplicationState state = uiApplications.get(preqContext.getSessionId()) ; PortletRequestContext pcontext = (PortletRequestContext) context ; String key = pcontext.getApplication().getApplicationId() ; UIApplication uiApplication = state.get(key) ; if(uiApplication!images/= null) return uiApplication; synchronized(uiApplications) { ConfigurationManager cmanager = app.getConfigurationManager() ; String uirootClass = cmanager.getApplication().getUIRootComponent() ; Class type = Thread.currentThread().getContextClassLoader().loadClass(uirootClass) ; uiApplication = (UIApplication)app.createUIComponent(type, null, null, context) ; state.put(key, uiApplication) ; } return uiApplication ; }
The render method business logic is quite similar to processAction().
/** * The render method business logic is quite similar to the processAction() one. * * 1) A PortletRequestContext object is created (or extracted from the cache if it already exists) * and initialized * 2) The PortletRequestContext replaces the parent one in the WebuiRequestContext ThreadLocal object * 3) If the portal has already called the portlet processAction() then the call to all onStartRequest of * the ApplicationLifecycle has already been made, otherwise we call them * 4) The ParentStateManager is also used to get the UIApplication, as we have seen it delegates the call * to the PortalStateManager which caches the UI component root associated with the current application * 5) the processRender() method of the UIPortletApplucaton is called * 6) Finally, the method onEndRequest() is called on every ApplicationLifecycle referenced in the portlet * configuration XML file and the parent WebuiRequestContext is restored * */ public void render(RenderRequest req, RenderResponse res) throws Exception { WebuiRequestContext parentAppRequestContext = WebuiRequestContext.getCurrentInstance() ; PortletRequestContext context = createRequestContext(req, res, parentAppRequestContext) ; WebuiRequestContext.setCurrentInstance(context) ; try { if(!context.hasProcessAction()) { for(ApplicationLifecycle lifecycle : getApplicationLifecycle()) { lifecycle.onStartRequest(this, context) ; } } UIApplication uiApp = getStateManager().restoreUIRootComponent(context) ; context.setUIApplication(uiApp) ; if(!context.isResponseComplete()) { UIPortletApplication uiPortletApp = (UIPortletApplication)uiApp; uiPortletApp.processRender(this, context) ; } uiApp.setLastAccessApplication(System.currentTimeMillis()) ; } finally { try { for(ApplicationLifecycle lifecycle : getApplicationLifecycle()) { lifecycle.onEndRequest(this, context) ; } } catch (Exception exception){ log.error("Error while trying to call onEndRequest of the portlet ApplicationLifecycle", exception); } WebuiRequestContext.setCurrentInstance(parentAppRequestContext) ; } }
The processRender() call made on the UIPortletApplication is shown now:
/** * The default processRender for an UIPortletApplication handles two cases: * * A. Ajax is used * ~UWC_TOKEN_START~1255420331108~UWC_TOKEN_END~ * If Ajax is used and that the entire portal should not be re rendered, then an AJAX fragment is * generated with information such as the portlet id, the portlet title, the portlet modes, the window * states as well as the HTML for the block to render * * B. A full render is made * ---- * a simple call to the method super.processRender(context) which will delegate the call to all the * Lifecycle components * */ public void processRender(WebuiApplication app, WebuiRequestContext context) throws Exception { WebuiRequestContext pContext = (WebuiRequestContext)context.getParentAppRequestContext(); if(context.useAjax() &&!images/pContext.getFullRender()) { Writer w = context.getWriter() ; Set<UIComponent> list = context.getUIComponentToUpdateByAjax() ; // if(list == null) list = app.getDefaultUIComponentToUpdateByAjax(context) ; if(list!images/= null) { if(getUIPopupMessages().hasMessage()) context.addUIComponentToUpdateByAjax(getUIPopupMessages()) ; for(UIComponent uicomponent : list) { renderBlockToUpdate(uicomponent, context, w) ; } return ; } } super.processRender(context) ; }