I have several portlets which each use an aggregation of pooled CSS and JS files within a single web application. Currently each portlet will add appropriate head tags in doHeaders(). However, this causes duplicate tags within the head when more than one portlet is on the same page.
Currently the portlets are deployed on eXo which runs on GateIn. eXo has it's own JS AMD framework and portlet skin system, but we add head elements using doHeaders() to be as platform-agnostic as possible for risk mitigation.
Head elements are added in the following manner:
@Override
public void doHeaders(RenderRequest request, RenderResponse response)
{
Element element = response.createElement("link");
element.setAttribute("type", "text/css");
element.setAttribute("rel", "stylesheet");
element.setAttribute("href", request.getContextPath() + "/service/resource/themes/stylesheet.css");
response.addProperty(MimeResponse.MARKUP_HEAD_ELEMENT, element);
}
I need to remove duplicate entries from the head or prevent duplicates from being written in the first place.
I am attempting to write a common RenderFilter which could strip duplicate head elements. But I cannot seem to get access to the current Element properties from the RenderResponse; I can only setProperty() or addProperty().
I could also write the RenderFilter to replace each individual portlet's doHeaders() method and add the entire CSS and JS pool to the head. But I cannot ensure that this logic would run only per user session page render.
A solution I've currently implemented is a RenderFilter which writes the entire JS and CSS resource pool to the head if the pool is not already written. This is somewhat sub optimal as detecting pending head elements is not something that I've found can be done in a platform-independent manner. However, deciding what to do with redundant head elements is completely up to platform delegation, as JSR-286 does not dictate what should be done in this case.
Through use of eXo's PortalRequestContext, it's possible to obtain the list of pending head elements. By adding a meta element to identify the resource pool, the RenderFilter can then decide if the pool needs to be written or if it already has been written.
Here is the basic detection logic:
boolean addHeaderElements = true;
if (Util.getPortalRequestContext() != null && Util.getPortalRequestContext().getExtraMarkupHeaders() != null)
{
for (Element markupHeaderElement : Util.getPortalRequestContext().getExtraMarkupHeaders())
{
if (markupHeaderElement.getTagName().equalsIgnoreCase("meta") &&
markupHeaderElement.getAttribute("name") != null &&
markupHeaderElement.getAttribute("name").equalsIgnoreCase("project-name"))
{
addHeaderElements = false;
break;
}
}
}
This could also be written to operate on a file basis, but my portlets' resource pool is commonly shared to the point that an all-or-nothing approach can be used.