JBoss.orgCommunity Documentation

Chapter 6. Portlet development

6.1. Web User Interface - WebUI
6.1.1. Resources
6.2. AJAX in GateIn Framework
6.2.1. Overview
6.2.2. Portlet Preparation
6.2.3. AJAX in the Groovy template
6.2.4. How JavaScript works
6.2.5. PortletResponse
6.2.6. PortalResponse
6.2.7. AjaxRequest
6.2.8. HttpResponseHandler
6.2.9. Portal Ajax Response Data Structure
6.2.10. Manage Several Popups
6.3. Groovy Templates
6.3.1. Overview
6.3.2. Basic structure
6.3.3. Groovy language
6.3.4. Linking a portlet with a template
6.4. Portlet Lifecycle
6.4.1. Overview
6.4.2. Portlet init
6.4.3. Portlet request handler
6.4.4. ProcessAction phase
6.4.5. Render phase
6.5. Portlet Primer
6.5.1. JSR-168 and JSR-286 overview
6.5.2. Tutorials
6.6. Create a WebUI Portlet
6.6.1. Overview
6.6.2. Configure the portlet
6.6.3. Use the Portlet
6.6.4. Add a "HelloWorld" popup

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. Using its own framework means that the way the portal is build will not interfere with any web technology used by the portlet deployed. Using a particular technology would have make it more difficult for a user to use a different version (newer or older) of the same technology. If one choose to create WebUI portlets, here is some documentation. In general we recommend to use JSF as the framework of choice to develop new portlets.

The WebUI framework is a component tree based framework. The key aspects of WebUI are :

Our portlets can use specific ActionListener 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 : ParentClass 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, like this :

listeners = ParentClass.SaveActionListener.class

along with the correct annotation ComponentConfig , EventConfig , etc.,

For example, the configuration for UIAccountForm :

...

@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 execute 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 even attribute. 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 :

If your action has to update an element on your client's interface, you must call addUIComponentToUpdateByAjax() at the end of the execute 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.

Done. Your portlet is ready to accept Ajax calls from your client.

All the javascript is managed by the file 02eXoresources:javascript/eXo/portal/PortalHttpRequest.js in the portal project.

In this class, you will find 4 functions/classes (detailed below):

and 6 functions:

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 :

int min = 1;
def totalPage = uicomponent.getAvailablePage();
String name = "uiPortlet";
categories = uicomponent.getItemCategories();
String columns = uicomponent.getColumns();

Other expressions :

for(category in categories) { ... }
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;

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 uicomponent. 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 uicomponent variable, you can access all the attributes and functions of your component, to use them in your template. Example : UIPageIterator.gtmpl:

<%
  def currentPage = uicomponent.getCurrentPage();
%>
...
<a href="<%=uicomponent.event("ShowPage","$currentPage")%>" class="Icon LastTopPageIcon">
  <span></span>
</a>

This example shows that uicomponent can be used to make Ajax calls, thanks to the event method. See Section 6.2, “AJAX in GateIn Framework” for more details.

Another variable that you can use is 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 :

<%
  def rcontext = ctx.getRequestContext() ;
  context.getJavascriptManager().importJavascript('GateIn.webui.UIPopupWindow');
  ctx.appRes(popupId + ".title."+ title);
%>

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 uiform. The UIForm class provides the methods, begin() and end(), 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 uiform.renderField(field)

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>
</portlet>

The structure of the configuration.xml file is exactly the same as the webui-configuration.xmlwhich we have already introduced in the Section 5.1, “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 ;
}

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() &amp;&amp;!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

UIApplication uiApp = getStateManager().restoreUIRootComponent(context); asks the StateManager defined for the portlet to get the UI root component. In the case of a portlet the root component must extend UIPortletApplication.

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 
 *   ----
 *     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() &amp;&amp;!images/pContext.getFullRender()) {
    Writer w =  context.getWriter() ;
    
    Set<UIComponent> list = context.getUIComponentToUpdateByAjax() ;
    if(list!images/= null) {
      if(getUIPopupMessages().hasMessage()) context.addUIComponentToUpdateByAjax(getUIPopupMessages()) ;
      for(UIComponent uicomponent : list) {
        renderBlockToUpdate(uicomponent, context, w) ;
      }
      return ;
    }
  }
  super.processRender(context) ;    
}

