I started using Adobe Experience Manager (CQ 5.6.1) with a focus on component development and building OSGi services and I strongly believe that learning how to leverage AEM’s capabilities (as well as it’s underlying technologies like Apache Sling) are key to a successful CMS implementation.
With that in mind, I’ve been keeping a list of useful tips and tricks that I’d like to share with you. These are mostly about increasing productivity when working with AEM or just general things I wish I knew about earlier. This post is targeted more at developers starting out with AEM but I’m also hoping more seasoned users can benefit from it too.
With that in mind, I’ve been keeping a list of useful tips and tricks that I’d like to share with you. These are mostly about increasing productivity when working with AEM or just general things I wish I knew about earlier. This post is targeted more at developers starting out with AEM but I’m also hoping more seasoned users can benefit from it too.
?debug=layout
Most people know about the
Here’s the result of applying this property to the Geometrixx sample website that comes with AEM:
wcmmode
query parameter, that allows you to switch between the different page viewing modes (editing, preview, design or disabled), but did you know about debug=layout
? This query parameter can be added to any page you’re viewing (author or preview mode) in order for AEM to display properties of the components on the page. This can be extremely useful when trying to find out what components are making up a page. It sure beats inspecting the DOM or looking through JCR nodes in CRXDE.Here’s the result of applying this property to the Geometrixx sample website that comes with AEM:
Injecting content from other pages
The cq:include tag provides developers with the ability to “hardcode” components on a page instead of leveraging the parsys component – nothing new here. However, did you know the cq:include tag can be used to inject content from other pages? For instance you can inject a product description on the homepage of your site by using the following code:
/apps/geometrixx/components/homepage/content.jsp
[code language=”java”]
<%@include file="/libs/foundation/global.jsp" %>
<%@include file="/libs/foundation/global.jsp" %>
<div id="main">
<div class="grid_4 right_container">
<!– Injecting external content here –>
<cq:include path="/content/geometrixx/en/products/square/overview/jcr:content/par/text" resourceType="foundation/components/text"/>
<cq:include path="r<ightpar" resourceType="foundation/components/parsys"/>
</div>
<!– Injecting external content here –>
<cq:include path="/content/geometrixx/en/products/square/overview/jcr:content/par/text" resourceType="foundation/components/text"/>
<cq:include path="r<ightpar" resourceType="foundation/components/parsys"/>
</div>
</div>
[/code]
In this example, I’m injecting the “square” product description on the sidebar of the homepage but you can just as easily inject an entire
parsys
like so:
/apps/geometrixx/components/homepage/content.jsp
[code language=”java”]
<%@include file="/libs/foundation/global.jsp" %>
<%@include file="/libs/foundation/global.jsp" %>
<div id="main">
<div class="grid_4 right_container">
<!– Injecting external content here –>
<cq:include path="/content/geometrixx/en/products/square/overview/jcr:content/par" resourceType="foundation/components/parsys"/>
<cq:include path="rightpar" resourceType="foundation/components/parsys"/>
</div>
<!– Injecting external content here –>
<cq:include path="/content/geometrixx/en/products/square/overview/jcr:content/par" resourceType="foundation/components/parsys"/>
<cq:include path="rightpar" resourceType="foundation/components/parsys"/>
</div>
</div>
[/code]
This might look quite limiting at first but you have to realise that you can place any valid expression in the
path
property and programmatically generate the path to the content to inject. AEM actually has a built-in component with this exact functionality called the Reference component but it’s not really mentioned anywhere.Reloading ClientLibs
Have you ever been in a situation where you’ve made a change to one of your ClientLibs but that change is not appearing after a browser refresh? Perhaps you even tried clearing the browser cache and still, your change is not there? This might be because AEM has not picked up your change and so hasn’t had the opportunity to invalidate the internal design cache for the particular entry that you modified.
This is what is meant to happen when you make a change to a ClientLib:
[code language=”java”]
*INFO* [pool-5-thread-4] com.day.cq.wcm.core.impl.designer.DesignCacheImpl
Detecting design change. invalidating cache.
*INFO* [pool-5-thread-4] com.day.cq.wcm.core.impl.designer.DesignCacheImpl
invalidated /etc/designs/geometrixx
*INFO* [pool-5-thread-2] com.day.cq.widget.impl.LibraryCacheImpl Invalidating
library /etc/designs/geometrixx/clientlibs
*INFO* [pool-5-thread-2] com.day.cq.widget.impl.HtmlLibraryManagerImpl
detected [JS, CSS] library: /etc/designs/geometrixx/clientlibs, sourced from 5 files.
*INFO* [pool-5-thread-2] com.day.cq.widget.impl.LibraryCacheImpl
Rebuilt 1238 ancestor paths
[/code]
*INFO* [pool-5-thread-4] com.day.cq.wcm.core.impl.designer.DesignCacheImpl
Detecting design change. invalidating cache.
*INFO* [pool-5-thread-4] com.day.cq.wcm.core.impl.designer.DesignCacheImpl
invalidated /etc/designs/geometrixx
*INFO* [pool-5-thread-2] com.day.cq.widget.impl.LibraryCacheImpl Invalidating
library /etc/designs/geometrixx/clientlibs
*INFO* [pool-5-thread-2] com.day.cq.widget.impl.HtmlLibraryManagerImpl
detected [JS, CSS] library: /etc/designs/geometrixx/clientlibs, sourced from 5 files.
*INFO* [pool-5-thread-2] com.day.cq.widget.impl.LibraryCacheImpl
Rebuilt 1238 ancestor paths
[/code]
Note the cache getting invalidated. The next time you access a page that includes this clientLib, the design will be rebuilt:
[code language=”java”]
*INFO* [0:0:0:0:0:0:0:1 [1442733510496] GET /content/geometrixx/en.html
HTTP/1.1] com.day.cq.wcm.core.impl.designer.SystemDesign Initialized
system design at /etc/designs/geometrixx in 12ms
*INFO* [0:0:0:0:0:0:0:1 [1442733510649] GET /etc/designs/geometrixx/clientlibs.css
HTTP/1.1] com.day.cq.widget.impl.HtmlLibraryManagerImpl Start building CSS library:
/etc/designs/geometrixx/clientlibs
*INFO* [0:0:0:0:0:0:0:1 [1442733510649] GET /etc/designs/geometrixx/clientlibs.css
HTTP/1.1] com.day.cq.widget.impl.HtmlLibraryManagerImpl finished building library
/etc/designs/geometrixx/clientlibs.css
*INFO* [0:0:0:0:0:0:0:1 [1442733510650] GET /etc/designs/geometrixx/clientlibs.js
HTTP/1.1] com.day.cq.widget.impl.HtmlLibraryManagerImpl Start building JS library:
/etc/designs/geometrixx/clientlibs
*INFO* [0:0:0:0:0:0:0:1 [1442733510650] GET /etc/designs/geometrixx/clientlibs.js
HTTP/1.1] com.day.cq.widget.impl.HtmlLibraryManagerImpl finished building library
/etc/designs/geometrixx/clientlibs.js
[/code]
*INFO* [0:0:0:0:0:0:0:1 [1442733510496] GET /content/geometrixx/en.html
HTTP/1.1] com.day.cq.wcm.core.impl.designer.SystemDesign Initialized
system design at /etc/designs/geometrixx in 12ms
*INFO* [0:0:0:0:0:0:0:1 [1442733510649] GET /etc/designs/geometrixx/clientlibs.css
HTTP/1.1] com.day.cq.widget.impl.HtmlLibraryManagerImpl Start building CSS library:
/etc/designs/geometrixx/clientlibs
*INFO* [0:0:0:0:0:0:0:1 [1442733510649] GET /etc/designs/geometrixx/clientlibs.css
HTTP/1.1] com.day.cq.widget.impl.HtmlLibraryManagerImpl finished building library
/etc/designs/geometrixx/clientlibs.css
*INFO* [0:0:0:0:0:0:0:1 [1442733510650] GET /etc/designs/geometrixx/clientlibs.js
HTTP/1.1] com.day.cq.widget.impl.HtmlLibraryManagerImpl Start building JS library:
/etc/designs/geometrixx/clientlibs
*INFO* [0:0:0:0:0:0:0:1 [1442733510650] GET /etc/designs/geometrixx/clientlibs.js
HTTP/1.1] com.day.cq.widget.impl.HtmlLibraryManagerImpl finished building library
/etc/designs/geometrixx/clientlibs.js
[/code]
If none of this happens, you can force a refresh of your ClientLibs by restarting the
com.day.cq.widget.impl.HtmlLibraryManagerImpl
service. You can do this by going to http://localhost:4502/system/console/components and once you’ve located the service, restart it. You should now be able to see your change.
There is another way of doing this which will be covered later.
Server Logs
Personally, I prefer “tailing” logs from the command line but for those of you that prefer a GUI approach to viewing server logs, AEM’s got you covered.
From CRXDE Lite
Server logs can be viewed from CRXDE by going to the Console tab on the bottom panel and enabling the display of new messages (click the stop sign).
Server logs can be viewed from CRXDE by going to the Console tab on the bottom panel and enabling the display of new messages (click the stop sign).
Please note that log viewing from CRXDE may be disabled by stopping the “Adobe CRXDE Support” bundle via the Bundles console. If this has been done, you’ll see the following error message when attempting to display new messages: “Could not retrieve log messages. Please check that CRXDE support is installed properly!”.
From Admin Console
Navigating to http://localhost:4502/system/console/status-slinglogs will show you a concatenation of logs from request.log, access.log, upgrade.log, history.log, audit.log, error.log, etc. I’m not a big fan of this approach because logs are not tailed and it’s quite cumbersome to search through.
Navigating to http://localhost:4502/system/console/status-slinglogs will show you a concatenation of logs from request.log, access.log, upgrade.log, history.log, audit.log, error.log, etc. I’m not a big fan of this approach because logs are not tailed and it’s quite cumbersome to search through.
Debug Mode (the simple way)
Once you start developing OSGi services, you quickly learn how to setup AEM to run in debug mode so that you can set breakpoints remotely and step through the execution of your code. I’m willing to bet you’re currently running AEM in debug mode using something similar to this:
[code language=”java”]
java -Xdebug -Xrunjdwp:transport=dt_socket,address=58242,suspend=n,
server=y -Xmx1024m -XX:MaxPermSize=256M -jar cq6-author-p4502.jar
[/code]
java -Xdebug -Xrunjdwp:transport=dt_socket,address=58242,suspend=n,
server=y -Xmx1024m -XX:MaxPermSize=256M -jar cq6-author-p4502.jar
[/code]
There is actually a much simpler way of doing it and it’s a lot easier to remember:
[code language=”java”]
java -jar cq6-author-p4502.jar -debug 58242
[/code]
java -jar cq6-author-p4502.jar -debug 58242
[/code]
Pretty neat, right?
Dependency Finder
The Dependency Finder is used to figure out which OSGi bundles are exporting the package we’re looking for and get the corresponding Maven dependency for our POM. For instance, if I’d like to make use of Sling Models in my AEM Maven project, all I have to do is navigate to the Dependency Finder at http://localhost:4502/system/console/depfinder and look up the following package: org.apache.sling.models.annotations.
The only thing remaining to do now is to copy the value in “Maven Dependency” and place it in our POM.
Hiding the Content Finder
This is probably one of the most simple tips for this post. By default, when you view a page on an author instance, you’ll see a sidebar on the left hand side which allows you to search for content in the DAM (among other things). You can prevent this sidebar from appearing by removing the
cf#
part of the URL.
After removing the cf# portion of the URL:
Dumplibs Utility
AEM comes with a very useful utility for querying the state of ClientLibs, which in my experience can get easily out of control. As a project grows, it can become quite difficult to keep track of the different categories available and what dependencies exist between them. The various features of the “dumplibs” utility take the guess work out of all this.
You can access this utility at http://localhost:4502/libs/granite/ui/content/dumplibs.html
Test category resolution | Enter a category name and retrieve the ClientLib paths that correspond to it. |
Libraries by path | A list of all clientlib paths, their types, categories and dependencies, etc |
Libraries by category | A list of all client lib categories and the corresponding ClientLib paths. |
Libraries by channel | A list of all ClientLibs by channel (extjs, ie6, etc) |
Output testing | Perform a category resolution and find out exactly what scripts/css will be injected onto your page. |
(extra) Invalidating cache and rebuilding libraries | Handy way to perform cache invalidation and rebuild client libraries (this complements the “Reloading ClientLibs” segment mentioned earlier) |
The Output testing is by far the coolest feature so I’ll show you exactly what it does. Let’s say I want to know what scripts will be injected on my page by using the “cq.jquery” ClientLib, I can go to the output test at http://localhost:4502/libs/granite/ui/content/dumplibs.test.html and enter “cq.jquery”. Here’s the result:
As you can see from the screenshot, the output test has resolved the ClientLib category and displayed each file that will be included as part of this category. This is extremely useful to debug issues with ordering of scripts or simply to check that a ClientLib is behaving as expected.
That’s a wrap…
Let me know in the comments if you’ve enjoyed this post or feel free to share your own tips and tricks. I’ll aim to release a part 2 with more advanced concepts like leveraging the Sling Resource Resolving engine, passing parameters to options rendered by a servlet, interacting with the Sidekick programmatically, etc.
No comments:
Post a Comment
If you have any doubts or questions, please let us know.