Portlets Plugin

Portlets Plugin

Overview

This plugin aims to provide a simple (grails) way of developing JSR-168 portlets using well known Grails conventions that can be deployed to compliant Portal servers. Currently Liferay 5.2 and Pluto 1.1.7 are supported.

JSR-286 will be supported once springframework bundled with Grails is upgraded to 3.0. spring-mvc-portlet 2.5.6 doesn't support JSR-286.

Portlets plugin consists of "grails-portlets-plugin" generating JSR-168 compliant configuration files, "grails-portlets-liferay" for Liferay specific configurations, "grails-portlets-pluto" for Pluto specific configuration, "grails-pluto", which adds pluto runtime to grails and launch the container with "run-app" command, and "grails-liferay-exploded", allowing a dynamic reloading of classes and GSPs on a stand-alone Liferay server.

"grails-portlets-plugin" doesn't provide a container specific support so it won't work unless implementation specific plugin such as "grails-portlets-liferay" or "grails-portlets-pluto" is installed. This plugin is automatically installed by installing those plugins.

Installation

Portlets plugin should be implicitly installed by installing a container specific portlets plugin.

Automatic plugin dependency resolution is not working against the portlet which name contains hyphens due to a bug GRAILS-4586. In the meanwhile, we can work around the issue by manually installing "portlets-pluto" or "portlets-liferay".

Liferay portlets

Please see portlets-liferay and liferay-exploded for the details.
export LIFERAY_HOME=/your_path_to/liferay-portlet-5.2.2
grails install-plugin portlets-liferay
grails install-plugin liferay-exploded

Pluto portlets

Please see portlets-pluto and pluto for the details.
grails install-plugin portlets-pluto  
grails install-plugin pluto

Create a portlet

grails create-portlet MyFirst

The command generates a portlet file "grails-app/portlets/MyFirstPortlet.groovy" as following.

import javax.portlet.*

