domingo, 13 de abril de 2014

Convert a JAR into Bundle

Personal notes on how to quickly create a bundle based on a list of jars or other dependencies. It uses the content-package and embedded maven configuration to easily deploy the bundle.

Normal and Painful Way
The most difficult way is to follow the standard way and create a manifest file with all the description available. You will also need to figure out the dependencies of your bundle. This blog will show the easy way to package a bundle with all the dependencies that you need.

Step#1: Easy Way

The project is quickly created by the classic mvn archetype. The idea is to follow the structure. 

myprojectlib/depency-pkg-libs

where myprojectlib is the root project and depency-pkg-libs the module with the declared dependencies. We will not include any other code into the folders. Also for the purposes of example, we will use the JUnit Sling Testing on the server side. This bundle is not included in the default distribution and requires JUnit as a dependency.

Step #2: Create the First module

mvn archetype:generate

Make sure you create a basic maven repository with the pom project as root.

In our case the  myprojectlib will look like:

<packaging>pom</packaging>

Step#3: Create the  Content-package
Create a package depency-pkg-libs inside the root directory. Make sure you change the packaging so that it becomes content-package

<packaging>content-package</packaging>

Step#4: Add Embedded List
To your list of build plugins, add the content-package-maven plugin

<groupId>com.day.jcr.vault</groupId>
<artifactId>content-package-maven-plugin</artifactId>

In the list of embedded element add those dependencies that you need. In my case, the junit.remote:

<embeddeds>
  <embedded>
     <groupId>org.apache.sling</groupId>
      <artifactId>org.apache.sling.junit.remote</artifactId>
     <target>${package.root}/install</target>
  </embedded>
</embeddeds>

You should also add the same artifacts listed in embedded to your list of dependencies. Otherwise those will not be installed.

     <dependency>
          <groupId>org.apache.sling</groupId>
          <artifactId>org.apache.sling.junit.remote</artifactId>
          <version>1.0.6</version>
          <scope>provided</scope>
      </dependency>

Actual Code
You can check the actual code from bitbucket. The meat is in the pom.
References
This blog is based on the following resources.


lunes, 7 de abril de 2014

Retrieving Service from Request

Quick way to retrieve a service from a slingRequest

private <E> E getServiceFormRequest(
final SlingHttpServletRequest request, Class<E> serviceType ){
        final SlingBindings bindings = 

(SlingBindings) request.getAttribute(SlingBindings.class.getName());
        SlingScriptHelper slingScriptHelper = bindings.getSling();
        return slingScriptHelper.getService(serviceType);
    }

domingo, 23 de marzo de 2014

Date Range Query Medicine for CQ AEM

One of the most annoying features of xpath queries is the performance problem of date range queries. QueryBuilder tends to perform badly. You can see Marcel's presentation in AdaptTo if you want to know the details. Thus, when faced with this problem, there are several alternatives:
  • modifying the granularity of the dates,so that you select up to the day and exclude hours/min/secs
  • using a node structure in which the date is saved as the path; for example,
    • yyyy/mm/dd/my_content_node
  • or converting the date property into a long 
I am going to focus on the third one: ways to convert a date property into a long property.

Approach
The basic idea is to leverage the JCR Observation offered in AEM to add a property of type long each time a property of type date is created or modified.

First I created my own namespace (thanks this notes) by just going to

http://<host>:<port>/crx/explorer/nodetypes/index.jsp

I selected my own namespace netval so that there will be no conflicts with existing properties. 

Then I registered my class DatePropertyEventListener that implements the EventListener interface. The class needs to use the @Component annotation. You have to register for the event listener in the @Activate method. Make sure you get the ComponentContext in the activate method in order to register for the event. 

In my onEvent I used the EventIterator to get the property. I then filtered those properties that are of type date. Conveniently the Property class can return almost any type of value being String, long, Calendar, etc. In my case I needed the value as a long. 

Once I have the value, I can set it in the new property that has netval as the namespace and the name of the property in order to avoid collisions. For example, if the attribute name was activationDate the new attribute would be:

netval:activationDate

Code
The first thing is to register and de-register the event listener in the @Activate and @Deactivate methods. 

A quick look at my DatePropertyEventListener class reveals the following. The observationManager and admin session are class attributes. They will be used in the @Deactivate  nd Activate methods. It is in the @Activate method where we register the event listener. 

observationManager = adminSession.getWorkspace().getObservationManager();
observationManager.addEventListener(this,
Event.PROPERTY_ADDED|Event.PROPERTY_CHANGED,
                "/content/news/", // absolute path
                true, // isDeep
                null, // uuid
                null, // nodeTypeName
                true // no local
        );

Note that the absolute path is restrictive to the root path of your content. I highly recommend being as specific as possible in order not to receive too much noise.

The onEvent it is easy to get the date property

        while( eventIterator.hasNext() ){
            final Event event = eventIterator.nextEvent();
            if(PropertyType.DATE == getPropertyType(event) ){

Caveats and Warnings
This procedure should be disabled for operations that modify many nodes, like during a migration of content.

A suggestion is to have a producer-consumer data structure (like LinkedBlockedQueue) that will allow you to process the events more efficiently.