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; } }
Solved! Go to Solution.
Views
Replies
Total Likes
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
Views
Replies
Total Likes
Views
Replies
Total Likes
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.
Views
Replies
Total Likes
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.
Views
Replies
Total Likes
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
Views
Replies
Total Likes
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.
Views
Replies
Total Likes
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.
Views
Replies
Total Likes
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.
Views
Replies
Total Likes
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.
Views
Replies
Total Likes
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 ). 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.
Views
Replies
Total Likes
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 ;-)
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.
Views
Replies
Total Likes
Views
Likes
Replies