Expand my Community achievements bar.

SOLVED

Squeakysand taglib NPE Calling getService

Avatar

Former Community Member

I am using the CQBlueprints archetype and following through the example implementation of simple tag library processing. The sample class provided is below. Seems pretty straight-forward. The OSGI bundles for the tablib and service are installed and Active. The GoodbyeWorldService bundle exports all its pacakges. The TLD generated is very simple. I can see that the setName method is called correctly from the provided JSP. Everything looks fine. HOWEVER ... as soon as the code hits the

GoodbyeWorldService service = getService(GoodbyeWorldService.class);

call an NPE occurs. :-

Caused by: java.lang.NullPointerException at com.mycompany.aem.archetypetest7.taglib.GoodbyeWorldTag.doTag(GoodbyeWorldTag.java:31)

Comment (3) below suggests that getService is a helper method inherited from the CqSimpleTagSupport class and I assumed would handle instantiation of GoodWorldService

Anyone else come across this, or has any suggestions ?

Kind regards

Fraser.

package com.mycompany.testproject.taglib; import com.cqblueprints.taglib.CqSimpleTagSupport; import com.squeakysand.jsp.tagext.annotations.JspTag; import com.squeakysand.jsp.tagext.annotations.JspTagAttribute; import com.mycompany.testproject.services.GoodbyeWorldService; import java.io.IOException; import javax.servlet.jsp.JspException; /** * Example JSP Custom Tag demonstrating three important concepts: * * 1. use of the SqueakySand annotations for auto-generating the Tag Library Descriptor (.tld) file * * 2. extending the CqSimpleTagSupport class from the CQ Blueprints library that provides many *    useful methods to make writing JSP Custom Tags for the CQ platform easier for developers * * 3. accessing an OSGI service from within a JSP Custom Tag using one of the methods inherited *    from the CqSimpleTagSupport class */ @JspTag public class GoodbyeWorldTag extends CqSimpleTagSupport { private String name; @Override public void doTag() throws JspException, IOException { GoodbyeWorldService service = getService(GoodbyeWorldService.class); String message = service.getMessage(name); getJspWriter().write(message); } public String getName() { return name; } @JspTagAttribute(required = true, rtexprvalue = true) public void setName(String name) { this.name = name; } }
1 Accepted Solution

Avatar

Correct answer by
Employee

Hi Fraser,

I'm not sure if anyone from Headwire monitors these forums. You might want to reach out to them directly for support.

The code to get an OSGi service from a JSP TagLib is this:

final SlingBindings bindings = (SlingBindings) pageContext.getRequest().getAttribute( SlingBindings.class.getName()); final SlingScriptHelper scriptHelper = bindings.getSling(); final GoodbyeWorldService service = scriptHelper.getService(GoodbyeWorldService.class);

I would imagine that this is exactly what is going on inside the getService() method you are using, but I don't know for sure (and Headwire would). If the third line there still produces null, then I would suggest checking that the service is actually availalble. You could check this using the Services tab in the WebConsole.

Also, the code sample you provided implies that GoodbyeWorldService is a class. It is a better coding practice to register OSGi services using interfaces, not concrete classes. So your code should be:

 

package com.myco; public interface GoodbyeWorldService { String getMessage(String n); } package com.myco.impl; @Component @Service public class GoodbyeWorldServiceImpl implements GoodbyeWorldService { public String getMessage(String n) { return n.toUpperCase(); } }

Registering an OSGi service as a concrete class is possible and there are some cases where it is a good idea, but those are few and far between.

HTH

View solution in original post

10 Replies

Avatar

Level 10
Can you let us know the URL of the article that you are following.

Avatar

Former Community Member

Hi Scott,

sure its :-

http://www.cqblueprints.com/setup/maven.html

The CQ Blueprints archetype is the one that our external AEM consultancy partner has recommended that we use. It appears to provide a reasonable separation for the various types of resource that are created. Whats Adobe's view .. would you recommend we use the multimodule-content-package-archetype from the Adobe public repo that creates only a bundle and content structure ?

Fraser.

Avatar

Former Community Member

