Friday, October 18, 2013

Monday, August 19, 2013

Creating custom JCR node type

Sometimes it is required for application to have custom schema with application-specific namespace, node types and properties. Although David's model rule #1 is "Data First, Structure Later. Maybe", sometimes "maybe" becomes "must". So how I can create custom node type?

First of all, read carefully official JCR documentation: Node Types and Examples. ;-) And now let's implement it.

I. Create node type definition using CND descriptor. 

For simplicity we will put it into existing CQ namespace. (If you want to customize namespace, just define it alongside existing namespaces in CND file).

<cq  = 'http://www.day.com/jcr/cq/1.0'>
<sling = 'http://sling.apache.org/jcr/sling/1.0'>

//---- Custom Node Type ----

[cq:CustomNode] > nt:hierarchyNode, mix:title, mix:modified, mix:versionable
    orderable
    - * (undefined) multiple
    - * (undefined)
    + * (nt:base) = cq:CustomNode version

Here we define node type, which is hierarchical, contains jcr:created, jcr:modified properties and which supports versioning (mix:versionable). If your project is built using maven, put this file to the resources folder of your OSGI bundle. I use CQ-style and put it under CQ-INF/nodetypes folder.

II. Create OSGI listener which registers our node type during bundle activation.

Now you need to register newly created definition. For that you should invoke JCR API from the links above. In order to simplify things, we will register our node type during bundle activation, so we need to create an OSGI component.

package com.yvv.customnode.bundle;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.jackrabbit.api.JackrabbitNodeTypeManager;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Session;
import java.io.InputStream;

@Component
public class CustomNodeTypeListener {

    private final Logger LOGGER = LoggerFactory.getLogger(CustomNodeTypeListener.class);
    public static final String NODE_TYPE_NAME = "cq:CustomNode";

    @Reference
    private org.apache.sling.jcr.api.SlingRepository repository;

    private Session session;

    protected void activate(ComponentContext context) throws Exception {
        session = repository.loginAdministrative(null);
        registerCustomNodeTypes(session);
    }

    protected void deactivate(ComponentContext componentContext) {
        if (session != null) {
            session.logout();
            session = null;
        }
    }

    public void registerCustomNodeTypes(Session session)
            throws Exception {
        JackrabbitNodeTypeManager manager = (JackrabbitNodeTypeManager) session.getWorkspace().getNodeTypeManager();
        if (manager.hasNodeType(NODE_TYPE_NAME)) {
            manager.unregisterNodeType(NODE_TYPE_NAME);
        }
        InputStream is = this.getClass().getClassLoader().getResourceAsStream("CQ-INF/nodetypes/cq-customnode.cnd");
        manager.registerNodeTypes(is, JackrabbitNodeTypeManager.TEXT_X_JCR_CND);
    }
}

III. Verify newly created node type.

Browse to http://localhost:4502/crx/explorer/nodetypes/index.jsp and under the tab "All registered node types" find cq:CustomNode, open it to view details.

IV. Create nodes using your custom type.

You can do it using i.e. curl command:
curl -D -u admin:admin -F"jcr:primaryType=cq:CustomNode" -F"title=some title" http://localhost:4502/content/customnode

Tuesday, August 6, 2013

Reporting using Groovy scripts

It's often necessary to get different kinds of statistics on CQ data. You can do it in several ways:
  • using build-in CQ reporting engine . Unfortunately it is pretty inflexible and requires lot's of configuration to be done, so I would recommend using it for simple JC queries only;
  • writing custom components with required logic in ESP/JSP/Java/Groovy
  • use third-party tools like Splunk which parses CQ logs (you should have required data in logs! ;-)  

In this blog post I focus on groovy scripts as one of the most quick ways to operate with CQ API.
To use groovy scripts you can use pretty nice groovy console, which is ultimately installed as a CQ package.

As an example, let's write a simple script which will gather DAM statistics (# of all assets in JCR repository, total DAM storage size, asset name, asset path, asset file size).

First you need to install groovy console according to instructions from https://github.com/Citytechinc/cq-groovy-console
Once installed, navigate to http://localhost:4502/groovyconsole. You should see code area in the center of the screen, Type the following code there:
import javax.jcr.query.*
import com.day.cq.dam.api.*
import javax.jcr.*
import org.apache.sling.api.resource.*

def query = createSQL2Query("/content/dam") // TODO start folder for search
def result = query.execute()
def rows = result.rows

totalSize = 0
rows.each { row ->
    Resource res = resourceResolver.getResource(row.path)
    Asset asset = res.adaptTo(Asset.class)
    fileSize = asset.getOriginal().getSize();
    totalSize += fileSize
//    println "Name=$asset.path; fileSize=$fileSize"
}
println "Total assets number: ${rows.size}; Total file size: ${formatFileSize(totalSize)}"


def createSQL2Query(startPage) {
    def queryManager = session.workspace.queryManager

    def statement = "select * from [nt:base] as p where (isdescendantnode (p, '$startPage')) and p.[jcr:primaryType] = 'dam:Asset'"

    def query = queryManager.createQuery(statement, Query.JCR_SQL2)

    query
}

