Expand my Community achievements bar.

Enhance your AEM Assets & Boost Your Development: [AEM Gems | June 19, 2024] Improving the Developer Experience with New APIs and Events
SOLVED

Using Sling in a tag file

Avatar

Former Community Member

I've been doing some experimenting using tag libraries. I started out following a classic approach by implementing my tag library as a Java class. Everything works fine but understanding how everything fits together is somewhat complicated. I decided to give tag *files* a try, mainly because one of the reasons why some people appear to prefer them is that they are simpler to implement and understand (i.e. they are just a special type of JSP).

I am actually using JSP Documents (JSPX) and as a consequence tagx files, but thats mostly incidental.

Again I have everything working BUT, my tag file (and library) make a call to an OSGi service and to get a reference to the service instance I am using :-

sling.getService(myservice_interface.class)

I don't appear to be able to resolve a reference to sling in my tag file at least not by simply including a taglib directive in my file (or rather the tagx equivalent XML namespace declaration :-

<jsp:root xmlns="http://www.w3.org/1999/xhtml" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:sling="http://sling.apache.org/taglibs/sling/1.0" version="2.1">

If I subsequently try and use sling.getService(...) I just get a 'cannot resolve sling' exception.

I also tried using various import directives, but couldn't find one that had any [positive] effect. E.g. :-

<jsp:directive.tag import="org.apache.sling.xxx"/>

I *have* been able to get it to work by PASSING a reference to sling as an attribute FROM the calling JSP :-

JSP

<h1><aviva-aem:helloWorld name="AEM User" sling="${sling}"/></h1>

mytagfile.tagx

 

<jsp:directive.attribute name="sling" rtexprvalue="true" required="true" type="org.apache.sling.api.scripting.SlingScriptHelper"/>

The call to sling.getService(...) now operately successfully, ... but it seems a bit klunky ... is there a neater approach ??

Thanks

Fraser.

1 Accepted Solution

Avatar

Correct answer by
Former Community Member

I've made a bit more progress on this, so I'm posting where I am for anyone else who is interested.

As Justin said using <sling:defineObjects/> is important. I had tried it previously and still saw the 'cannot resolve sling' exception in the log, BUT as it turns out this was NOT caused by adding that element but my subsequent use of the sling prefix in a function. The line numbers in exception stack trace were just a bit misleading and the code snippet it showed started with <sling:defineObjects/> so naturally I put 2 and 2 together and can up with 5 !

Here's what my current slingTest.tagx file looks like (I know its a bit naff, but it's just a test to understand behaviour) :-

<?xml version="1.0" encoding="UTF-8"?> <jsp:root xmlns="http://www.w3.org/1999/xhtml" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:sling="http://sling.apache.org/taglibs/sling/1.0" xmlns:c="http://java.sun.com/jsp/jstl/core" version="2.1"> <jsp:directive.tag trimDirectiveWhitespaces="true"/> <jsp:directive.tag import="com.aviva.aem.archetypetest.jspcompile.jspcompile.services.api.IGoodbyeWorldService"/> <jsp:directive.tag import="org.apache.sling.api.scripting.SlingScriptHelper"/> <jsp:directive.attribute name="name" rtexprvalue="true" required="true" type="java.lang.String"/> <sling:defineObjects /> <jsp:declaration> private String getServiceMessage(String username, SlingScriptHelper sling) throws JspException { IGoodbyeWorldService gws = sling.getService(com.aviva.aem.archetypetest.jspcompile.jspcompile.services.api.IGoodbyeWorldService.class); String message = gws.getMessage(username); return message; } </jsp:declaration> <jsp:scriptlet> out.println("Message from service (called from slingTest.tagx): " + getServiceMessage(name, sling)); </jsp:scriptlet> </jsp:root>

There's a few things to note :-

  1. The Sling tag library is referenced by the namespace declaration - xmlns:sling="http://sling.apache.org/taglibs/sling/1.0"
  2. The SlingScriptHelper package is imported -  <jsp:directive.tag import="org.apache.sling.api.scripting.SlingScriptHelper"/>
  3. <sling:defineObjects /> is called
  4. The function expects sling to be passed in - getServiceMessage(String username, SlingScriptHelper sling)
  5. The scriptlet that makes the call passes a sling argument - getServiceMessage(name, sling));

So the culprit for the exception was the sling.getService(...) call inside the function. The key was to pass it as an argument and getting access to the sling reference required <sling:defineObjects/>

There's still room for improvement, but at least this is a step forward from passing sling in as an attribute from the calling JSP.