The tutorials contained in this chapter are targeted toward portlet developers. Although they are a good starting and reference point, it is highly recommend that portlet developers read and understand the JSR-286 Portlet Specification .

This example is using Maven to compile and build the web archive. If you don't have Maven already installed, you will find a version for your operating system here

To compile and package the application, go to the SimplestHelloWorld directory and type mvn package .

Once successfully packaged, the result should be available in: SimplestHelloWorld/target/SimplestHelloWorld-0.0.1.war . Simply copy that file into JBOSS_HOME/server/default/deploy , then start JBoss Application Server if it was not already started.

Now you will need to create a new page and add that portlet on it.

Let's study the Java class in detail.

The following file is the SimplestHelloWorldPortlet/src/main/java/org/gatein/portal/examples/portlets/SimplestHelloWorldPortlet.java Java source.

package org.gatein.portal.examples.portlets;


import java.io.IOException;
import java.io.PrintWriter;
import javax.portlet.GenericPortlet;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
(1)public class SimplestHelloWorldPortlet extends GenericPortlet
{
   public void doView(RenderRequest request, 
(2)                       RenderResponse response) throws IOException
   {
(3)      PrintWriter writer = response.getWriter();
(4)      writer.write("Hello World !");
(5)      writer.close();
   }
}
               
1

All portlets must implement the javax.portlet.Portlet interface. The portlet API provides a convenient implementation of this interface, in the form of the javax.portlet.GenericPortlet class, which among other things, implements the Portlet render method to dispatch to abstract mode-specific methods to make it easier to support the standard portlet modes. As well, it provides a default implementation for the processAction , init and destroy methods. It is recommended to extend GenericPortlet for most cases.

2

As we extend from GenericPortlet , and are only interested in supporting the view mode, only the doView method needs to be implemented, and the GenericPortlet render implemention calls our implementation when the view mode is requested.

3

Use the RenderResponse to obtain a writer to be used to produce content.

4

Write the markup to display.

5

Closing the writer.

GateIn Portal requires certain descriptors to be included in a portlet WAR file. Thise descriptors are defined by the Jave EE (web.xml) and Portlet Specification (portlet.xml).

The following is an example of the SimplestHelloWorldPortlet/WEB-INF/portlet.xml file. This file must adhere to its definition in the JSR-286 Portlet Specification. You may define more than one portlet application in this file:

<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd 
                                         http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
   version="2.0">
   <portlet>
      <portlet(1)-name>SimplestHelloWorldPortlet</portlet-name>
      <portlet(2)-class>
         org.gatein.portal.examples.portlets.SimplestHelloWorldPortlet
      </portlet-class>
      <support(3)s>
        <mime-type>text/html</mime-type>
      </supports>
      <portlet(4)-info>
          <title>Simplest Hello World Portlet</title>
      </portlet-info>
   </portlet>
</portlet-app>
               
1

Define the portlet name. It does not have to be the class name.

2

The Fully Qualified Name (FQN) of your portlet class must be declared here.

3

The <supports> element declares all of the markup types that a portlet supports in the render method. This is accomplished via the <mime-type> element, which is required for every portlet. The declared MIME types must match the capability of the portlet. As well, it allows you to pair which modes and window states are supported for each markup type. All portlets must support the view portlet mode, so this does not have to be declared. Use the <mime-type> element to define which markup type your portlet supports, which in this example, is text/html . This section tells the portal that it only outputs HTML.

4

When rendered, the portlet's title is displayed as the header in the portlet window, unless it is overridden programmatically. In this example, the title would be Simplest Hello World Portlet .

Let's study the Java class in detail.

The following file is the JSPHelloUser/src/main/java/org/gatein/portal/examples/portlets/JSPHelloUserPortlet.java Java source. It is split in different pieces.

package org.gatein.portal.examples.portlets;


import java.io.IOException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.UnavailableException;
public class JSPHelloUserPortlet extends GenericPortlet
{
   
