In this article I will show you how you can embed an existing Liferay portlet into your own plug-in portlet without using Ext. This might be useful in several scenarios, for example: building your own ‘nested portlet’ portlet or include the web form portlet in your own portlet.
The solutions works in Liferay 6.0 EE SP1 and can include portlets from Liferay itself as well as portlets coming from other plug-ins. Other versions of Liferay have not been tested yet.
I hope this snippet is as useful to you as it is to me!
package nl.liones.liferay;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.portlet.PortletBag;
import com.liferay.portal.kernel.portlet.PortletBagPool;
import com.liferay.portal.kernel.util.WebKeys;
import com.liferay.portal.service.PortletLocalServiceUtil;
import com.liferay.portal.theme.PortletDisplay;
import com.liferay.portal.theme.ThemeDisplay;
import com.liferay.portal.util.PortalUtil;
import com.liferay.portal.util.PortletKeys;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import javax.portlet.ValidatorException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
* Portlet embed utility method.
*
* @author Stef Busking (Liones), Bert Willems (Liones)
*/
public class RuntimePortletEmbedUtil {
/**
* Renders the given portlet as a runtime portlet and returns the portlet's HTML.
*
* @param request The {@link PortletRequest}.
* @param response The {@link PortletResponse}.
* @param portletId The ID of the portlet to render, includes instance ID for instance portlets.
* @param queryString The query string to use for the portlet.
* @return The HTML for the given portlet.
* @throws SystemException on error.
* @throws IOException on error.
* @throws ServletException on error.
* @throws ValidatorException on error.
*/
public static String renderPortlet(final PortletRequest request, final PortletResponse response, final String portletId, final String queryString) throws SystemException, IOException, ServletException, ValidatorException {
// Get servlet request / response
HttpServletRequest servletRequest = PortalUtil.getOriginalServletRequest(PortalUtil.getHttpServletRequest(request));
HttpServletResponse servletResponse = PortalUtil.getHttpServletResponse(response);
// Get theme display
final ThemeDisplay themeDisplay = (ThemeDisplay) servletRequest.getAttribute(WebKeys.THEME_DISPLAY);
// Backup current state
PortletDisplay portletDisplay = themeDisplay.getPortletDisplay();
PortletDisplay portletDisplayClone = new PortletDisplay();
portletDisplay.copyTo(portletDisplayClone);
final Map<String, Object> requestAttributeBackup = new HashMap<String, Object>();
for (final String key : Collections.list((Enumeration<String>) servletRequest.getAttributeNames())) {
requestAttributeBackup.put(key, servletRequest.getAttribute(key));
}
// Render the portlet as a runtime portlet
String result;
try {
PortletBag implPortletBag = PortletBagPool.get(PortletKeys.JOURNAL);
com.liferay.portal.model.Portlet portlet = PortletLocalServiceUtil.getPortletById(PortalUtil.getCompanyId(request), portletId);
servletRequest.setAttribute(WebKeys.RENDER_PORTLET_RESOURCE, Boolean.TRUE);
result = PortalUtil.renderPortlet(implPortletBag.getServletContext(), servletRequest, servletResponse, portlet, queryString, false);
} finally {
// Restore the state
portletDisplay.copyFrom(portletDisplayClone);
portletDisplayClone.recycle();
for (final String key : Collections.list((Enumeration<String>) servletRequest.getAttributeNames())) {
if (!requestAttributeBackup.containsKey(key)) {
servletRequest.removeAttribute(key);
}
}
for (final Map.Entry<String, Object> entry : requestAttributeBackup.entrySet()) {
servletRequest.setAttribute(entry.getKey(), entry.getValue());
}
}
return result;
}
}
Using this code
The snippet below demos how to embed the Liferay Web Form portlet and how to set a preference for this embedded portlet.
final String portletId = "1_WAR_webformportlet_INSTANCE_abcd";
final ThemeDisplay themeDisplay = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);
// get the preferences
final PortletPreferences prefs = PortletPreferencesUtilityLocalServiceUtil.getWrappedPortletPreferences(themeDisplay, portletId);
// set a preference
prefs.setValue("successURL", "some url");
// dont forget to store the preferences
formPrefs.store();
// get the HTML from the portlet
final String portletHtml = RuntimePortletEmbedUtil.renderPortlet(request, response, portletId, "");
8 Replies to “Liferay: Embedding Portlets in your Portlet”
July 27th, 2011 at 17:49
Hello,
Can you give me some hint how to pass portlet-preferences for this runtime portlet?
July 27th, 2011 at 20:46
Hello Parimajan,
I have updated the article with a sample, hope this helps.
Regards,
Bert
July 28th, 2011 at 10:55
10x a lot!
August 8th, 2011 at 12:30
Thanks for sharing this! Very useful.
Would it be possible to facilitate one way or two way IPC between the plug-in portlet and the embedded portlet using this approach?
August 8th, 2011 at 16:55
Hello Stian,
In theory it should be possible, however Liferay does currently not facilitate it.
See http://issues.liferay.com/browse/LPS-8098 for details.
Regards,
Bert
August 8th, 2011 at 20:27
Thanks Bert. Having embedded portlets participate in the normal portlet lifecycle would certainly be useful for building apps where you want to make use of portlets for information display (amongst other things I’m sure).
I’m exploring a different use case at present which possibly could benefit from embedded portlets. I’d like to give the embedded portlets their own IPC environment. i.e. the plug-in portlet would process IPC events and publish select IPC events for its embedded portlet(s) as and when it deems appropriate. The embedded portlets should also be able to publish IPC events that the plug-in portlet processes and then publishes for other pages on the portal page to process.
This would enable composite apps to be built that operate in predictable environments, yet can cooperate with other portlets on the same portal page.
I guess you could describe this as the plug-in portlet operating its own IPC events messaging bus.
If you have any thoughts on this I’d be very interesting to hear them.
Regards,
Stian
October 19th, 2011 at 15:45
Thank you very much for the snippet!
It works very well but I’m trying to set some preferences on the embedded portlet and I would need the “PortletPreferencesUtilityLocalServiceUtil” class.
Could you share it, please?
October 20th, 2011 at 08:09
Just for share info: we’ve found an alternative to “PortletPreferencesUtilityLocalServiceUtil.getWrappedPortletPreferences”:
PortletPreferencesFactoryUtil.getPortletPreferences(request, portletId);