Monday, May 6, 2013

cq development overview

Create a resource

First, create a resource, typically under /content.
You can use curl, http://localhost:4502/crxde, or http://localhost:4502/libs/wcm/core/content/siteadmin.html.
Let's say you created /content/example/page  node of type cq:Page,  and /content/example/page/jcr:content of type cq:PageContent.

Set type of the resource

Add a property called sling:resourceType for this node: /content/example/page/jcr:content
For example, /content/example/page/jcr:content/sling:resourceType = 'my/base/page'

Define the type

Now, you can define the type, my/base/page,  by creating a scriptlet under /apps/my/base/page/page.jsp  (or /apps/my/base/page/html.jsp . Read this:

Or, define a servlet and register it with my/base/page.
For example,
import org.apache.felix.scr.annotations.*;
@Component(immediate = true, metatype = false)
@Properties({@Property(name = "service.description", value = "my example page"),
        @Property(name = "service.vendor", value = "The Apache Software Foundation"),
        @Property(name = "sling.servlet.resourceTypes", value = {"my/base/page"}),
        @Property(name = "sling.servlet.extensions", value = {"html"}),
        @Property(name = "sling.servlet.methods", value = {"GET"})
public class BasePageServlet extends SlingAllMethodsServlet {

 (read:  and  )

Make requests to the resource

Now, you can do GET /content/example/page.html .
The request will be handled by /apps/my/base/page/page.jsp  (if you used scriptlet)  or BasePageServlet (if you used provided an OSGi component).


You create resources. Tag each resource with sling:resourceType, which defines how the resource responds to HTTP requests.

CQ components behave the same way.
Normally, a CQ page is made of many components (resources):

Each resource has type. If you're using scriptlet, you can use <cq:include> or <sling:include> (read: to include those child nodes.

Thursday, April 5, 2012

5.5 upgrade resets author password to author.

If you are upgrading to CQ 5.5, make sure you change passwords to all users.

Otherwise, you'll open up your author instance to the public.
For example, you can login here as author:author

Wednesday, December 14, 2011

escaping xpath query

node name in xpath query cannot start with numbers:

So, use org.apache.jackrabbit.util.ISO9075 as shown in:

Example xpath servlet (/apps/sandbox/xpath/xpath.jsp):

runs xpath
contentType="text/plain; charset=UTF-8"
%><%@include file="/libs/foundation/global.jsp"%><%!
private static Pattern XPATH = Pattern.compile("^/jcr:root(/.+)//(.+)$");
public static String escapeXpath(String xpath) {
    final Matcher matcher = XPATH.matcher(xpath);
    if (matcher.find()) {
        final String path = ISO9075.encodePath(;
        final String props =;
        return String.format("/jcr:root%s//%s", path, props);
    return xpath;
final String xpathOriginal = slingRequest.getParameter("xpath");
final String xpath = escapeXpath(xpathOriginal);
final boolean isXpathEscaped = !xpath.equals(xpathOriginal);

final String displayParam = slingRequest.getParameter("display");
final boolean display = "false".equals(displayParam) ? false : true;//by default, iterates query result.

final QueryManager queryManager = resourceResolver.adaptTo(Session.class).getWorkspace().getQueryManager();
final Query query = queryManager.createQuery(xpath, Query.XPATH);

final String limitParam = slingRequest.getParameter("limit");
final long limit = (null != limitParam && !"".equals(limitParam)) ? Long.parseLong(limitParam, 10) : 100;

final String offsetParam = slingRequest.getParameter("offset"); 
final long offset = (null != offsetParam && !"".equals(offsetParam)) ?  Long.parseLong(offsetParam, 10) : -1;
if (offset > 0) {

final long t = System.currentTimeMillis();
final QueryResult result = query.execute();
final NodeIterator iter = result.getNodes();
long count = iter.getSize();

out.append("original xpath:\n");
out.append(xpathOriginal + "\n");
out.append("escaped xpath:\n");
out.append(xpath + "\n");
if (isXpathEscaped) {
    out.append("(xpath is escaped)\n");


if (display) {
    count = 0;
    while (iter.hasNext()) {
        final Node node = iter.nextNode();
        out.append(node.getPath() + "\n");

final long took = System.currentTimeMillis() - t;

queried <%= count %> nodes (offset: <%= offset %>, limit: <%= limit %>) in <%= (took / 1000.0) %> secs.

Friday, December 2, 2011

log4j.xml for crx (logging QueryImpl)

I wanted to see impact of repository size on query performance.
You can configure crx logging to log query times.

    <appender name="query" class="org.apache.log4j.RollingFileAppender">
        <param name="File" value="crx-quickstart/logs/crx/query.log"/>
        <param name="maxFileSize" value="10MB"/>
        <param name="maxBackupIndex" value="100"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{dd.MM.yyyy HH:mm:ss} *%-5p* %c{1}: %m (%F, line %L)%n"/>
    <logger name="org.apache.jackrabbit.core.query.QueryImpl" additivity="false">
        <level value="debug"/>
        <appender-ref ref="query" />

it'll be logged to cq/crx-quickstart/logs/crx/query.log
 additivity="false" will skip console logging (if root logger has console logger).

Example log:
02.12.2011 14:46:37 *DEBUG* QueryImpl: executed in 0.01 s. (/jcr:root/content/dam//*[(@jcr:primaryType='dam:AssetContent' and jcr:contains(metadata/@dc:format, 'image'))] order by @jcr:lastModified descending) (, line 143)

Wednesday, November 30, 2011

/etc/workflow/launcher/config is flat

I wanted to create various launchers for this project.

So, I started to create launcher nodes:
/etc/workflow/launcher/proj1 is sling:Folder

This does not work. expects /etc/workflow/launcher/config  to be flat. Every child node of the node should have eventType property. Obviously, the sling:Folder, proj1 didn't have.

That broke ENTIRE WORKFLOW LAUNCHER. No workflow was launched.

Make it flat.

Tuesday, October 18, 2011

event handling and dispatcher flush

So, I want to configure CQ like the diagram.
  • a: author instance
  • p1, p2, .... : publish instances
  • d1, d2, ... : dispatchers
  • lb: load balancer
black arrow is replication and flush direction.
red arrow is request handler delegation.

Author will only replicate to publish instances. Publish instances may be clustered.
Dispatcher is not used for load balancing, but as a simple cache (reverse proxy).

And,  I want to disable auto invalidation of dispatcher cache. And, I want to programmically select a handful of affected dispatcher cache whenever there's update to publish instance.

The main reason for this is because auto-invalidation of dispatcher cache is URL path level based (I can only invalidate cache matching /glob under certain path level).

If your application and repository is laid out with dispatcher in mind, you would not have to programmically calculate dispatcher cache to flush. But, my repository is not structured with HTTP in mind (probably wrong usage of Sling and JCR). So, I need to hand pick dispatcher paths to flush whenever there's an update to publish instance's repository.

Also, my author instance calls Replicator.replicate() in various places (for example, when DAM rendition is modified, author instance automatically replicates the rendition to publish instance).

So, publish instances need to listen to events and when things are changed, it should flush relevant dispatcher cache.

Events to listen can be determined by looking at:
where localhost:4503 is one of publish instances.

I looked at the event logs after activating a page and activating a rendition with On Modification trigger on the flush agent set and unset.

Relevant events are:
(various constants are documented in javadoc: )


@Component(immediate = true,
        enabled = true
@Service(value = {EventHandler.class, JobProcessor.class})
@Property(name = EventConstants.EVENT_TOPIC, propertyPrivate = true, value = {
        SlingConstants.TOPIC_RESOURCE_CHANGED,//this is for rendition edit
        PageEvent.EVENT_TOPIC//this is for page edit
public class DispatcherFlushOnPublish implements EventHandler, JobProcessor {

Saturday, September 24, 2011

vlt prints password

so, you want to use vlt

read -p "password: " -s p
vlt -q rcp -q -b 1000 -t 1 - u "http://user:$p@foo/crx/-/jcr:root/content" "http://user:$p@bar/crx/-/jcr:root/content"

and it prints stuff like
Connecting via JCR remoting to http://user:r34lp455w0rd!!@localhost:4502/crx/server

I could not find a way to disable that. so,
vlt -q rcp -q -b 1000 -t 1 - u "http://user:$p@foo/crx/-/jcr:root/content" "http://user:$p@bar/crx/-/jcr:root/content" |grep -v "http://" >> your.log