AEM · AEM 6

AEM | ACS – Generic List | Service to Configure Site Specific List


In this article we will create a list service, which will allow Multiple sites to configure List path, we will create a service and will use site specific runmodes for site-list configuration.

Note: Please see Generic List – Support Site Inheritance for understanding of Generic List requirement and complete solution.

Resources

  1. Code – Download
  2. Run Mode – Download

Step 1: Create MyList Interface

package com.mysite.services;

import java.util.List;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;

import org.apache.sling.commons.json.JSONArray;

/**
 * @author Mohit K. Bansal
 */
public interface MyList {
    /**
     * Find correct resource based on mapping, and return list of json
     * @param request
     * @return JSONArray
     */
    public JSONArray getJSONList(final SlingHttpServletRequest request);
	
    /**
     * Find correct resource based on mapping, and return list of resource. 
     * @param request
     * @param listName
     * @return List
     */
    public List getResourceList(final SlingHttpServletRequest request, final String listName);
}

Step 2: Create MyListImpl class to Implement MyList Interface

package com.mysite.services.impl;

import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceMetadata;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.wrappers.ValueMapDecorator;
import org.apache.sling.commons.json.JSONArray;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.JSONObject;
import org.osgi.service.component.ComponentContext;

import com.adobe.granite.ui.components.ds.ValueMapResource;
import com.mysite.services.MyList;

/**
 * List Service - an extended version of ACS commons generic list. 
 * List support inheritance for sites, which allows sites to have different list configurations for same component.
 * 
 * @author Mohit K. Bansal
 */
@Component(immediate = true, metatype = true, label = "MySite - Core - Generic List Mapping", description = "Generic List Mapping", enabled = true, configurationFactory = true)
@Service
@Properties(value = {
	@Property(name = "webconsole.configurationFactory.nameHint", value={"List for Content Path: {list.contentpath}, List Path: {list.listpath}"}),
        @Property(name = MyListImpl.PROPERTY_CONTENTPATH, 
        		  label = "Content Path",
        		  description = "Site base content path",
        		  propertyPrivate = false),
        @Property(name = MyListImpl.PROPERTY_LISTPATH, 
				  label = "Generic List Path",
				  description = "Site Specific Generic list base path",
				  propertyPrivate = false)
})
public class MyListImpl implements MyList {

    /**
     * Servlet Properties
     */
    static final String PROPERTY_CONTENTPATH = "list.contentpath";
    static final String PROPERTY_LISTPATH = "list.listpath";
    
    private static final String DEFAULT_LISTPATH = "/etc/lists/mysite";
    private static final String EXTN = ".mylist.json";
    private static final String LISTNODE = "jcr:content/list";
	
    private static final String FORWARDSHASH = "/";
    private static final String JCR_TITLE = "jcr:title";
    private static final String VALUE = "value";
    private static final String EMPTY = new String();
    private static final String TEXT = "text";
    private static final String NT_UNSTRUCTURED = "nt:unstructured";

    /**
     * Configuration Manager map
     */
    private static Map<String, String> configMaps = new HashMap<String, String>();
    
    /**
     * Registering List Services
     * 
     * @param ctx
     * @throws MalformedURLException
     */
    @Activate
    protected void activate(ComponentContext ctx) {
        final Dictionary properties = ctx.getProperties();
        
        String serviceName = (String) properties.get(PROPERTY_CONTENTPATH);
        String serviceValue = (String) properties.get(PROPERTY_LISTPATH);
        
        configMaps.put(serviceName, serviceValue);
    }
	
    /**
     * Find correct resource based on mapping, and return list of json
     * 
     * @param request 
     * 
     * @return JSONArray 
     */
    @Override
    public JSONArray getJSONList(SlingHttpServletRequest request) {
 	String url = request.getRequestURI();
    	String listPath = this._getListBasePath(url);
    	String listName = url.substring(url.lastIndexOf(FORWARDSHASH)+1, url.lastIndexOf(EXTN));
    	
    	Resource res = this._getListResource(request, listPath, listName);    	
    	JSONArray jarray = new JSONArray();
        if(null != res) {
            Resource listres = res.getChild(LISTNODE);
            Iterator listItems = listres.listChildren();
            while (listItems.hasNext()) {
                Resource listItem = listItems.next();
                String title = listItem.adaptTo(ValueMap.class).get(JCR_TITLE, String.class);
                String value = listItem.adaptTo(ValueMap.class).get(VALUE, EMPTY);
                if (title != null) {
                    JSONObject jobject=new JSONObject();
                    try {
    		        jobject.put(VALUE, value);
    			jobject.put(TEXT, title);
    		    } catch (JSONException e) {
    		    }
                    jarray.put(jobject);
                }
            }
        }
        return jarray;
    }
	
