Tuesday, March 7, 2017

AEM Site Performance Tuning - Query monitoring

If you are facing any performance issues with pages developed in AEM, then its time to look on the JCR queries running in your application.

Some of the query monitoring mechanism are given below

1) Identify any potential errors or warnings in error.log.
If you see any warnings as below, then you are required to add proper oak index.
 org.apache.jackrabbit.oak.spi.query.Cursors$TraversingCursor Traversed 12000 nodes with filter Filter... consider creating an index or changing the query

Ref: https://docs.adobe.com/docs/en/aem/6-1/deploy/platform/queries-and-indexing.html

2) Debug query details
Add new logger org.apache.jackrabbit.oak.query and configure the log level to Debug/Trace.




You will more details on query, index and cost.

Ref: http://jackrabbit.apache.org/oak/docs/query/query-engine.html

3) Query Monitoring tools

AEM provides Query Performance tool for analyzing the slow queries.
http://localhost:4502/libs/granite/operations/content/diagnosis/tool.html/_granite_queryperformance

In explain query tab, you can test the query and analyze the time, applied index etc.

In addition, there is ACS Commons which provides additional Explain Query feature for analyzing more details like index cost.
http://localhost:4502/etc/acs-tools/explain-query.html



Thursday, February 23, 2017

Creating AEM service user and defining ACL package

From AEM6 , the usage of admin session is deprecated. In this post, i'll try to explain how to create the service user and get the session.  Also briefly explains how to create the ACL service package.

1) Create a system user
The service user is normal AEM user having enough permissions in required nodes. 

Go to http://localhost:4502/useradmin and create a user and provide proper permissions.

Sample user:  rsa-service-user


2) Create the service mapping Osgi configurations.

File: org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-rsa.xml

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:OsgiConfig"
service.ranking="0"
user.mapping="[org.test.rsa-osgi:repositoryService=rsa-service-user]" />

This mapping is required for getting jcr session in specified Osgi bundles. Here "org.test.rsa-osgi" is orgi bundle name.

3) Getting service user session in Osgi service

// Getting Resource Resolver
ResourceResolver resourceResolver = null;
try {
final Map<String, Object> param = new HashMap<String, Object>();
param.put(ResourceResolverFactory.SUBSERVICE, "repositoryService");
resourceResolver = resolverFactory.getServiceResourceResolver(param);
} catch (LoginException e) {
//
}

// Getting JCR session
Session session = null;
try {
session = repository.loginService("repositoryService", repository.getDefaultWorkspace());
} catch (RepositoryException e) {
//
}
return session;
}

4) Creating ACL package

You may create the ACL service package so it will be useful on deploying in higher environment.
In this package, we need to include rep policy path  and service user path.


The user path can be copied from edit user console.

Edit the package and select the option Overwrite in AC Handling for overwriting the access policies.





Friday, February 3, 2017

How to change authoring mode in AEM 6?

Touch UI is the default authoring mode in AEM 6.  Here is how to change the default authoring mode to CLASSIC mode.

Change via Web console
Go to Felix web console and open configuration Manager
http://localhost:4502/system/console/configMgr

Open WCM Authoring UI Mode Service configuration.

Change the value to CLASSIC. By default it is TOUCH.


Change via Osgi config

Create Osgi config under project /apps/config

com.day.cq.wcm.core.impl.AuthoringUIModeServiceImpl.xml

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="sling:OsgiConfig"
    authoringUIModeService.default="CLASSIC"
    authoringUIModeService.editorUrl.classic="/cf#"
    authoringUIModeService.editorUrl.touch="/editor.html"/>


Note: This setting change globally for all sites hosted in that instance. 

Friday, January 27, 2017

How to create and package the Oak index in AEM 6?


Issue: If you see some warnings like below in error.log, then its time to create index for improving jcr search performance

*WARN* [0:0:0:0:0:0:0:1 [1485528947344] GET /bin/services/rsa/profile.json HTTP/1.1] org.apache.jackrabbit.oak.spi.query.Cursors$TraversingCursor Traversed 26000 nodes with filter Filter(query=select [jcr:path], [jcr:score], * from [nt:unstructured] as a where [userid] like '_%' and [securityservice] like 'rsa' and isdescendantnode(a, '/profile') /* xpath: /jcr:root/profile//element(*, nt:unstructured)[(jcr:like(@userid, '_%') and jcr:like(@securityservice, 'rsa'))] */, path=/profile//*, property=[userid=[(_%..], securityservice=[rsa]]); consider creating an index or changing the query


Steps

1) Identify the potential properties that need to be indexed.

