How to Use Jquery with Grunt

I have recently started working with Grunt, i loathed it first but it has many varied uses and i seem to have started liking it.

I was recently working on a requirement where i had to create a AEM component automatically with dialogs and clientlibs. More on this later, in a later post. While implementing the requirement, i wanted to use Jquery on grunt for some dom manipulation.

Here is how you do it.

var doc = jsdom(grunt.file.read(//path to html file));
var window = doc.parentWindow;
var $ = require(‘jquery’)(window);
var bodyText = $(“body”).html();

So, simple. I was reading Stackoverflow and came across fs.readFile and fs.readSyncFile which are async and sync functions but they were not simply working and hence i had to use grunt.file.read function.

Advertisements

Create Maven Project for CQ5/AEM using archetype 7,8

Before, i started working with AEM 6, i was building CQ5 maven project with archetype version ~1.

Recently, i had to setup another project and i did the below steps to produce a maven project. Please note that archetype 7&8 are compatible with AEM 6.

How to Archetype 8 with MAVEN and CQ5

1) Install Maven and configure it with the environment variables etc.

2) Use the latest archetype available at

https://github.com/Adobe-Marketing-Cloud/aem-project-archetype

3) Currently, the latest archetype version available is 8.

4) Before you create an empty project you have to access if you need additional sub modules inside the project. It is always good to create a parent project and add the modules inside them, even if you don’t need them right away. This enables you to add sub modules inside if needed later on.

5) If you want to create a parent module use the command below.

mvn archetype:generate -DarchetypeGroupId=org.codehaus.mojo.archetypes -DarchetypeArtifactId=pom-root -DarchetypeVersion=RELEASE

This will generate a folder with a pom file where the packaging type will be pom.

6) Now, go into the directory that you created and run the command below to generate maven project for CQ, you can also generate a project using the command below if needed for dev purposes.

mvn -X archetype:generate -DarchetypeGroupId=com.adobe.granite.archetypes -DarchetypeArtifactId=aem-project-archetype -DarchetypeVersion=8 -DarchetypeRepository=adobe-public-releases

Once the project generates, you will see that it gets automatically added to the parent project. Inspect the pom file for changes.

Errors that you might encounter when you create this project

1) content-package-plugin cannot be resolved. I removed the version number and it was downloaded fine from the adobe remote repository.

2) The same can happen for enforcer plugin. Since you can’t browse the maven repository of Adobe on your browser as it is just maven compatible , you will end up experimenting with few versions and plugins.

3) After the project generates, you also need to run mvn clean install and the profile autoInstallPackage. This might generate additional errors. The common one being that it-launcher project will fail every time.  I couldn’t asses if I really needed this project and hence removed it from the pom.xml file module section and removed the directory as well.

Archetype 7 works well without issues and hence is recommended to use. 

Below command will help you generate such a project with AEM and MAVEN and Archetype version 7.

mvn archetype:generate -DgroupId=de.test -DartifactId=testAEM -DarchetypeArtifactId=sample-project-archetype -DarchetypeGroupId=com.adobe.granite.archetypes -DarchetypeVersion=7 -DarchetypeCatalog=https://repo.adobe.com/nexus/content/groups/public/archetype-catalog.xml -DinteractiveMode=true

Prerequisites for maven to work.

You need to have adobe repository reference in your maven settings.xml file.

<profiles>

<profile>

<id>adobe-public</id>

 

<activation>

<activeByDefault>true</activeByDefault>

</activation>

 

<properties>

<releaseRepository-Id>adobe-public-releases</releaseRepository-Id>

<releaseRepository-Name>Adobe Public Releases</releaseRepository-Name>

<releaseRepository-URL>http://repo.adobe.com/nexus/content/groups/public</releaseRepository-URL&gt;

</properties>

<repositories>

<repository>

<id>adobe-public-releases</id>

<name>Adobe Public Repository</name>

<url>http://repo.adobe.com/nexus/content/groups/public</url&gt;

<releases>

<enabled>true</enabled>

<updatePolicy>never</updatePolicy>

</releases>

<snapshots>

<enabled>false</enabled>

</snapshots>

</repository>

</repositories>

 

<pluginRepositories>

<pluginRepository>

<id>adobe-public-releases</id>

<name>Adobe Public Repository</name>

<url>http://repo.adobe.com/nexus/content/groups/public</url&gt;

<releases>

<enabled>true</enabled>

<updatePolicy>never</updatePolicy>

</releases>

<snapshots>

<enabled>false</enabled>

</snapshots>

</pluginRepository>

</pluginRepositories>

</profile>

</profiles>

Jcr Sql 2 vs Xpath queries comparison and point of view CQ5/AEM

I was quite fascinated by the world of XPath and the way i could configure a quick map and pass it to the queryBuilder interface to run a simple query and parse results. All my dreams shattered when i was told Adobe has deprecated the support of XPath queries and you are now required to use the JCR – SQL2 queries. The next search class that i ended up writing was for JCR and all was good until we really started pushing the content. The response time of the queries climbed up rapidly and we started feeling the pain of working with JCR queries.

