My favorites | Sign in
Project Home Downloads Wiki Issues Source
Project Information
Members

Tapestry 5 Portlet

Update (2010-05-05)

Fixed a problem, where all urls returned by an XMLHttpRequest were encoded with wrong Window state.

Update (2010-03-11)

Fixed a problem, where page Activation Context was not encoded into event/action links.

Preamble

The code is based on the JIRA commitments by Trun Le Xuan and Kristina B. Taylor: https://issues.apache.org/jira/browse/TAP5-64

Up to now it is only working in Liferay Portal, because some features are based on the liferay api (explanation above).

Features

Origin Features:

  • Render Requests
  • Action Requests

The following features have been added to the origin commitments:

  • XHR Requests (AJAX)
  • Resource Serving (Portlet 2.0 Download Functionality)

Architecture

The class-diagram should give you a quick impression: Link

Usage

Basic Configuration



The project is based on maven and eclipse wtp, so just import it.

Make sure the additional libraries are in your classpath:

  • Portlet 2.0 API
  • Liferay Kernel (portal-kernel.jar)

Portlet Development is almost the same as developing a default tapestry application, but there are some things you have to be aware of:

There are two IoC registries for each portlet.

The first registry (refered as "PortletRegistry") is initiated in the "ApplicationPortlet.class". This class is the first one reached in the portlet lifecycle, it delegates the different requests (action, render, resource) to their corresponding handlers.

The second one (refered as "FilterRegistry") is created in the Tapestry Filter, which is only used for assets, so make sure the Tapestry filter maps to "/assets/*", otherwise your portlet would be accessible from outside the portal.

Each registry has its own custom module for configuration:

  • "Portlet" Registry - PortletModule.class located in org.apache.tapestry5.portlet.services
  • Here you should do your custom configurations e.g. add new services, configure existing ones
  • "Filter" Registry - by default AppModule.class
  • You shouldn't add any custom services here, because as I already mentioned it is only used for serving Assets



To avoid redundancy configuration I also included the AppModule.class in my PortletRegistry to contribute ApplicationDefaults. If you don't do so you would have for example specify APPLICATION_VERSION twice, otherwise assets are to be searched on the wrong path, because a random version number is generated, which is only known either to the FilterRegistry or the PortletRegistry.

If your class is not named "AppModule", you will have to edit the PortletUtilities.class in org.apache.tapestry5.portlet. Just replace appInitializer.addModules(AppModule.class); by appInitializer.addModules(YourModule.class);.

Resource Serving / Download Functionality

StreamResponses can only be returned as a result of an ActionEvent. The id of an resource serving ActionLink must end with "resource" e.g.: <t:actionlink t:id="downloadresource">Download</t:actionlink>



It is not possible to serve resources from page activation (onActivate()) (see redownloadresource and the pagelink in the sources - Index.tml)

Portlet Services / Access PortletRequest

Inject PortletRequestGlobals to access the underlying PortletRequest, RenderRequest, ActionRequest, ResourceRequest (for example to check user roles etc.)

Application Catalog

To be able to use the application catalog, you have to define it manually:

configuration.add(SymbolConstants.APPLICATION_CATALOG, "context:WEB-INF/app.properties");

Customization / Port to your portlet-container of choice

As I mentioned before the portlet functionality only works for Liferay.

In order to make it work with other portal implementations the following parts must be customized:

Recognize XHR Request

Tapestry recognizes XHR Requests by looking into the http request headers. If the header attribute X-Requested-With with the value XMLHttpRequest is present, the request is XHR.

The portlet api does not provide direct access to the underlying http request. The Portlet specification states:

A portlet can access portal/portlet-container specific properties and, if available, the
headers of the HTTP client request through the following methods of the methods of the
PortletRequest interface:
• getProperty
• getProperties
• getPropertyNames

...

Depending on the underlying web-server/servlet-container and the portal/portletcontainer
implementation, client request HTTP headers may not be always available.

Liferay does not put the http request headers into the PortletRequest properties, so I had to rely on the liferay api, which provides access to the HttpServletRequest.



class: PortletRequestImpl package: org.apache.tapestry5.internal.portlet.services method: isXHR()

LiferayPortletRequest liferayRequest = (LiferayPortletRequest) _request;

if (XML_HTTP_REQUEST.equals(liferayRequest.getHttpServletRequest().getHeader((REQUESTED_WITH_HEADER)))){
    	   _logger.info("REQUEST IS XHR");
    	   return true;
    	} else {
    	   return false;
    	}

So go find out, if your portlet-container puts the request headers into the PortletRequest properties. If it does you are lucky and you dont have to rely on your portlet-container api.

XHR Links

Currently XHR Requests and XHR Responses are handled via render urls with special liferay specific parameters (LiferayWindowsState.EXCLUSIVE):

see ComponentEventLinkEncoderImplPortlet.class in org.apache.tapestry5.internal.portlet.services

	portletURL = renderResponse.createRenderURL();
        portletURL.setPortletMode(portletRequest.getPortletMode());
 	portletURL.setWindowState(LiferayWindowState.EXCLUSIVE);

in your portal implementation you would rather use resource links for XHR:

	portletURL = renderResponse.createResourceURL();

TODO

The FilterRegistry loads all default Tapestry Services (TapestryModule.class), though only services for handling assets are needed. There should be a way to write a filter, which only loads the necessary services.

Share the portlet registry among portlets (put PortletUtilities.class in the portlet-container shared dir ?).

Powered by Google Project Hosting