2) Create _oak_index folder and add the content xml for creating oak index definition nodes.

Path: /your_module/src/main/content/jcr_root/_oak_index/.content.xml


<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:slingevent="http://sling.apache.org/jcr/event/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:dam="http://www.day.com/dam/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:rep="internal"
    jcr:mixinTypes="[rep:AccessControllable]"
    jcr:primaryType="nt:unstructured">
    <userid
        jcr:primaryType="oak:QueryIndexDefinition"
        propertyNames="{Name}[userid]"
        reindex="{Boolean}false"
        type="property"/>
    <securityservice
        jcr:primaryType="oak:QueryIndexDefinition"
        propertyNames="{Name}[securityservice]"
        reindex="{Boolean}false"
        type="property"/>      
</jcr:root>

2) Add the paths in filter.xml

  <filter root="/oak:index/userid"/>
  <filter root="/oak:index/securityservice"/>

This will create Oak index below node /oak:index. You can also use CRXDE to create index. 



Ref: https://docs.adobe.com/docs/en/aem/6-0/deploy/upgrade/queries-and-indexing.html


Tuesday, January 24, 2017

How to unblock POST method filtered by CSRF Filter in AEM 6.1?

Issue: The POST method form submission returns 403 forbidden error in AEM 6.1

Solution: In AEM 6.1, the CSRF security is added besides Apache Sling Referrer. The AEM is expecting a valid CSRF token for every POST by default.

Include following clientlib in the template or component using the HTML form.

<cq:includeClientLib js="granite.csrf.standalone"/>

The script will generates the CSRF token like below and sends with POST request.
CSRF-Token:
eyJleHAiOjE0ODUyNzQ2NDQsImlhdCI6MTQ4NTI3NDA0NH0.AOyz-sTX9Ohh5hfBwFF_Qx8_y6GU8kfdMwIeVZZa88I


For verifying whether the POST method is blocked by CSRF framework, you can test by removing the POST entry in CSRFFilter configuration.

http://localhost:4502/system/console/configMgr/com.adobe.granite.csrf.impl.CSRFFilter

Ref: https://docs.adobe.com/docs/en/aem/6-1/develop/security/csrf-protection.html

Saturday, July 30, 2016

Sling Model, the easier way to map data model in AEM

In the previous post, we see  how to adapt the JCR data to POJO model using Sling Adaptable interface. There are easier way to map jcr resource to domain models. Here comes the Sling Model for our help from AEM 6.0 onwards.

In the below example i'm trying to illustrate with same VehicleData model using Sling Model.

Only thing we need to annotate the VehicleData model class as follows.

import javax.inject.Inject;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;

@Model(adaptables=Resource.class)
public class VehicleData {

@Inject
private String vin;
@Inject
private String manufacturer;

/**
* @return the vin
*/
public String getVin() {
return vin;
}

/**
* @param vin the vin to set
*/
public void setVin(String vin) {
this.vin = vin;
}

/**
* @return the manufacturer
*/
public String getManufacturer() {
return manufacturer;
}

/**
* @param manufacturer the manufacturer to set
*/
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}

}

ie all, now we easier to adapt the resource to vehicledata domain model.


VehicleData vehicleData = resource.adaptTo(VehicleData.class);

Wednesday, July 27, 2016

Custom node type and Adaptable mapping in AEM

Scenario: If we comes up with a requirement of  handling custom set of data  in AEM, and sometimes its better to create custom node types than using nt:unstructured node type. The reason is its easier to map and search the data.

In the following example, i'll try to explain how to create custom node type, how to map with model class using Sling Adaptable interface and how to display the model data in UI.

We can take an example of handling a custom vehicle data structure. The following are the sequence of steps to create, search and display custom data.

1: Register custom node type.

The following is the Node Definition for vehicle data.

<’tv’=‘http://mynamespace.tv/tv’>  
[tv:Vehicle] 
orderable
- vin (string) mandatory
- manufacturer (string)
- type (string)
- color (string)
- hp (long)
- capacity (long)

You can import the above definition via CRX explorer. Also we can add programmatically add using NodeTypeManager and it may depends on your project requirements.

2: Create vehicle data content

Next is to create list of vehicle nodes in crx of type tv:VehicleData. For this example, nodes are created under /content/TestBP/test