Fraser.

View solution in original post

5 Replies

Avatar

Employee

Hi Fraser,

It looks like you need to add <sling:defineObjects/> or <cq:defineObjects/> in your tag file.

Justin

Avatar

Former Community Member

Hey Justin,

I'm pretty sure I did include a <sling:defineObjects/> at some point in the tag file and got the same 'cannot resolve sling' exception and the exception trace pointed directly to that line. I'll give I another try tomorrow to be sure and report back.

Thanks for your suggestion

Fraser

Avatar

Correct answer by
Former Community Member

I've made a bit more progress on this, so I'm posting where I am for anyone else who is interested.

As Justin said using <sling:defineObjects/> is important. I had tried it previously and still saw the 'cannot resolve sling' exception in the log, BUT as it turns out this was NOT caused by adding that element but my subsequent use of the sling prefix in a function. The line numbers in exception stack trace were just a bit misleading and the code snippet it showed started with <sling:defineObjects/> so naturally I put 2 and 2 together and can up with 5 !

Here's what my current slingTest.tagx file looks like (I know its a bit naff, but it's just a test to understand behaviour) :-

<?xml version="1.0" encoding="UTF-8"?> <jsp:root xmlns="http://www.w3.org/1999/xhtml" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:sling="http://sling.apache.org/taglibs/sling/1.0" xmlns:c="http://java.sun.com/jsp/jstl/core" version="2.1"> <jsp:directive.tag trimDirectiveWhitespaces="true"/> <jsp:directive.tag import="com.aviva.aem.archetypetest.jspcompile.jspcompile.services.api.IGoodbyeWorldService"/> <jsp:directive.tag import="org.apache.sling.api.scripting.SlingScriptHelper"/> <jsp:directive.attribute name="name" rtexprvalue="true" required="true" type="java.lang.String"/> <sling:defineObjects /> <jsp:declaration> private String getServiceMessage(String username, SlingScriptHelper sling) throws JspException { IGoodbyeWorldService gws = sling.getService(com.aviva.aem.archetypetest.jspcompile.jspcompile.services.api.IGoodbyeWorldService.class); String message = gws.getMessage(username); return message; } </jsp:declaration> <jsp:scriptlet> out.println("Message from service (called from slingTest.tagx): " + getServiceMessage(name, sling)); </jsp:scriptlet> </jsp:root>

There's a few things to note :-

  1. The Sling tag library is referenced by the namespace declaration - xmlns:sling="http://sling.apache.org/taglibs/sling/1.0"
  2. The SlingScriptHelper package is imported -  <jsp:directive.tag import="org.apache.sling.api.scripting.SlingScriptHelper"/>
  3. <sling:defineObjects /> is called
  4. The function expects sling to be passed in - getServiceMessage(String username, SlingScriptHelper sling)
  5. The scriptlet that makes the call passes a sling argument - getServiceMessage(name, sling));

So the culprit for the exception was the sling.getService(...) call inside the function. The key was to pass it as an argument and getting access to the sling reference required <sling:defineObjects/>

There's still room for improvement, but at least this is a step forward from passing sling in as an attribute from the calling JSP.

Fraser.

Avatar

Level 10

Are you following online AEM documentation or KB that needs to be updated? If so -- can you please post the URL of the Adobe online content that you are following here. 

Avatar

Former Community Member

smacdonald2008 wrote...

Are you following online AEM documentation or KB that needs to be updated? If so -- can you please post the URL of the Adobe online content that you are following here. 

 

Hey Scott,

no I'm not following any existing AEM article though it would be useful if there were some that covered custom tag handler (including tag file) development practice. This one also touches on JSP[x], JSTL and the numerous OOTB tag libraries (JSP core, CQ, Sling, et al), and a few others (like Headwire's Squeakysand). There are also important aspect that relate to the development environment and tooling (e.g. various Maven plugins that play in this space).

I am partly responsible for creating our internal software engineering best practice (in this case for AEM), and this is one of the topics in what is becoming a large body of work !

Thanks for your interest

Fraser

The following has evaluated to null or missing: ==> liqladmin("SELECT id, value FROM metrics WHERE id = 'net_accepted_solutions' and user.id = '${acceptedAnswer.author.id}'").data.items [in template "analytics-container" at line 83, column 41] ---- Tip: It's the step after the last dot that caused this error, not those before it. ---- Tip: If the failing expression is known to be legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: #assign answerAuthorNetSolutions = li... [in template "analytics-container" at line 83, column 5] ----