class MyFirstPortlet {

def title = 'Portlet Title' def description = ''' Description about the portlet goes here. ''' def displayName = 'Display Name' def supports = ['text/html':['view', 'edit', 'help']]

def actionEdit = { //TODO Define action phase portletResponse.setPortletMode(PortletMode.VIEW) }

def renderEdit = { //TODO Define render phase. Return the map of the variables bound to the view ['mykey':'myvalue'] }

def actionView = { //TODO Define action phase }

def renderView = { //TODO Define render phase. Return the map of the variables bound to the view ['mykey':'myvalue'] }

def actionHelp = { //TODO Define action phase portletResponse.setPortletMode(PortletMode.VIEW) }

def renderHelp = { //TODO Define render phase. Return the map of the variables bound to the view ['mykey':'myvalue'] } }

"actionXXX" is invoked when a form is submitted, and "renderXXX" is invoked when the page is rendered.

The following variables are bound to the portlet class.

To generate views, run the following command.

grails generate-portlet-views MyFirst

It generates edit.gsp, help.gsp, and view.gsp under "grails-app/views/myfirst" directory. View.gsp looks like this.

<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<div>
<h1>View Page</h1>
The map returned by renderView is passed in. Value of mykey: ${mykey}
<form action="<portlet:actionURL/>">
    <input type="submit" value="Submit"/>
</form>
</div>

Deploy to container

You can generate portlet war file just same as a servlet war file.

grails war

Please follow the instruction of the portlet container to deploy the portlet war file.

Pluto

If you install "grails-pluto" plugin, you can run Pluto and deploy the portlet and hot reload the modifications in place. For the details, see Pluto Plugin.

Liferay

If you install "grails-liferay-exploded" plugin, you can deploy an exploded war file to Liferay, and dynamically reload and reflect your changes in the grails project.
  1. Run a deployment command. It generates a war, expand, and copy context.xml
file to Liferay's deploy directory. You can run this command when the server is running or stopped.
grails liferay-deploy
  1. Monitor the console output, and confirm the servlet is successfully
registered.

(liferay1 is the name of a sample portlet war file)
…
21:33:16,176 INFO  [PortletHotDeployListener:219] Registering portlets for liferay1
21:33:18,050 INFO  [PortletHotDeployListener:303] 2 portlets for liferay1 are available for use
...

Once you deploy, you can change the code in the grails projects, and reflect the changes to the running Liferay server by invoking the following command.

grails liferay-update

For the details, please refer to Liferay exploded plugin.

"liferay-exploded" plugin is designed for development purposes only. It utilizes tomcat's hot deployment feature, but due to a memory leak on reloading, you will get OutOfMemoryError after four or five executions of "liferay-update". For production, please deploy the packaged war through Liferay's deploy directory or administration GUI.

Unit Testing

Your portlet groovy classes can be unit tested as per normal Grails controllers.

Functional Testing

The Portlets Plugin includes an embded apache Pluto portlet container (Version 1.1.7) which will be deployed during grails run-app, f you install pluto plugin. Browse to localhost:8080/pluto (login as pluto/pluto). Your portlets will be displayed on the default tab labeled 'Home'. This allows basic functional testing via the WebTest plugin.

Known Issues

TODO

Reference

Configuration

Standard property names are used to generate the portlet.xml information for each portlet. They should be defined at the class level and initialized. e.g.
def title = 'HelloWorld'
def displayName = 'Grails Hello World Portlet'
def supports = ['*/*':['view', 'edit']]

There are portal container specific properties too. Please consult portlets-liferay and portlets-pluto plugins for the details.

Portlet Information

title (mandatory)

the title of the portlet (normally appears in the title bar when the portlet is displayed)

description

a description of the portlet

displayName (mandatory)

the name of the portlet when displayed to the user

shortTitle

a short title for your portlet

keywords

keywords that describe your portlet

Portlet Mode

supports (mandatory)

a map containing mime types and a list containing supported window modes. Standard modes available (According to the spec) are view, edit and help

customModes

a map containing custom window modes and their descriptions. Any custom modes referenced in supports need to be declared here.

Portlet Preferences

supportedPreferences

a map defining the portlet preferences supported by your portlet. The key is the parameter name, the value is the default value for the preference. Multiple values can be supplied as a list and will create a multi-valued portlet preference.

Security

roleRefs

a list or map that specifies the roles required to access this portlet. It is either a list of role names or a map wher the key is the role name and the value is the role link (see the Portlet spec for more details on what how these fields are used). These values are used to generate <security-role-ref> elements.

Custom Window States

customWindowStates

a map containing custom window states used by this portlet and their descriptions

Internationalization

TODO

Action Conventions

Simple case

For simple cases a portlet can define two closures: doAction and doRender which will be called as per the Portlet specification request behaviour e.g. doRender each time a page with the portlet on is rendered and doAction when a form inside the portlet posts to the actionURL.

Portlet mode based action

action and render closures with portlet mode as a prefix (e.g. actionView, renderView) are invoked.

parameter based action

It hasn't been tested. You can inspect a request parameter in portlet mode based action, and dispatch based on the value as an interim solution.

actionView = {
    if(portletRequest.getParameter('action') == 'foo') {
        actionFoo()
    }
}

private actionFoo() { … }

View Conventions

The plugin will first look for a view matching the current portlet mode e.g view.gsp, edit.gsp, help.gsp etc. If a view cannot be found it will fallback to render.gsp.

Convenience properties

The following properties are provided for use inside actions to access common objects provided by the Portlet API.

Request/Response

portletRequest

the current PortletRequest. It will be a RenderRequest or an ActionRequest depending on which controller action is executing (a render or an action call).

portletResponse

the current PortletResponse. It will be a RenderResponse or an ActionResponse depending on which controller action is executing (a render or an action call).

request

It is an instance of HttpServletRequest of the underlying portlet container. You should use portletRequest instead of request for normal portlet operations. It is available only when you need to something special with the underlying servlet request and use at your own risk.

response

It is an instance of HttpServletResponse of the underlying portlet container. You should use portletResponse instead of response for normal portlet operations. It is available only when you need to something special with the underlying servlet request and use at your own risk.

Portlet Session

session

the current PortletSession.

Portlet Preferences

preferences

the PortletPreferences object for the current user.

Window State

windowState

the current WindowState.

Portlet Mode

mode

the current PortletMode.

3 Comments

  • Gravatar
    How close is this plugin to functional? I've got a new portal project coming up and would love to do this in Grails.
    Mar 28, 2009 08:03 AM punkisdead
  • Gravatar
    +1 for me. My project starts next week…
    Apr 14, 2009 10:04 AM Zerzio
  • Gravatar
    +1 for me

    we could participate & help finishing the plugin. Who shall we contact?

    cheers! Jack

    May 14, 2009 14:05 PM JackTheNippel

Post a Comment