Goal
This post is about creating a sample OSGI bundle (what is an OSGI bundle? Any search engine will bring you loads of information) from scratch on IntelliJ IDEA 12. Read this good article on CQ listeners
Here we create a bundle with
1) Sample JCR Observation Event Listener
2) When a new page is created, listener automatically creates a new tag with page name and assigns it to the page. So basically some sample code to create and assign tags.
Install
Setup Maven
1) To add the dependency jars to your IDE, install and setup Maven
2) Download Maven and extract it to C:\dev\apache-maven-3.1.0.
3) Open System Environment Variables and add the System Varilable M2_HOME=C:\dev\apache-maven-3.1.0
4) Add the User Variable M2=%M2_HOME%\bin
New IDEA Module
1) In your IDEA project, Select File -> New Module -> Maven Module and enter the module name autotag
2) Enter the following
GroupId: com.mysample.autotag
ArtifactId: autotag
Version: 1.0
3) Check "Create from archetype" and click "Add archetype"
4) Enter the following information and click ok
GroupdId: com.day.jcr.vault
ArtifactId: multimodule-content-package-archetype
Version: 1.0.2
Repository: http://repo.adobe.com/nexus/content/groups/public
5) Add the following maven properties
appsFolderName - autotag
artifactName - Auto Tag Listener
packageGroup - Test Company
6) Here is a screen shot with all the details entered. Click Finish
7) Here is my autotag module in IDEA
8) Maven dependencies were already imported and created on file system. For example, the dependency "org.osgi"
On file system, the jars are available in
9) Lets delete the unnecessary source files created by archetype; later we'll add our listener code. At the time of this writing i deleted files under "C:\dev\code\projects\autotag\bundle\src\main\java\com\mysample\autotag". Here is my module now with "C:\dev\code\projects\autotag\bundle\src\main\java" added as module source
Sample Listener Logic
1) Lets create a folder listeners and add some sample listener code. Here we are adding a JCR observation listener. This listener operates at JCR level and so has no knowledge of sling web framework, we operate on the raw jcr properties.
2) Here is the listener source code. The annotation "@Reference" for "repository" makes the jcr repository available for querying nodes. "activate" and "deactivate" methods are self-explanatory.
Line 31: we are registering the listener for "NODE_ADDED" events only. So when a new page is created, a node is added in JCR under "/content"; the NODE_ADDED event fires and this listener catches execution.
package com.mysample.autotag.listeners; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Reference; import org.apache.sling.jcr.api.SlingRepository; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.observation.Event; import javax.jcr.observation.EventIterator; import javax.jcr.observation.EventListener; import javax.jcr.observation.ObservationManager; @Component public class AutoTagListener implements EventListener { private final Logger LOGGER = LoggerFactory.getLogger(AutoTagListener.class); @Reference private SlingRepository repository; private Session session; private ObservationManager observationManager; protected void activate(ComponentContext context) throws Exception { session = repository.loginAdministrative(null); observationManager = session.getWorkspace().getObservationManager(); observationManager.addEventListener(this, Event.NODE_ADDED, "/", true, null, null, true); LOGGER.info("Added JCR event listener - AutoTagListener"); } protected void deactivate(ComponentContext componentContext) { try { if (observationManager != null) { observationManager.removeEventListener(this); LOGGER.info("Removed JCR event listener - AutoTagListener"); } } catch (RepositoryException re) { LOGGER.error("Error removing the JCR event listener - AutoTagListener", re); } finally { if (session != null) { session.logout(); session = null; } } } public void onEvent(EventIterator it) { try { }catch (Exception e) { LOGGER.error(e.getMessage(), e); } return; } }
Install and Test Bundle
1) In your IDEA click on "Maven Projects" on the right side of your screen
2) Under Profiles -> Check "autoInstallBundle", "autoInstallPackage". Under "Reactor Project" right click on "install' and "Run Maven Build". Make sure your AEM (CQ) instance is up and running before you attempt this.
3) If the install is successful, in CRX Lite you can see the installed component bundle
4) Log "C:\dev\code\install\author\crx-quickstart\logs\error.log" shows the autotag bundle installed
5) Bundles console "http://localhost:4502/system/console/bundles", confirms the bundle installation
The Real Listener
1) Lets add some useful logic in the listener. The logic we are going to add creates a tag with page name in CRX and assigns it to the page
2) Add the following dependency to C:\dev\code\projects\autotag\pom.xml
2) Add the following dependency to C:\dev\code\projects\autotag\pom.xml
com.day.cq.tagging cq-tagging 5.6.2
3) Add the following dependency to C:\dev\code\projects\autotag\bundle\pom.xml
com.day.cq.tagging cq-tagging
The jars for necessary dependency are imported to "C:\Users\nalabotu\.m2\repository\com\day\cq\tagging\cq-tagging\5.6.2". If the jar is not available, get it from CRX (/libs/cq/tagging/install/cq-tagging-5.6.2.jar) and place it in the location
2) Add necessary logic to listener
Line 25: Declare a variable for tag namespace. Namespaces are like containers for tags. Tags that are going to be created by this listener are stored in the namespace "mysample"
Line 30, 31: Create a reference to tag manager implementation (a tag manager object for playing with tags)
Line 67, 74: A node added event is fired when a page content node is created. So we get the page content node, page node and continue with tag creation. If the node created is not of our interest (say an audit node) we skip the event processing
Line 85: Check if the namespace exists. A null means the namespace doesn't exist, so create one
Line 90: Create the tag with page name
Line 95: Create the cq:tags property on page content node and set it with the tag created above.
Line 96: Very important, save the changes
Line 25: Declare a variable for tag namespace. Namespaces are like containers for tags. Tags that are going to be created by this listener are stored in the namespace "mysample"
Line 30, 31: Create a reference to tag manager implementation (a tag manager object for playing with tags)
Line 67, 74: A node added event is fired when a page content node is created. So we get the page content node, page node and continue with tag creation. If the node created is not of our interest (say an audit node) we skip the event processing
Line 85: Check if the namespace exists. A null means the namespace doesn't exist, so create one
Line 90: Create the tag with page name
Line 95: Create the cq:tags property on page content node and set it with the tag created above.
Line 96: Very important, save the changes
package com.mysample.autotag.listeners; import com.day.cq.tagging.JcrTagManagerFactory; import com.day.cq.tagging.Tag; import com.day.cq.tagging.TagManager; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Reference; import org.apache.sling.jcr.api.SlingRepository; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.observation.Event; import javax.jcr.observation.EventIterator; import javax.jcr.observation.EventListener; import javax.jcr.observation.ObservationManager; @Component public class AutoTagListener implements EventListener { private final Logger LOGGER = LoggerFactory.getLogger(AutoTagListener.class); private static final String NAMESPACE = "/etc/tags/mysample"; @Reference private SlingRepository repository; @Reference JcrTagManagerFactory tmf; private Session session; private ObservationManager observationManager; protected void activate(ComponentContext context) throws Exception { session = repository.loginAdministrative(null); observationManager = session.getWorkspace().getObservationManager(); observationManager.addEventListener(this, Event.NODE_ADDED, "/", true, null, null, true); LOGGER.info("Added JCR event listener - AutoTagListener"); } protected void deactivate(ComponentContext componentContext) { try { if (observationManager != null) { observationManager.removeEventListener(this); LOGGER.info("Removed JCR event listener - AutoTagListener"); } } catch (RepositoryException re) { LOGGER.error("Error removing the JCR event listener - AutoTagListener", re); } finally { if (session != null) { session.logout(); session = null; } } } public void onEvent(EventIterator it) { try { while (it.hasNext()) { Event event = it.nextEvent(); LOGGER.info("AutoTagListener - new add event: ", event.getPath()); Node pageContentNode = session.getNode(event.getPath()); if( ( pageContentNode == null ) || !pageContentNode.getPrimaryNodeType().isNodeType("cq:PageContent")){ LOGGER.debug("Skip processing node: " + event.getPath()); return; } Node pageNode = pageContentNode.getParent(); if( ( pageNode == null ) || !pageNode.getPrimaryNodeType().isNodeType("cq:Page")){ LOGGER.debug("Skip processing node: " + pageNode); return; } TagManager tMgr = tmf.getTagManager(session); Tag superTag = tMgr.resolve(NAMESPACE); Tag tag = null; if(superTag == null){ tag = tMgr.createTag(NAMESPACE, "My Sample", "My Sample tags", true); LOGGER.info("Tag Name Space created : ", tag.getPath()); } tag = tMgr.createTag(NAMESPACE + "/" + pageNode.getName(), pageNode.getName(), "Auto tag : " + pageNode.getName(), true); String tagArray[] = new String[1]; tagArray[0] = tag.getNamespace().getName() + ":" + tag.getPath().substring(tag.getPath().indexOf(NAMESPACE) + NAMESPACE.length() + 1); pageContentNode.setProperty("cq:tags", tagArray); session.save(); } }catch (Exception e) { LOGGER.error(e.getMessage(), e); } return; } }
3) Reinstall the component (Run Maven Goal "install" )
If you see the following error in error.log
02.09.2013 17:08:21.097 *WARN* [127.0.0.1 [1378159701028] POST /crx/packmgr/service.jsp HTTP/1.1] com.day.jcr.vault.packaging.impl.JcrPackageImpl Refusing to recreate snapshot Test Company/.snapshot:autotag-content:1.0, already exists.
Remove the previously installed bundle from OSGI container (http://localhost:4502/system/console/bundles)
Remove the previously installed component from CRX (http://localhost:4502/crx/de/index.jsp#/apps, Right click on "autotag" and "Delete")
4) Run the maven goal "install"
2) Check the Tagging console (http://localhost:4502/tagging). The tag listener-test should have been created under namespace "My Sample"
If you see the following error in error.log
02.09.2013 17:08:21.097 *WARN* [127.0.0.1 [1378159701028] POST /crx/packmgr/service.jsp HTTP/1.1] com.day.jcr.vault.packaging.impl.JcrPackageImpl Refusing to recreate snapshot Test Company/.snapshot:autotag-content:1.0, already exists.
Remove the previously installed bundle from OSGI container (http://localhost:4502/system/console/bundles)
4) Run the maven goal "install"
Test and Check
1) Create a page "Listener Test" of any template type in siteadmin console (http://localhost:4502/siteadmin)
3) In CRX (http://localhost:4502/crx) check the tags node (/etc/tags/mysample/listener-test)
4) Check the page "Listener Test" properties in CRX
5) Check if tag "listener-test" is assigned to the page by opening Side Kick -> Pages tab -> Page Properties -> Tags/Keywords
6) Check if the tag is assigned by navigating to the page (http://localhost:4502/cf#/content/test-site/listener-test.html) and searching with tags:listener-test in Content Finder Pages tab
Hi Sreekanth,
ReplyDeleteThanks a lot for sharing this. Will try this at my end and post back my queries/doubts.
hi all
ReplyDeletehow to read or retrive tags under/etc/tags/namespace propertice
Hi does this code still work under 6.3.3 having some trouble compiling since its ideal 13 and a couple other revs...
ReplyDeleteThis is exactly what I need for a project so any guidance would be awesome