Convert an existing AngularJS App into a Liferay Portlet

As an experiment I tried to deploy an existing AngularJS Application on an Liferay Portal Server. Just to see if it would be possible to use HTML 5 + JSON Services for Portlet development too.
It turned out to be quite simple, but it feels a bit weird of course. For example AngularJS uses hashbangs and they make browser navigation suddenly available on the portlet level.
The approach is also limited, because it might be difficult to have multiple HTML 5 portlets on a single portal page. The DOM manipulation would most likely not work properly. But it could be interesting if you provide portlets for mobile devices that run maximized anyway.

Below a step by step guide:

1. Convert the front-end

Let’s say your index.html looks like this:

<!doctype html>
<html ng-app="my-angularjs-app">
    <link rel="stylesheet" href="css/app.cs">
    <div ng-controller="HelloCntl">
     Your name: <input type="text" ng-model="name"/>
    Hello {{name || "World"}}!

    <script type="text/javascript" src="js/app.js"></script>

To run this as a portlet you have to do the following:

  1. Create a Java Web-Application as usual and copy all your resources into it
  2. In the document root create a view.jsp and copy everything within the body of index.html into it
  3. Wrap the content of view.jsp with an additional div layer and add the attribute ng-app=”my-angularjs-app”
  4. Create the usual portlet configuration files under WEB-INF
  5. Configure Liferay to add the Javascript to the footer of every portal page that contains the portlet
  6. Configure Liferay to add the Stylesheet to the header section of every portal page that contains the portlet

Here the view.jsp:

<%@ taglib uri="" prefix="portlet"%>

<portlet:defineObjects />

<div ng-app="my-angularjs-app">

  <!-- The body of index.html -->
  <div ng-controller="HelloCntl">
     Your name: <input type="text" ng-model="name"/>
    Hello {{name || "World"}}!


The portlet.xml and liferay-display.xml config files are straightforward:

<?xml version="1.0"?>
<portlet-app xmlns="" xmlns:xsi="" xsi:schemaLocation="" version="2.0">
<?xml version="1.0"?>
<!DOCTYPE display PUBLIC "-//Liferay//DTD Display 6.1.0//EN" "">
    <category name="AngularJSPortlets">
        <portlet id="my-angularjs-app-portlet" />

In liferay-portlet.xml we need to include the javascripts and stylesheets from the index.html:

<?xml version="1.0"?>
<!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 6.0.0//EN" "">

If you have multiple template files in your application you might have to fix the path to them, because relative URLs won’t work in Portal environment. The URLs need to point to the context of your deployed application. For example, if you have a route like this:

  {templateUrl: 'page1.html', controller: 'Page1Ctrl'});
  {templateUrl: 'page2.html', controller: 'Page2Ctrl'});

They should now look like this:

  {templateUrl: '/my-application/page1.html', controller: 'Page1Ctrl'});
  {templateUrl: '/my-application/page2.html', controller: 'Page2Ctrl'});

I know, that’s a bit odd, to hardcode an application context, but you could make the path configurable.

That’s basically it for the front-end. If you deploy your application within a WAR on Liferay you should be able to add your portlet to a portal page.

2. Integrate the back-end

Essentially, there are two possibilities here:

  1. Call remote REST services. Here you could get troubles with the Same-Origin-Policy, but fortunately, AngularJS supports JSONP. Is you choose this solution you’ve nothing else to do.
  2. Deploy the back-end (the services) together with your portlet. You cannot get problems with the Same-Origin-Policy and it is even possible to obtain the currently authenticated portal user in the service code.

I chose option 2, and deployed the Spring MVC REST back-end as part of the portlet. To hide the application context of the portlet and to be able to obtain the shared session attributes of Liferay (e.g. the authenticated user) I’ve used Liferay’s PortalDelegateServlet:

In web.xml of the portlet application:


If you’ve a service defined like this in Spring MVC:

public class MyService {
  public List<Customer> customers() {

You can now access your the service under:


Please note: This only works, if the sub-context in the delegate definition matches the path prefix of the services.

The advantage of using the delegate (instead of directly accessing the service in the context of the webapp containing your portlet) is that could can obtain the currently authenticated Liferay user like this:


3. Alternative approach with Spring Portlet MVC

I also tried to use Spring Portlet MVC to get a more traditional portlet setup. It would also have the advantage that the back-end is actually container authenticated and e.g. request.isInRole() could be used.

The problem was, it required a lot of additional configuration and a specific Portlet controller. Also, “normal” URLs couldn’t be used for service calls, they needed to be constructed as Action URLs like this:

<portlet:actionURL var="serviceURL" >
    <portlet:param name="serviceName" value="myServiceName"/>
   // Javascript
   var url = "${serviceURL}";

At the end it was too much effort for me to get it running.

Remove duplicate javscript resources of JSF portlets in Liferay

A common problem when developing JSF portlets for a portal server is that each portlet pulls all required resources (JavaScript, CSS) into the resulting portal page. If you have six portlets on a portal page, all the huge JavaScript files are loaded and parsed six times.

Although this is a common problem, i found no common solution for that. I tried a couple of approaches and the best one seemed to be to write a custom JSF head renderer to suppress rendering a resource if it already has been rendered by another portlet (within the same request). So, only the first rendered portlet pulls the resource into the portlet page.

My implementation is very specific for IceFaces and the Liferay Faces Bridge, but the concept itself should be applicable for all JSF frameworks.

Below the implementation of the HeadRenderer. It simple stores all already rendered resources in a shared request attribute (by default all attributes starting with LIFERAY_SHARED_ are automatically shared for all portlets, even across multiple WARs).

package at.nonblocking.icefaces.renderkit;

public class SuppressDuplicateJsHeadRenderer extends HeadRendererICEfacesImpl {

  private static final Logger LOG = LoggerFactory.getLogger(SuppressDuplicateJsHeadRenderer.class);
  private static final String ICEFACES_VERSION_KEY = "ICEFACE_3_0_0";  

  private static final String EXTENSION_CSS = "css";

  public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException {
    // Ignore AJAX requests
    if (facesContext.getPartialViewContext().isAjaxRequest()) {

    ExternalContext externalContext = facesContext.getExternalContext();
    PortletRequest portletRequest = (PortletRequest) externalContext.getRequest();
    BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
    PortletContainer portletContainer = bridgeContext.getPortletContainer();
    String portletName = bridgeContext.getPortletConfig().getPortletName();

    PortletNamingContainerUIViewRoot uiViewRoot = (PortletNamingContainerUIViewRoot) facesContext.getViewRoot();
    List<UIComponent> uiComponentResources = buildCssJsResourceList(facesContext, uiComponent, uiViewRoot);

    // Save a temporary reference to the ResponseWriter provided by the
    // FacesContext.
    ResponseWriter responseWriterBackup = facesContext.getResponseWriter();

    // Replace the ResponseWriter in the FacesContext with a HeadResponseWriter
    // that knows how to write to
    // the <head>...</head> section of the rendered portal page.
    HeadResponseWriter headResponseWriter = (HeadResponseWriter) portletRequest.getAttribute("headResponseWriter");

    if (headResponseWriter == null) {
      headResponseWriter = (HeadResponseWriter) portletContainer.getHeadResponseWriter(responseWriterBackup);

    portletRequest.setAttribute("headResponseWriter", headResponseWriter);

    // Store the rendered resources in the portlet request as shared attribute - works accross multiple WARs
    List<String> addedResources = (List<String>) portletRequest.getAttribute(RENDERED_RESOURCES_MAP_REQUEST_ATTRIBUTE);
    if (addedResources == null) {
      addedResources = new ArrayList<String>();
      portletRequest.setAttribute(RENDERED_RESOURCES_MAP_REQUEST_ATTRIBUTE, addedResources);

    for (UIComponent uiComponentResource : uiComponentResources) {
      String resourceName = (String) uiComponentResource.getAttributes().get("name");

      if (addedResources.contains(resourceName)) {
        LOG.debug("Ignoring resource '{}' of portlet '{}' because it already has been added to html head.", resourceName, portletName);
      } else {
        LOG.debug("Adding resource '{}' of portlet '{}' to html head.", resourceName, portletName);

        // Command the resource to render itself to the HeadResponseWriter

    // Restore the temporary ResponseWriter reference.

  private List<UIComponent> buildCssJsResourceList(FacesContext facesContext, UIComponent uiComponent, PortletNamingContainerUIViewRoot uiViewRoot) {
    List<UIComponent> uiComponentResources = new ArrayList<UIComponent>();

    // Add the list of components that are to appear first.
    List<UIComponent> firstResources = getFirstResources(facesContext, uiComponent);

    if (firstResources != null) {

    // Sort the components that are in the view root into stylesheets and
    // scripts.
    List<UIComponent> uiViewRootComponentResources = uiViewRoot.getComponentResources(facesContext, TARGET_HEAD);
    List<UIComponent> uiViewRootStyleSheetResources = null;
    List<UIComponent> uiViewRootScriptResources = null;

    for (UIComponent curComponent : uiViewRootComponentResources) {
      String resourceName = (String) curComponent.getAttributes().get("name");

      if ((resourceName != null) && resourceName.endsWith(EXTENSION_CSS)) {

        if (uiViewRootStyleSheetResources == null) {
          uiViewRootStyleSheetResources = new ArrayList<UIComponent>();

      } else {

        if (uiViewRootScriptResources == null) {
          uiViewRootScriptResources = new ArrayList<UIComponent>();


    // Add the list of stylesheet components that are in the view root.
    if (uiViewRootStyleSheetResources != null) {

    // Add the list of components that are to appear in the middle.
    List<UIComponent> middleResources = getMiddleResources(facesContext, uiComponent);

    if (middleResources != null) {

    // Add the list of script components that are in the view root.
    if (uiViewRootScriptResources != null) {

    // Add the list of components that are to appear last.
    List<UIComponent> lastResources = getLastResources(facesContext, uiComponent);

    if (lastResources != null) {

    return uiComponentResources;


To install the new HeadRenderer i’ve created a JSF RendererKitFactory and defined it in META-INF/services/javax.faces.render.RenderKitFactory:


So far, so good, but now it gets a bit tricky. We have to take care to not unintentionally remove the existing IceFaces, Mojarra and Bridge RenderKitFactory’s. What I did is to inherit my custom RenderKitFactory from com.liferay.faces.bridge.renderkit.html_basic.RenderKitBridgeImpl and just replace the existing Bridge RenderKitFactory.

The RenderKitFactory hierarchy before installing the custom Factory is:
-> org.icefaces.impl.renderkit.DOMRenderKit
  -> com.liferay.faces.bridge.renderkit.html_basic.RenderKitBridgeImpl
    -> com.sun.faces.renderkit.RenderKitImpl

And after it:
-> org.icefaces.impl.renderkit.DOMRenderKit
  -> at.nonblocking.icefaces.renderkit.SuppressDuplicateJsRenderKitFactory
    -> com.sun.faces.renderkit.RenderKitImpl

Below my RenderKitFactory and the RenderKit to introduce the SuppressDuplicateJsHeadRenderer:

package at.nonblocking.icefaces.renderkit;

public class SuppressDuplicateJsRenderKitFactory extends RenderKitFactoryImpl {

  private static final Logger LOG = LoggerFactory.getLogger(SuppressDuplicateJsRenderKitFactory.class);
  public void addRenderKit(String renderKitId, RenderKit renderKit) {

    if (renderKitId.equals(HTML_BASIC_RENDER_KIT) && renderKit instanceof DOMRenderKit) {"Installing Javascript reducing RenderKit {}", SuppressDuplicateJsRenderKit.class);
      try {
        Field delegateField = renderKit.getClass().getDeclaredField("delegate");
        delegateField.setAccessible(true); //Reflection hack
        RenderKitBridgeImpl bridgeRenderKit = (RenderKitBridgeImpl) delegateField.get(renderKit);        
        RenderKit originalRenderKit = bridgeRenderKit.getWrapped();  //instance of com.sun.faces.renderkit.RenderKitImpl
        delegateField.set(renderKit, new SuppressDuplicateJsRenderKit(originalRenderKit));
      } catch (NoSuchFieldException e) {
        LOG.error("Failed to install Javascript reducing RenderKit!", e);
      } catch (IllegalArgumentException e) {
        LOG.error("Failed to install Javascript reducing RenderKit!", e);
      } catch (IllegalAccessException e) {
        LOG.error("Failed to install Javascript reducing RenderKit!", e);

    super.addRenderKit(renderKitId, renderKit);
package at.nonblocking.icefaces.renderkit;

public class SuppressDuplicateJsRenderKit extends RenderKitBridgeImpl {

  private static final String JAVAX_FACES_HEAD = "javax.faces.Head";
  private static final String JAVAX_FACES_OUTPUT = UIOutput.COMPONENT_FAMILY;
  public SuppressDuplicateJsRenderKit(RenderKit wrappedRenderKit) {
  public Renderer getRenderer(String family, String rendererType) {
      if (JAVAX_FACES_OUTPUT.equals(family) && JAVAX_FACES_HEAD.equals(rendererType)) {
          return new SuppressDuplicateJsHeadRenderer();      
      return super.getRenderer(family, rendererType); 

That’s it. You can test the effect with the Google Page Speed plug-in, it will warn you if any resources are still present multiple times.

There might be better approaches to suppress duplicate resources in a portal and if you know one, please leave a comment.