   public void doView(RenderRequest request, RenderResponse response)
(1)       throws PortletException, IOException
   {
      String sYourName = (String) request.getParameter("yourname");
(2)      if (sYourName != null)
      {
         request.setAttribute("yourname", sYourName);
         PortletRequestDispatcher prd = 
(3)            getPortletContext().getRequestDispatcher("/jsp/hello.jsp");
(4)         prd.include(request, response);
      }
      else
      {
         PortletRequestDispatcher prd = getPortletContext().getRequestDispatcher("/jsp/welcome.jsp");
         prd.include(request, response);
      }
   }
...
1

As in the first portlet, we override the doView method.

2

Here we try to obtain the value of the render parameter names yourname . If defined we want to redirect to the hello.jsp JSP page, otherwise to the welcome.jsp JSP page.

3

Very similar to the Servlet way, we get a request dispatcher on a file located within the web archive.

4

The last step is to perform the inclusion of the markup obtained from the JSP.

We have seen the VIEW portlet mode, the spec defines two other modes that can be used called EDIT and HELP . In order to enable those modes, they will need to be defined in the portlet.xml descriptor as we will see later. Having those modes defined will enable the corresponding buttons on the portlet's window.

The generic portlet that is inherited dispatches the different views to methods named: doView , doHelp and doEdit . Let's watch the code for those two last portlet modes.

...

