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, "");

16 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);
May 24th, 2012 at 04:35
[...] might want to look at a portlet called NestedPortlet that does exactly that and inspect it. Also, Bert Willems has written a nice article about this topic. Tagged: Liferayliferay-6questions /* * * CONFIGURATION [...]
September 4th, 2012 at 16:26
Thanks a lot for sharing, this works great..
October 17th, 2012 at 20:25
[...] are some suggestions on the web on how this can be achieved, but the code seems to be somewhat complex, which makes it a [...]
October 23rd, 2012 at 11:44
Thanks for the code snippet. It worked nicely in Liferay 6.1.0 but unfortunately it broke with exceptions in 6.1.1. Not sure if this is a Liferay bug but if anyone else encounters this here is what we ended up doing to work around the issue:
1) Using the version of PortalUtil.renderPortlet which takes (among other things) a path argument
2) Setting the path argument to “/html/portal/load_render_portlet.jsp”
This fixes the exceptions but renders the portlets as a template, so ..
3) Create a copy of load_render_portlet.jsp and place it in jsp_overrides in the hook project so that it deploys to the server
4) Change the path argument to refer to this modified file
5) In the modified file, remove the <c: tags and the [$TEMPL.. line.
Hope this helps. Took me two days to work out.
December 12th, 2012 at 23:07
Hi,
Thank you for your code, it works great with liferay 6.1 with Martin instructions. It makes my day as it took me just a few hours to come up with something working !
I had some problem rendering my custom spring mvc portlets, because liferay won’t accept to add portlet in a page where it was not added by someone with the right to do so.
So after digging render_portlet.jsp I found out I have to had them all to the properties
portlet.add.default.resource.check.whitelist
or just switch
portlet.add.default.resource.check.enabled to false (which is not a great idea in production env but quite handy in dev).
Also I had to adapt the setting prefs so that it would copy the prefs from the other portlet that is being cloned from. Here it is :
// Pour pouvoir rendre la portlet correctement il faut avoir les preferences settees a l’identique.
PortletPreferences originalPrefs = PortletPreferencesFactoryUtil.getStrictLayoutPortletSetup(layout, portletId);
PortletPreferences targetPrefs = PortletPreferencesFactoryUtil.getStrictLayoutPortletSetup(renderLayout, portletId);
for (Entry entry : targetPrefs.getMap().entrySet()) {
targetPrefs.reset(entry.getKey());
}
for (Entry entry : originalPrefs.getMap().entrySet()) {
targetPrefs.setValues(entry.getKey(), entry.getValue());
}
// Sauvegarde des preferences.
targetPrefs.store();
final String portletHtml = RuntimePortletEmbedUtil.renderPortlet(request, response, portletId, “”);
December 24th, 2012 at 14:35
snippet is very useful for nested portlet and also working well.
Thanks for sharing.
January 18th, 2013 at 15:14
Hi,
Thanks a lot for the above solution.
In the above code if we set:
com.liferay.portal.model.Portlet portlet = PortletLocalServiceUtil.getPortletById(PortalUtil.getCompanyId(request), portletId);
portlet.setRenderWeight(0); // following is added.***
servletRequest.setAttribute(WebKeys.RENDER_PORTLET_RESOURCE, Boolean.TRUE);
result = PortalUtil.renderPortlet(implPortletBag.getServletContext(), servletRequest, servletResponse, portlet, queryString, null, null, null, path, false);
Then I noticed that although we do need to provide the path, “/html/portal/load_render_portlet.jsp” as explained by Martin, we dont need to create a hook.
I noticed that when I made above changes, portlets like hello world, which dont usually get embedded by tag
also get embedded properly, by calling
RuntimePortletEmbedUtil.renderPortlet(renderRequest, renderResponse, portletId,”", “/html/portal/load_render_portlet.jsp”);
in Jsp.
However I am running in permission issue.
when I use above approach, if I am including a custom portlet, it says,
“You do not have the roles required to access this portlet.”
in the embedded portlet.
Not sure of the reason.
I am new to liferay, so I might be making some basic mistake.
Please help!!
January 24th, 2013 at 12:11
Hi,
i don’t understand where i have to put this code. I have a webcontent page for create a new webcontent and i would like to put my custom portlet in this page, because i have to find first the register users and after pubblish the webcontent. How can i get it? I tried with hook and ext, byt don’t work. I’m sorry for my english, i hope you understand my question. Thanks.