Portlets Plugin
Please create an issue in JIRA under the Grails-Portlets component if you find problems or come up with ideas for enhancements.
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. "Portlets" plugin
is automatically installed by installing those plugins.

You can generate container specific portlet war file by installing "portlets-liferay" or "portlets-pluto". "liferay-exploded" or "pluto" allows to test with those portal container from grails environment.
Installation
Portlets plugin should be implicitly installed by installing a container
specific portlets plugin.
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']]// Liferay server specific configurations
def liferay_display_category = 'MyCategory' 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. Please note
portletRequest and
portletResponse are bound to the GSP.
<%@ 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="${portletResponse.createActionURL()}">
<input type="submit" value="Submit"/>
</form>
</div>Deploy to container
You can generate portlet war file just same as a servlet war file.
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.
- 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.
- 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.
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.
History
0.7 (11/4/09)
- Got rid of unused Jetty import statements.
0.5 (09/22/09)
- Added commons-logging-1.1.1.jar to fix the deployment issue on liferay-5.2.3 with tomcat-6 due to listener registration failure.
0.4 (06/30/09)
- Fixed unbalanced curly bracket in gsp template files.
0.3 (05/28/09)
- Updated gsp template so that submission works on both pluto and liferay.
- Added
liferay_display_category field to the template so that you can generate
Liferay compatible portlet without any modifications.
0.2 (05/25/09)
Initial release.