3: Create data model

This is just a data model representing the object of above vehicle data structure.

public class VehicleData {
private String vin;
private String manufacturer;
public String getVin() {
return vin;}
public void setVin(String vin) {
this.vin = vin;
}
public String getManufacturer() {
return manufacturer;}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;}
}

4: Create Custom Adapter class

The custom adapter class converts the sling resource to vehicledata model.  We have to write the mapping rule here.

public class VehicleAdapter {
protected final Logger log = LoggerFactory.getLogger(this.getClass());
public VehicleData adaptToVehicleData(Resource resource){
VehicleData vehicleData = null;
Node node = null;
if(null != resource){
vehicleData = new VehicleData();
node = resource.adaptTo(Node.class);
try {
vehicleData.setVin(node.getProperty("vin").getString());
// TODO: we can add remaining mapping here 
} catch (ValueFormatException e) {
log.error(e.getMessage());
} catch (PathNotFoundException e) {
log.error(e.getMessage());
} catch (RepositoryException e) {
log.error(e.getMessage());
}
}
return vehicleData;
}
}

5: Create Custom AdapterFactory

This is  Osgi service registers the adaptables and adapters.

@Component
@Service(value=org.apache.sling.api.adapter.AdapterFactory.class)
@Properties({
   @Property(name = "adaptables", value = { "org.apache.sling.api.resource.Resource" }),
   @Property(name = "adapters", value = { "org.tv.aem.vehiclemgt.models.VehicleData" })
})
public class VehicleAdapterFactory implements AdapterFactory {

public <AdapterType> AdapterType getAdapter(final Object adaptable,
Class<AdapterType> type) {
return (AdapterType) getAdapter((Resource)adaptable, type);
}

private <AdapterType> AdapterType getAdapter(Resource resource,
Class<AdapterType> type) {
return (AdapterType) new VehicleAdapter().adaptToVehicleData(resource);
}

}


6:  Create domain service

This is a Osgi domain service with method to search the vehicle content and return the vehicle list.

public interface VehicleManagement {

List<VehicleData> getVehicles();

}

@Component
@Service(value=VehicleManagement.class)
public class VehicelManagementImpl implements VehicleManagement{

protected final Logger log = LoggerFactory.getLogger(this.getClass());
final static String VEHICLE_LIST_PATH = "/content/TestBP/test";
@Reference
private ResourceResolverFactory resolverFactory;
public List<VehicleData> getVehicles() {
ResourceResolver resourceResolver;
Session session;
//QueryManager queryManager;
QueryBuilder queryBuilder;
Query query;
SearchResult results;
List<VehicleData> vehicleList = null;
try {
resourceResolver = resolverFactory.getAdministrativeResourceResolver(null);
session = resourceResolver.adaptTo(Session.class);
queryBuilder = resourceResolver.adaptTo(QueryBuilder.class);
query = queryBuilder.createQuery(PredicateGroup.create(getSearchMap()), session);
results = query.getResult();
if(results.getHits().size() > 0){
vehicleList = new ArrayList<VehicleData>();
for (Hit hit : results.getHits()) {
Resource resource = hit.getResource();
VehicleData vehicleData = resource.adaptTo(VehicleData.class);
vehicleList.add(vehicleData);
}
}
} catch (LoginException e) {
log.error(e.getMessage());
}
catch (RepositoryException e) {
log.error(e.getMessage());
}
return vehicleList;
}
private Map<String, String> getSearchMap(){
Map<String, String> map = new HashMap<String, String>();
map.put("path", VEHICLE_LIST_PATH);
map.put("type", "tv:VehicleData");
return map;
}


}

7: Create vehicle list component

This is a custom aem component used to call vehicle list service and display.

<%@include file="/libs/foundation/global.jsp"%>
<%@page session="false" import="org.tv.aem.vehiclemgt.VehicleManagement,org.tv.aem.vehiclemgt.models.VehicleData,java.util.List" %><%
%><%
%>

<% VehicleManagement vms = sling.getService(VehicleManagement.class); %>
<p>Vehicle list</p>
<div id="container">
<% 
// TODO: create table structure.  
List<VehicleData> vehicles = vms.getVehicles();
if(vehicles != null ){
for(VehicleData vehicle:vehicles){
out.println(vehicle.getVin());
// we can add more 
}
}
%>


</div>

References
https://docs.adobe.com/docs/en/aem/6-1/develop/platform/custom-nodetypes.html