Accessing Services in JSPs

@Reference can't be used in JSPs, so how do you get OSGi JSPs?

Introduction

When developing JSP-based portlets for OSGi deployment, and even when doing JSP fragment bundle overrides, it is often necessary to get service references in the JSP pages. But OSGi @Reference won't work in the JSP files, so we need ways to expose the services so they can be accessed in the JSPs...

Retrieving Services in the JSP

So we're going to work this a little backwards, we're going to cover how to get the service reference in the JSP itself.

In order to get the references, we're going to use a scriptlet to pull the reference from the request attributes, similar to :

<%
  TrashHelper trashHelper = (TrashHelper) request.getAttribute(TrashHelper.class.getName());
%>

The idea is that we will be pulling the reference directly out of the request attributes. We need to cast the object coming from the attributes to the right type, and we'll be following the Liferay standard of using the full class name as the attribute key.

The challenge is how to set the attribute into the request.

Setting Services in a Portlet You Control

So when you control the portlet code, injecting the service reference is pretty easy.

In your portlet class, you're going to add your @Reference for the service you need to pass. Your portlet class would include something along the lines of:

@Reference(unbind = "-")
protected void setTrashHelper(TrashHelper trashHelper) {
  this._trashHelper = trashHelper;
}

private TrashHelper _trashHelper;

With the reference available, you'll then override the render() method to set the attribute:

@Override
public void render(RenderRequest renderRequest, RenderResponse renderResponse)
    throws IOException, PortletException {

  renderRequest.setAttribute(TrashHelper.class.getName(), _trashHelper);

  super.render(renderRequest, renderResponse);
}

So this sets the service as an attribute in the render request. On the JSP side, it would be able to get the service via the code shared above.

Setting Services in a Portlet You Do Not Control

So you may need to build a JSP fragment bundle to override JSP code, and in your override you need to add a service which was not injected by the core portlet.  It would be kind of overkill to override the portlet just to inject missing services.

So how can you inject the services you need? A portlet filter implementation!

Portlet filters are similar to the old servlet filters, they are used to wrap the invocation of an underlying portlet. And, like servlet filters, can make adjustments to requests/responses on the way into the portlet as well as on the way out.

So we can build a portlet filter component and inject our service reference that way...

@Component(
  immediate = true,
  property = "javax.portlet.name=com_liferay_dictionary_web_portlet_DictionaryPortlet",
  service = PortletFilter.class
)
public class TrashHelperPortletFilter implements RenderFilter {

  @Override
  public void doFilter(RenderRequest renderRequest, RenderResponse renderResponse,
      FilterChain filterChain) throws IOException, PortletException {

    filterChain.doFilter(renderRequest, renderResponse);

    renderRequest.setAttribute(TrashHelper.class.getName(), _trashHelper);
  }

  @Reference(unbind = "-")
  protected void setTrashHelper(TrashHelper trashHelper) {
    this._trashHelper = trashHelper;
  }

  private TrashHelper _trashHelper;
}

So this portlet filter is configured to bind to the Dictionary portlet. It will be invoked at each portlet render since it implements a RenderFilter. The implementation calls through to the filter chain to invoke the portlet, but on the way out it adds the helper service to the request attributes.

Conclusion

So we've seen how we can use OSGi services in the JSP files indirectly via request attribute injection. In portlets we control, we can inject the service directly. For portlets we do not control, we can use a portlet filter to inject the service too.