// Convert a number of bytes to a more readable string
def formatFileSize(size) {
    labels = [ ' bytes', 'KB', 'MB', 'GB', 'TB' ]
    label = labels.find {
        if( size < 1024 ) {
            true
        } else {
            size /= 1024
            false
        }
    }
    "${new java.text.DecimalFormat('0.##').format( size )}$label"
}
After pressing "Run Script" button you will see output below console. You can of course substitute println to SLF4J methods, here it's just for brevity.


Friday, August 2, 2013

CQ Page Editing Tips

Here is the summary of several tips useful during developing your CQ pages (it's an aggregated information from other blogs, just to keep it in one place):

1. Show content finder on the page.
Add /cf# prefix to page's URL. Remove it to hide content finder.

Example: http://localhost:4502/cf#/content/geometrixx/en/products.html


2. Set the mode of your page.
Add parameter wcmmode=(edit|preview|design|disabled)

Example: http://localhost:4502/content/geometrixx/en/products.html?wcmmode=preview


3. Shows details of all the components used on your page.
Add parameter debug=layout


Example: http://localhost:4502/content/geometrixx/en/products.html?debug=layout



4. Run Firebug Lite tool for the web page. 
Add parameter debugConsole=true


Example: http://localhost:4502/content/geometrixx/en/products.html?debugConsole=true


5. Writes Logs all data in your Clientlib category as separate files (to view it you should view HTML-source of the page).
Add parameter debugClientLibs=true



Example: http://localhost:4502/content/geometrixx/en/products.html?debugClientLibs=true





6. View timing information of your page.
Press CTRL+SHIFT+U

Wednesday, July 10, 2013

Organizing CQ project structure

It's pretty obvious that a good project structure can significantly reduce development and maintainable efforts of any software. CQ5 is not an exclusion, that's why it's important to stick to the best practices of organizing your code.

As you might know, CQ advocates moving all application-specific code to the  folder with custom application name. Resources, located in that folder either inherit base CQ items (components, templates, designs, etc.) or are independent resources. CQ applications can contain:

  • business logic implemented in java code and deployed in OSGI bundles, 
  • presentation logic which is embedded into scripts (JSPs, ESPs, Groovy scripts. etc.), 
  • content data (dialog/page/template configuration metadata, design artifacts like js and css, sample content).
Here is the proposal for a typical CQ project structure (screenshots taken from IntelliJ IDEA):
where bundles folder contains all application OSGI bundles, content folder contains scripts and content data, qa-rulesets folder contains code quality tools profiles. As you can see, project is Maven-based with a root pom in project's root folder. 
Let's expand each folder in more detail starting from bundles:
"Bundles"  folder contains one "core" bundle, which includes some common functionality and at least one "components"  bundle which contains presentation logic code for your application components. It can also contain other bundles according to application needs, but aforementioned two are the required minimum.
If you used Maven before you already noted that all bundles have standard structure with src/main/java for java source code, src/test/java for java tests and src/main/resources for required bundle resources, i.e. some configuration. Each bundle contain its own pom.xml which refers to the parent pom.xml in project's root folder.
Let's have a quick look at the core bundle:
You can see that it contains bundle activator, service (in this case configuration service), servlet, unit tests for all these classes and integration tests under "integration" folder. Integration tests are written using Cucumber framework, which we'll cover in detail in a separate blog post.

Now let's switch to "content" folder:
It contains CQ content structure, which advocates putting data under application-specific folder (in this case sampleapp). 
Components folder is further divided into page and modular folders, where page contains page component-related resources (scripts, JCR metadata) and modular contains components resources which can be used on those pages. Idea here is to split all your components into logical units.
Designs folder contains all client artifacts (JS, CSS) organized into several clientlibs. For example you can see clientlib for twitter bootstrap (clientlib-bootstrap) and application-specific clientlib (clientlib-all).
META-INF contains configuration files for CQ vault tool, which is needed during deployment.
Having organized content this way we isolated our application from core CQ logic and from other applications logic, because everything is put under our own application folder sampleapp. If we want to reuse other components we can easily do so with the help of JCR node's inheritance or composition.

Finally qa-rulesets folder contains profiles for code quality tools which are executed during maven build.
Here we use Checkstyle, PMD and FindBugs for local code quality checks. We also have Sonar profile which integrates all previous profiles.

Automating project skeleton creation.

As our project is Maven-based, it is quite natural that we need to create maven archetype which will generate this project structure. For this project I used one of custom archetypes which: 
  • Contains OSGI modules with Slice activator (more on it in next blog posts), includes sample CQ project based on Twitter Bootstrap.
  • Contains profile for executing local SCA analysis (Checkstyle/PMD/FindBugs are used for that) and automatic SCA using Sonar QA platform.
  • Integrates with Cucumber. Has sample mock usage in unit tests using Powermock/Mockito
You can freely use this archetype as a starting point for creating new CQ5-based project.