   protected void doHelp(RenderRequest rRequest, RenderResponse rResponse) throws PortletException, IOException,
         UnavailableException
   {
      rResponse.setContentType("text/html");
      PortletRequestDispatcher prd = getPortletContext().getRequestDispatcher("/jsp/help.jsp");
      prd.include(rRequest, rResponse);
   }
   protected void doEdit(RenderRequest rRequest, RenderResponse rResponse) throws PortletException, IOException,
         UnavailableException
   {
      rResponse.setContentType("text/html");
      PortletRequestDispatcher prd = getPortletContext().getRequestDispatcher("/jsp/edit.jsp");
      prd.include(rRequest, rResponse);
   }
...

If you have read the portlet specification carefully you should have notice that portlet calls happen in one or two phases. One when the portlet is just rendered, two when the portlet is actionned then rendered. An action phase is a phase where some state change. The render phase will have access to render parameters that will be passed each time the portlet is refreshed (with the exception of caching capabilities).

The code to be executed during an action has to be implemented in the processAction method of the portlet.

...

(1)         public void processAction(ActionRequest aRequest, ActionResponse aResponse) throws PortletException, IOException,
         UnavailableException
   {
(2)      String sYourname = (String) aRequest.getParameter("yourname");
(3)      aResponse.setRenderParameter("yourname", sYourname);
   }
...
1

processAction is the method from GernericPorlet to override for the action phase.

2

Here we retrieve the parameter obtained through an action URL .

3

Here we need to keep the value of yourname to make it available in the rendering phase. With the previous line, we are simply copying an action parameter to a render parameter for the sake of this example.

Let's have a look inside the JSP pages.

The help.jsp and edit.jsp files are very simple, they simply display some text. Note that we used CSS styles as defined in the portlet specification. It ensures that the portlet will look "good" within the theme and accross portal vendors.


<div class="portlet-section-header">Help mode</div>
<div class="portlet-section-body">This is the help mode, a convenient place to give the user some help information.</div>

<div class="portlet-section-header">Edit mode</div>
<div class="portlet-section-body">This is the edit mode, a convenient place to let the user change his portlet preferences.</div>

Now let's have a look at the landing page, it contains the links and form to call our portlet:

<%@ taglib uri(1)="http://java.sun.com/portlet" prefix="portlet" %>

<div class="portlet-section-header">Welcome !</div>

<br/>

<div class="portlet-font">Welcome on the JSP Hello User portlet,
my name is GateIn Portal. What's yours ?</div>

<br/>

<div class="portlet-font">Method 1: We simply pass the parameter to the render phase:<br/>
<a href="<port(2)let:renderURL><portlet:param name="yourname" value="John Doe"/>
                </portlet:renderURL>">John Doe</a></div>

<br/>

<div class="portlet-font">Method 2: We pass the parameter to the render phase, using valid XML:
Please check the source code to see the difference with Method 1.
<portlet:rende(3)rURL var="myRenderURL">
    <portlet:param name="yourname" value='John Doe'/>
</portlet:renderURL>
<br/>
<a href="<%= m(4)yRenderURL %>">John Doe</a></div>

<br/>

<div class="portlet-font">Method 3: We use a form:<br/>

<portlet:actio(5)nURL var="myActionURL"/>
<form action="(6)<%= myActionURL %>" method="POST">
         <span class="portlet-form-field-label">Name:</span>
         <input class="portlet-form-input-field" type="text" name="yourname"/>
         <input class="portlet-form-button" type="Submit"/>
</form>
</div>
1

Since we will use the portlet taglib, we first need to declare it.

2

The first method showed here is the simplest one, portlet:renderURL will create a URL that will call the render phase of the current portlet and append the result at the place of the markup (Here within a tag...). We also added a parameter directly on the URL.

3

In this method instead of having a tag within another tag, which is not XML valid, we use the var attribute. Instead of printing the url the portlet:renderURL tag will store the result in the referenced variable ( myRenderURL in our case).

4

The variable myRenderURL is used like any other JSP variable.

5

The third method mixes form submission and action request. Like in the second method, we used a temporary variable to put the created URL into.

6

The action URL is used in the HTML form.

On the third method, first the action phase is triggered then later in the request, the render phase is triggered, which output some content back to the web browser based on the available render parameters.

In order to write a portlet using JSF we need a piece of software called 'bridge' that lets us write a portlet application as if it was a JSF application, the bridge takes care of the interactions between the two layers.

Such an example is available in examples/JSFHelloUser, it uses the JBoss Portlet Bridge. The configuration is slightly different from a JSP application, since it is a bit tricky it is usally a good idea to copy an existing application that starting from scratch.

First, as any JSF application, the file faces-config.xml is required. It includes the following required information in it:


<faces-config>
...
    <application>
      <view-handler>org.jboss.portletbridge.application.PortletViewHandler</view-handler>
      <state-manager>org.jboss.portletbridge.application.PortletStateManager</state-manager>
   </application>
...
</faces-config> 

The portlet bridge libraries must be available and are usually bundled with the WEB-INF/lib directory of the web archive.

The other difference compare to a regular portlet application, can be found in the portlet descriptor. All details about it can be found in the JSR-301 specification that the JBoss Portlet Bridge implements.

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>

Now, we will add a popup which say "HelloWorld" when you click on the button.

First, create the groovy template of the popup : in webapp/groovy/testRomain/portlet, create UIHelloWorldPopupContent.gtmpl :

<div id="<%=uicomponent.getId();%>">
	HelloWorld in a popup
</div>

In java/testRomain/portlet/component, create the java file for the popup look like : {code} package testRomain.portlet.component;

package testRomain.portlet.component;

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 {
  }
}

In UITestRomainPortlet.java, we will create the popup at the portlet creation (in the constructor) :

public UITestRomainPortlet() throws Exception {

  UIPopupWindow popup = addChild(UIPopupWindow.class, null, null);
  popup.setWindowSize(400, 300);
  
  UIHelloWorldPopupContent popupContent = createUIComponent(UIHelloWorldPopupContent.class, null, null);
  popup.setUIComponent(popupContent);
  popup.setRendered(false);
 

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.

Change the treatment of the event, replace the println by :

public static class OpenPopupActionListener extends EventListener<UITestRomainPortlet> {

  public void execute(Event&lt;UITestRomainPortlet> event) throws Exception {
    UITestRomainPortlet portlet = event.getSource();
    UIPopupWindow popup = portlet.getChild(UIPopupWindow.class);
    popup.setRendered(true);
    popup.setShow(true);
  }
}

When user clicks on the button, the popup is shown.

Redeploy the portlet and click on the button. You will see "HelloWorld in a popup" in a popup. If you don't change in the portlet, try to redeploy and reboot the tomcat server.