JCR queries shouldn’t be used at all for multiple paths, by this i mean if you want to search under two different paths with one query you will end up with a very high response times. XPath queries or Query Builder seems to do that fine. For simple queries, you won’t find much difference between the response times of JcrSql queries and Xpath queries but as you build complex queries and adding multiple predicate you will soon start to find yourself in deeper and deeper holes.

For this very reason, i believe Adobe has not been able to deprecate Xpath or Query builder as everything is backwards compatible.

The performance of XPath isn’t very good, if you put it under heavy load but it has to be heavy load more than 500 concurrent search requests or something. The average response times for us for not so complex queries under load were approx 5 seconds under very heavy load for us. We then decided to use listChildren method of Resource interface as this is quite fast. 100x faster and it is not an exaggeration. If you are building such a system, i hope you will get your caching strategies right. Try to save your query results for easier retrieval afterwards and make less number of queries as you can offload a lot of that to the caching server.

So, build your systems with some of advice mentioned above and you should be ok.

 

Get Resource Resolver from session CQ5/AEM

Oddly, it is easy enough to get a resourceResolver from session. // Below, is how you can do it.

public ResourceResolver getResourceResolverFromSession(final Session session, final ResourceResolverFactory resourceResolverFactory) {

try {
return resourceResolverFactory.getResourceResolver(Collections.singletonMap(“user.jcr.session”, (Object) session));
} catch (LoginException e) {
throw new IllegalStateException(e);
}
}

How to get an impersonated Resolver in CQ5/AEM?

It is very easy to get an impersonated resolver:

Try the below code. Just pass the user id and resourceResolverFactory instance.

private ResourceResolver getImpersonatedResolver(final String userId) {
try {
return resourceResolverFactory.getAdministrativeResourceResolver(Collections.singletonMap(ResourceResolverFactory.USER_IMPERSONATION, (Object) userId));
} catch (LoginException e) {
throw new RuntimeException(e);
}
}

Workflows gone into loop and occupied all the disk space AEM/CQ5

Some times invariably the workflows go into loop due to mis-configuration by developers in these cases you can check the status of the workflows using

http://xxx:4502/etc/reports/workflowreport.html // This will report a very high number in the case above is true.

What can you to fix up the disk space in CQ in case above

Suggest you try using http://xxx:4502/system/console/jmx/com.adobe.granite.workflow%3Atype%3DMaintenance to purge completed worklows.

After that you might still need to run the repository garbage collection to free up the space (http://xxx:4502/system/console/jmx/com.adobe.granite%3Atype%3DRepository).

This is just a step to free up some space temporarily but ideally the configuration steps should be looked at again and fixed.

How to send attachments in email CQ5/AEM

There are often requirements for sending attachments in emails. I was using upload OOTB form component and i had slightly customized the OOTB form component but whenever i was sending email on my local dev machine i was not receiving the attachments in email. 

Basically, you need to have instance of multipart email (apache commons library) and once you have that, it is easy enough to send that email .

The below code sample is what i used to send the attachment in the email.

Please see that this is not production ready and not complete code. You may have to place some pieces here and there.

 try {  
                List<String> contentNamesList = new ArrayList<String>();  
                Iterator<String> names = FormsHelper.getContentRequestParameterNames(request);  
                while (names.hasNext()) {  
                     String name = (String)names.next();  
                     contentNamesList.add(name);  
                }  
                Collections.sort(contentNamesList);  
                List<String> namesList = new ArrayList<String>();  
                Iterator<Resource> fields = FormsHelper.getFormElements(request.getResource());  
                while (fields.hasNext()) {  
                     Resource field = (Resource)fields.next();  
                     FieldDescription[] descs = FieldHelper.getFieldDescriptions(request, field);  
                     for (FieldDescription desc : descs)  
                     {  
                          contentNamesList.remove(desc.getName());  
                          if (!desc.isPrivate()) {  
                               namesList.add(desc.getName());  
                          }  
                     }  
                }  
                namesList.addAll(contentNamesList);  
                List<RequestParameter> attachments = new ArrayList<RequestParameter>();  
                for (String name : namesList) {  
                     RequestParameter rp = request.getRequestParameter(name);  
                     if (null!=rp && !rp.isFormField() &&rp.getSize() > 0L) {  
                          attachments.add(rp);  
                     }  
                }  
                MultiPartEmail mpEmail;  
                Email email;  
                if (attachments.size() > 0) {  
                     mpEmail = new HtmlEmail();  
                     email = mpEmail;  
                     for (RequestParameter rp : attachments) {  
                          ByteArrayDataSource ea = new ByteArrayDataSource(rp.getInputStream(), rp.getContentType());  
                          mpEmail.attach(ea, rp.getFileName(), rp.getFileName());  
                     }  
                } else {  
                     email = new HtmlEmail();  
                }  
                email.setCharset("utf-8");  
                email.setMsg(getEmailBody(//get body);  
                for (String rec : mailTo) {  
                     email.addTo(rec);  
                }  
                email.setSubject(//set subject);  
                email.setFrom(//set from);  
                getMessageGatewayService().getGateway(Email.class).send(email);  
           }  
           catch (EmailException e) {  
                throw new ServletException("Form can't be posted",e);  
           }