JUST to get something working I used *new* to create the GoodbyeWorldService instance in the doTag method, that is, I changed this :-

package com.aviva.aem.archetypetest7.taglib; import com.aviva.aem.archetypetest7.services.GoodbyeWorldService; import com.cqblueprints.taglib.CqSimpleTagSupport; import com.squeakysand.jsp.tagext.annotations.JspTag; import com.squeakysand.jsp.tagext.annotations.JspTagAttribute; import java.io.IOException; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; @JspTag public class GoodbyeWorldTag extends CqSimpleTagSupport { private String name; @Override public void doTag() throws JspException, IOException { GoodbyeWorldService service = getService(GoodbyeWorldService.class); String message = service.getMessage(name); getJspWriter().write(message); } ...

to :-

private String name; @Override public void doTag() throws JspException, IOException { //GoodbyeWorldService service = getService(GoodbyeWorldService.class); GoodbyeWorldService service = new GoodbyeWorldService(); String message = service.getMessage(name); getJspWriter().write(message); }

Now the code works, BUT ... I am not that comfortable with just using *new* since I don't know what the helper method getService *actually* does under the covers and it SHOULD work in the same way as the equivalent Sling getService method does.

Any ideas why the call to getService returnes a null pointer ??

Regards

Fraser.

Avatar

Correct answer by
Employee

Hi Fraser,

I'm not sure if anyone from Headwire monitors these forums. You might want to reach out to them directly for support.

The code to get an OSGi service from a JSP TagLib is this:

final SlingBindings bindings = (SlingBindings) pageContext.getRequest().getAttribute( SlingBindings.class.getName()); final SlingScriptHelper scriptHelper = bindings.getSling(); final GoodbyeWorldService service = scriptHelper.getService(GoodbyeWorldService.class);

I would imagine that this is exactly what is going on inside the getService() method you are using, but I don't know for sure (and Headwire would). If the third line there still produces null, then I would suggest checking that the service is actually availalble. You could check this using the Services tab in the WebConsole.

Also, the code sample you provided implies that GoodbyeWorldService is a class. It is a better coding practice to register OSGi services using interfaces, not concrete classes. So your code should be:

 

package com.myco; public interface GoodbyeWorldService { String getMessage(String n); } package com.myco.impl; @Component @Service public class GoodbyeWorldServiceImpl implements GoodbyeWorldService { public String getMessage(String n) { return n.toUpperCase(); } }

Registering an OSGi service as a concrete class is possible and there are some cases where it is a good idea, but those are few and far between.

HTH

Avatar

Former Community Member

Thanks Justin, that was really very helpful. I am now in touch with Headwire and have a working implementation (albeit there is still some way to go to get the archetype that I created based on theirs working properly). I didn't realise they worked out of Southern California (hmmm, I wish ;-)

kind regards

Fraser.

Avatar

Former Community Member

Hey Justin,

Can you suggest some standard or better approach in CQ for writing all components code in JSTL instead of script-lets. I had tried squeakysand api once.

Avatar

Employee

Hi maruthid,

I'm honestly not that familar with what Squeaksand does. From the looks of the original code sample, it is an annotation library for generating JSP TLDs. But when I go to squeaksand.com, there's a lot more there. On my projects, I use https://code.google.com/p/tld-generator/ for TLD generation. You can see a working example of that in https://github.com/Adobe-Consulting-Services/acs-aem-commons/blob/master/bundle/pom.xml.

One interesting note about Squeaksand is that the annotations have a retention of Runtime. This means that the annotations are "burned into" your class files and your code retains a runtime dependency on the Squeaksand library. For annotations used in code generation, I'm more used to seeing Source (which is what the TLD generator annotations I use have). Perhaps there is some reason for retaining the Squeaksand annotations at runtime; I don't know.

Either way, I do think that using annotation to drive TLD file generation is a great idea.

Avatar

Former Community Member

That's a good question. It's hard sometimes to decide between libraries supplied by Adobe or others, so it would be good to understand what Adobe recommend as best practice.

A short update on my issue. After chatting with the Headwire guys I have resolved the NPE issue. This was related to my POM which was missing a crucial instruction for one of the maven plugins which generates metadata about the reference type being used (GoodbyeWorldService in this case being called from the doTag method). I don't have that config section to hand right now but I'll post it back to this thread when I have done a bit more testing.

The Squeakysand libraries support JSTL just fine. But again, it would be helpful to hear what Adobe regard as solid development practice and how they support that in CQ.

HTHs

Fraser.

Avatar

Employee

Hi Fraser,

I can tell you internallly, most TLD files are hand crafted. As I wrote above, I've been using this particular TLD Generation annotation library for my last few projects and it has been successful, but it isn't "standard". The key thing in my opinion, is that consumers of my taglibs shouldn't care how the TLDs were generated - as I said above, that's a purely build-time issue (of course, you can ask how they're generated and I'm happy to tell you, but it shouldn't impact your usage of them).

On the broader question (and this is just me speaking, not Adobe), in my consulting projects, I haven't had a lot of opportunity to work with partner frameworks and libraries (though if any one wants me to review one, just private message me with the bits smiley). From past lives (i.e. before I worked at Adobe and worked with a different set of products, many of which don't exist anymore), I can say that you can get into framework fatigue and you do need to balance the value of the framework with your the ability to read and understand what your code is doing. Personally, I don't mind verbosity if it helps readability, troubleshooting, and maintenance.

There are also long-term support issues to consider. This is one reason why we in Adobe Consulting felt it was criticial that if we were going to publish a library, it had to be 100% open source. This caused it to take longer to get started and there probably could be more code available if we weren't going down that path, but I think it is the right thing to do.

Avatar

Former Community Member

Justin,

thanks for both response. I have read over the second para of your first response a few times (the one that starts '.. One interesting note about Squeaksand ...'). I'm not yet experienced enough to understand the full significance, but I will run that past Headwire and post their response. It *sounds* like you are suggesting a tighter coupling than would be desirable.

Anyway, as an update on the issues that I started this thread with. I now have a working implementation so hopefully its resolved. There were a few things that I had got slightly wrong, mostly due to my lack of experience. Resolving difficulties like this is a great way to learn stuff so I'm never that depressed when things don't work straight away OOTB.

In case anyone else encounters something similar, this is what I did (note: I have a few custom OSGI bundles that export packages from the squeakysand JARs as well as a few other commonly used libraries) :-

1. The critical omission from my parent POM was the <executions> section for the maven-scr-plugin.This is needed to generates metadata that  to bind to the GoodbyeService OSGi service implementation (I think ;-)

<plugin>
    <groupId>org.apache.felix</groupId>
    <artifactId>maven-scr-plugin</artifactId>
    <!-- <version>1.7.4</version> -->
    <version>1.15.0</version>
    <executions>
        <execution>
            <id>generate-scr-descriptor</id>
            <goals>
                <goal>scr</goal>
            </goals>
        </execution>
    <executions>
</plugin>
 
2. In my POM I am using version 1.9.6 of org.apache.felix.scr.annotations

<dependency>
    <groupId>org.apache.felix</groupId>
    <artifactId>org.apache.felix.scr.annotations</artifactId>
    <version>1.9.6</version>
    <scope>provided</scope>
</dependency>
 
This appears to be incompatible with version 1.7.4 of the felix maven-scr-plugin. So, as you can see from the first snippet above I switched to a later version (1.15.0).
 
This also has the consequence of creating a differently named file in the OSGI-INF/metatype folder. In the squeakysand example, the file is just called metatype.xml, whereas when using the later version of the maven-scr-plugin it is called <package-name>.<class-name>.xml, so in my case was : com.aviva.aem.archetypetest7.services.GoodbyeWorldService.xml.
 
The content of the file is exactly the same though.
 

3. I found that I only needed to Embed squeakysand-osgi-0.5.0.jar within the taglib and service bundles (I note the sample also embeds, commons-lang-2.6.jar, jsoup-1.6.21.jar, squeakysand-commons-0.4.0 and squeakysand-jsp-0.4.0.jar in the services bundle only). All of the others are exported by other bundles in our AEM instances. Is that OK or am I likely to trip on something later on ??

Fraser.