    /**
     * Find correct resource based on mapping, and return list of resource.
     * 
     * @param request 
     * @param listName 
     * 
     * @return List
     */
    @Override
    public List getResourceList(SlingHttpServletRequest request, String listName) {
	String listPath = this._getListBasePath(request.getRequestPathInfo().getSuffix());
		
 	Resource res = this._getListResource(request, listPath, listName);		
	List fakeResourceList = new ArrayList();
	ResourceResolver resolver = res.getResourceResolver();
        if(null != res) {
            Resource listres = res.getChild(LISTNODE);
            Iterator listItems = listres.listChildren();
        	
            while (listItems.hasNext()) {
                Resource listItem = listItems.next();
                String title = listItem.adaptTo(ValueMap.class).get(JCR_TITLE, String.class);
                String value = listItem.adaptTo(ValueMap.class).get(VALUE, EMPTY);
                if (title != null) {
                    ValueMap vm = new ValueMapDecorator(new HashMap<String, Object>());
                    vm.put(VALUE, value);
                    vm.put(TEXT, title);
                    
                    fakeResourceList.add(new ValueMapResource(resolver, new ResourceMetadata(), NT_UNSTRUCTURED, vm));
                }
            }
        }
        return fakeResourceList;
    }

    private Resource _getListResource(final SlingHttpServletRequest request, final String listPath, final String listName) {
        // Check 1. Find list in mapped list hierarchy
    	Resource rootResource = request.getResource().getResourceResolver().getResource(listPath);
    	Iterator resources = rootResource.listChildren();
    	while(resources.hasNext()) {
    	    Resource res = resources.next();
    	    if(listName.equalsIgnoreCase(res.getName())) {
    		return res;
    	    }
    	}

    	// Check 2. Find list in default list hierarchy
    	if(!listPath.equalsIgnoreCase(DEFAULT_LISTPATH)) {
    	    rootResource = request.getResource().getResourceResolver().getResource(DEFAULT_LISTPATH);
            resources = rootResource.listChildren();
            while(resources.hasNext()) {
        	Resource res = resources.next();
        	if(listName.equalsIgnoreCase(res.getName())) {
        	    return res;
        	}
            }
    	}
    	
    	return null;
    }
	
    private String _getListBasePath(final String url) {
	String path = DEFAULT_LISTPATH;
		
	for(final String key: configMaps.keySet()) {
            if(url.startsWith(key)) {
    		path = configMaps.get(key);
    		break;
    	    }
    	}	
	return path;
    }
}

Step 3: Configuration

List Service is fully configurable. We can create separate configuration for each website via Runmode or OSGI Console.

  • OSGI Configuration view
    A view of List Service in configuration manager console:

    Generic List - OSGI Configuration View
    Generic List – OSGI Configuration View
  • Configuration for each site

    Generic List - OSGI Configuration
    Generic List – OSGI Configuration
  • Details of each configuration
    Property Possible Value Description
    Content path <String> Site Base Content path. eg. /content/mysite
    Generic List Path <String> Path of Generic List for site
  • Registering new site with MyList Service
    To register a new site with List Service, we just need to create a new runmode with correct information.

    1. Create a runmode xml for MyList Service. Name of xml should be:
      com.mysite.services.impl.MyListImpl-<SiteName>.xml
    2. Add necessary information in runmode xml
      <?xml version="1.0" encoding="UTF-8"?>
      <jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
              jcr:primaryType="sling:OsgiConfig"
              list.contentpath="/content/mysite"
              list.listpath="/etc/lists/mysite" />

      You can download a sample runmode config file from here

 

 

Advertisements

3 thoughts on “AEM | ACS – Generic List | Service to Configure Site Specific List

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s