Wednesday, 2 September 2015

Upgrade to AEM 6.1 forms from AEM 6.0 forms for OSGi

Before you upgrade

Perform the following steps, before upgrading from AEM 6.0 forms to AEM 6.1 forms:
  • Install the latest service pack and patches for AEM 6.0 forms.
  • (If your AEM 6.0.1 instance is running on CRX2 repository) Open the Web Bundles console http://localhost:4502/system/console/bundles, delete the com.day.crx.sling.server bundle, and shut down the AEM instance.\

Perform the upgrade

Perform the following steps to upgrade from AEM 6.0 forms to AEM 6.1 forms:
  1. Download the AEM 6.1 QuickStart.
  2. Replace the AEM 6.0 QuickStart with AEM 6.1 QuickStart and start the author instance.
    Starting the AEM 6.1 QuickStart, upgrades the existing crx-repository to AEM 6.1, automatically migrates the content from older version to newer version, and creates a folder named archived-versions.  
  3. After the server is up and running, shut down the AEM instance.
    It might take a few minutes for the AEM instance to be up and running. You can consider the AEM instance is up and running after the [AEM-root]/logs/error.log file becomes stable.
  4. Open the archived-versions folder and copy all the folders inside archived-versions folder to the [AEM_root]\crx-repository folder. Restart the AEM 6.1 QuickStart.  
  5. Download and install AEM forms add-on package.
    Note:
    Before installing the AEM forms add-on package, ensure that the installation path of the AEM Quickstart does not contain any spaces.  
    AEM forms 6.1 add-on packageDownload link
    AEM forms add-on package for AIXadobe-aemfd-aix-pkg
    AEM forms add-on package for Linuxadobe-aemfd-linux-pkg
    AEM forms add-on package for Solarisadobe-aemfd-solaris-pkg
    AEM forms add-on package for Windowsadobe-aemfd-win-pkg
    Central Migration Bridge and Send to printer Servicesadobe-aemfd-cmb-pkg
    Add on package for Geometrixx Finance sample sitecq-geometrixx-finance-pkg
    Caution:
    After installing the add-on package, you will get a prompt to restart the server. However, do not immediately restart. Ensure that error.log is stable and all bundles (except signatures) are in active mode before you restart the server.
  6. Configure the Doc Assurance Service
    The DocAssurance service is not available out of the box. It requires RSA and BouncyCastle libraries installed with AEM forms package. Perform the following steps to bootdelegate these libraries:   
    1. Stop the AEM server.
    2. Open the sling.properties at [AEM installation]\crx-quickstart\conf\ for editing.
      Note:
      If you use [AEM_root]\crx-quickstart\bin\start.bat to start AEM, then edit the sling.properties at [AEM_root]\crx-quickstart\
    3. Add the following properties to the sling.properties file:
      1
      2
      sling.bootdelegation.class.com.rsa.jsafe.provider.JsafeJCE=com.rsa.*
      sling.bootdelegation.class.org.bouncycastle.jce.provider.BouncyCastleProvider=org.bouncycastle.*
    4. Save and close the file. Restart the AEM server.

Verify the upgraded instance

  1. Open Web Bundles console and ensure that all the bundles at /system/console/bundle are active. The default address of the Web Bundles is http://localhost:4502/system/console/bundles.
  2. Open the package manager, ensure that a cq-content package with version 6.1 exists.

HOW DISPATCHER RETURNS DOCUMENTS : How DISPATCHER caching Works ?



file



The Dispatcher always requests the document directly from the AEM instance in the following cases:

  • If the HTTP method is not GET. Other common methods are POST for form data and HEAD for the HTTP header.
  • If the request URI contains a question mark "?". This usually indicates a dynamic page, such as a search result, which does not need to be cached.
  • The file extension is missing. The web server needs the extension to determine the document type (the MIME-type).
  • The authentication header is set (this can be configured)
Determining if a document is cached
The Dispatcher stores the cached files on the web server as if they were part of a static website. If a user requests a cacheable document the Dispatcher checks whether that document exists in the web server's file system:
  • if the document is cached, Dispatcher returns the file.
  • if it is not cached, the Dispatcher requests the document from the AEM instance.
Determining if a document is up-to-date
To find out if a document is up to date, the Dispatcher performs two steps:

  1. It checks whether the document is subject to auto-invalidation. If not, the document is considered up-to-date.
  2. If the document is configured for auto-invalidation, the Dispatcher checks whether it is older or newer than the last change available. If it is older, the Dispatcher requests the current version from the AEM instance and replaces the version in the cache.
Methods for Caching
The Dispatcher has two primary methods for updating the cache content when changes are made to the website.
  • Content Updates remove the pages that have changed, as well as files that are directly associated with them.
  • Auto-Invalidation automatically invalidates those parts of the cache that may be out of date after an update. i.e. it effectively flags relevant pages as being out of date, without deleting anything.
Content Updates
In a content update, one or more AEM documents change. AEM sends a syndication request to the Dispatcher, which updates the cache accordingly:
  1. It deletes the modified file(s) from the cache.
  2. It deletes all files that start with the same handle from the cache. For example, if the file /en/index.html is updated, all the files that start with /en/index. are deleted. This mechanism allows you to design cache-efficient sites, especially in regard to picture navigations.
  3. It touches the so-called statfile; this updates the timestamp of the statfile to indicate the date of the last change.
The following points should be noted:
  • Content Updates are typically used in conjunction with an authoring system which "knows" what must be replaced.
  • Files that are affected by a content update are removed, but not replaced immediately. The next time such a file is requested, the Dispatcher fetches the new file from the AEM instance and places it in the cache, thereby overwriting the old content.
  • Typically, automatically generated pictures that incorporate text from a page are stored in picture files starting with the same handle - thus ensuring that the association exists for deletion. For example, you may store the title text of the page mypage.html as the picture mypage.titlePicture.gif in the same folder. This way the picture is automatically deleted from the cache each time the page is updated, so you can be sure that the picture always reflects the current version of the page.
  • You may have several statfiles, for example one per language folder. If a page is updated, AEM looks for the next parent folder containing a statfile, and touches that file.
Auto-invalidation
Auto-invalidation automatically invalidates parts of the cache - without physically deleting any files. At every content update, the so-called statfile is touched, so its timestamp reflects the last content update.
The Dispatcher has a list of files that are subject to auto-invalidation. When a document from that list is requested, the Dispatcher compares the date of the cached document with the timestamp of the statfile:
  • if the cached document is newer, the Dispatcher returns it.
  • if it is older, the Dispatcher retrieves the current version from the AEM instance.

Again, certain points should be noted:
  • Auto invalidation is typically used when the inter-relations are complex e.g. for HTML pages. These pages contain links and navigation entries, so they usually have to be updated after a content update. If you have automatically generated PDF or picture files, you may choose to auto-invalidate those too.
  • Auto-invalidation does not involve any action by the dispatcher at update time, except for touching the statfile. However, touching the statfile automatically renders the cache content obsolete, without physically removing it from the cache.






Friday, 17 April 2015

SSL on AEM 6 Author (Ubuntu)

Create a directory named ssl in the directory where the quickstart JAR file is located.

go there and below commands (Please change default valuse)

keytool -genkeypair -keyalg RSA -validity 3650 -alias cqse -keystore /Misc/SelfWork/Author/ssl/cqkeystore.keystore -keypass password -storepass password -dname "CN=sbroders-w7, OU=CQ, O=Adobe, L=Ottawa,S=Ontario, C=CA"


 keytool -export -alias cqse -file client.cer -keystore cqkeystore.keystore


 keytool -import -v -trustcacerts -alias cqse -file client.cer -keystore truststore.ts



Next


  1. Open CRXDE Lite and select the /apps folder. Click Create > Create Folder to create a folder named system (http://localhost:4502/crx/de).
  2. Below the system folder create a folder named config.author.
  3. Select the /apps/system/config.author node.
  4. Click Create > Create Node and enter the following properties:
    • Name: org.apache.felix.http
    • Type: sling:OsgiConfig 


Add properties to the node according to the following table:
Name Type Value
org.apache.felix.https.enable Boolean true
org.osgi.service.http.port.secure Long 5433
org.apache.felix.https.nio Boolean true
org.apache.felix.https.keystore String [quickstart_dir]/ssl/cqkeystore.keystore
org.apache.felix.https.keystore.password String The password.
org.apache.felix.https.keystore.key String alias e.g. cqse
org.apache.felix.https.keystore.key.password String The password.
org.apache.felix.https.truststore String Path to truststore
org.apache.felix.https.truststore.password String Truststore password.
(Optional) org.apache.felix.https.clientcertificate String Defaults to none


For more info http://docs.adobe.com/docs/en/aem/6-0/deploy/configuring/config-ssl.html

Friday, 16 January 2015

JCR observation

JCR observation


JCR observation is a very cool concept, is it allows you to react on changes in the repository; this allows you to change any written data after the write in a centralized manner, instead of attaching a post processing step to each and every method before it is doing the save to the repository.

So JCR observation is a powerful concept, and is widely used for many different purposes.

But as it is a very fundamental concept and basis for a lot of features within CQ, you can do also a lot of harm in there. So I want to discuss the most important feature of JCR observation.

JCR observation is single-threaded

In Jackrabbit 1 and Jackrabbit 2 the JCR observation is a single-threaded mechanism. So for every event each registered listener is evaluated, and if there’s a match, the onEvent method of that listener is called by that thread.

Recommendation 1
The code executed in that onEvent method should be really fast. No long-standing calculation, no network access.

Recommendation 2
If you have a long-standing calculation or if you need to do network access, do it in a dedicated thread and run it asynchronously to the JCR observation.

Recommendation 3
Be careful when your event listener is writing to the repository as well. It there’s already a heavy write load, your event listener might block when it comes to writing to the repository. This will add additional delay to the JCR observation processing.

Recommendation 4
If your JCR observation handler needs to be active only on certain instances, make the register process configurable. Based on run mode or OSGI configuration do not do the “addEventListener” call. This avoids  the small overhead of executing an Event listener, which is not required on this instance.

Recommendation 5
If you get the WARN statement in your log files, that you have more than 200k pending events in the observation queue, your alarm clocks should ring. In that case all save() operations are delayed until the queue goes below that limit again (see http://www.docjar.com/html/api/org/apache/jackrabbit/core/observation/ObservationDispatcher.java.html); if you encounter this message, your first priority should be to analyze your observation listeners and speed them up.

Recommendation 6
If you want to know which observation listener consumes how much time, you should set the class “org.apache.jackrabbit.core.observation.EventConsumer” to DEBUG; it will then print for each event (!), which listener consumed how much time.


A good example for this are the CQ DAM Asset Update workflows. There are many ways how you can ingest assets into your CQ: direct upload via browser, webdav, sync from Creative Cloud, uploads via your own bulk ingestions methods, the Sling POST servlet, … And instead of attaching the metadata extraction, the rendition generation and all the other stuff to each of these methods, a single centralized process takes care of it. It doesn’t matter that this process is asynchronous to the ingestion itself. In this case it’s a workflow, but the workflow triggers are also directly based on JCR observation.

Conclusion, if you use use JCR observation, be aware, that you the code fast you want to execute. Everything which exceeds a few milliseconds should be offloaded to a separate thread. And check, that you run only the observation listeners which are really required to run.

Thursday, 8 January 2015

Multi image component using MultiField - AEM 5.6 / 6.0

Create Component


1) In your CRXDE Lite http://localhost:4502/crx/de, create below folder and save changes

                      /apps/imagemultifield

2) Copy the component /libs/foundation/components/logo and paste it in path /apps/imagemultifield

3) Rename /apps/imagemultifield/logo to /apps/imagemultifield/imagemultifield

4) Rename /apps/imagemultifield/imagemultifield/logo.jsp to /apps/imagemultifield/imagemultifield/imagemultifield.jsp

5) Change the following properties of /apps/imagemultifield/imagemultifield

                     componentGroup - My Components
                     jcr:title - Image MultiField Component

6) Add the following to dialog (/apps/imagemultifield/imagemultifield/dialog) xml



<?xml version="1.0" encoding="UTF-8"?>
    jcr:primaryType="cq:Dialog"
    activeTab="{Long}0"
    title="Multi Image"
    xtype="tabpanel">
    <items jcr:primaryType="cq:WidgetCollection">
        <basic
            jcr:primaryType="cq:Widget"
            title="Images"
            xtype="panel">
            <items jcr:primaryType="cq:WidgetCollection">
                <images
                    jcr:primaryType="cq:Widget"
                    border="false"
                    hideLabel="true"
                    name="./images"
                    xtype="imagemultifield">
                    <fieldConfig
                        jcr:primaryType="cq:Widget"
                        border="false"
                        hideLabel="true"
                        layout="form"
                        padding="10px 0 0 100px"
                        xtype="imagemultifieldpanel">
                        <items jcr:primaryType="cq:WidgetCollection">
                            <image
                                jcr:primaryType="cq:Widget"
                                cropParameter="./imageCrop"
                                ddGroups="[media]"
                                fileNameParameter="./imageName"
                                fileReferenceParameter="./imageReference"
                                height="250"
                                mapParameter="./imageMap"
                                name="./image"
                                rotateParameter="./imageRotate"
                                sizeLimit="100"
                                xtype="imagemultifieldsmartimage"/>
                        </items>
                    </fieldConfig>
                </images>
            </items>
        </basic>
    </items>
</jcr:root>



Add JS Logic and Register XTypes


1) Create node /apps/imagemultifield/imagemultifield/clientlib of type cq:ClientLibraryFolder and add the following properties

             categories - String - cq.widgets

2) Create file (type nt:file) /apps/imagemultifield/imagemultifield/clientlib/js.txt and add the following

              imagemultifield.js

3) Create file (type nt:file) /apps/imagemultifield/imagemultifield/clientlib/imagemultifield.js and add the following code




CQ.Ext.ns("ImageMultiField");
 
ImageMultiField.Panel = CQ.Ext.extend(CQ.Ext.Panel, {
    initComponent: function () {
        ImageMultiField.Panel.superclass.initComponent.call(this);
 
        var multifield = this.findParentByType('imagemultifield');
        var image = this.find('xtype', 'imagemultifieldsmartimage')[0];
 
        var imageName = multifield.nextImageName;
 
        if(!imageName){
            imageName = image.name;
 
            if(!imageName){
                imageName = "demo";
            }else if(imageName.indexOf("./") == 0){
                imageName = imageName.substr(2); //get rid of ./
            }
 
            var suffix = multifield.nextImageNum = multifield.nextImageNum + 1;
            imageName = this.name + "/" + imageName + "-" + suffix;
        }
 
        image.name = imageName;
 
        var changeParams = ["cropParameter", "fileNameParameter","fileReferenceParameter",
                                "mapParameter","rotateParameter" ];
 
        CQ.Ext.each(changeParams, function(cItem){
            if(image[cItem]){
                image[cItem] = imageName + "/" +
                    ( image[cItem].indexOf("./") == 0 ? image[cItem].substr(2) : image[cItem]);
            }
        });
 
        CQ.Ext.each(image.imageToolDefs, function(toolDef){
            toolDef.transferFieldName = imageName + toolDef.transferFieldName.substr(1);
            toolDef.transferField.name = toolDef.transferFieldName;
        });
    },
 
    setValue: function (record) {
        var multifield = this.findParentByType('imagemultifield');
        var image = this.find('xtype', 'imagemultifieldsmartimage')[0];
 
        var recCopy = CQ.Util.copyObject(record);
 
        var imagePath = multifield.path + "/" + image.name;
        var imgRec = recCopy.get(image.name);
 
        for(var x in imgRec){
            if(imgRec.hasOwnProperty(x)){
                recCopy.data[x] = imgRec[x];
            }
        }
 
        recCopy.data[this.name.substr(2)] = undefined;
 
        var fileRefParam = image.fileReferenceParameter;
        image.fileReferenceParameter = fileRefParam.substr(fileRefParam.lastIndexOf("/") + 1);
 
        image.processRecord(recCopy, imagePath);
        image.fileReferenceParameter = fileRefParam;
    },
 
    validate: function(){
        return true;
    }
});
 
CQ.Ext.reg("imagemultifieldpanel", ImageMultiField.Panel);
 
ImageMultiField.SmartImage = CQ.Ext.extend(CQ.html5.form.SmartImage, {
    syncFormElements: function() {
        if(!this.fileNameField.getEl().dom){
            return;
        }
 
        ImageMultiField.SmartImage.superclass.syncFormElements.call(this);
    } ,
 
    afterRender: function() {
        ImageMultiField.SmartImage.superclass.afterRender.call(this);
 
        var dialog = this.findParentByType('dialog');
        var target = this.dropTargets[0];
 
        if (dialog && dialog.el && target.highlight) {
            var dialogZIndex = parseInt(dialog.el.getStyle("z-index"), 10);
 
            if (!isNaN(dialogZIndex)) {
                target.highlight.zIndex = dialogZIndex + 1;
            }
        }
 
        var multifield = this.findParentByType('multifield');
        multifield.dropTargets.push(target);
 
        this.dropTargets = undefined;
    }
});
 
CQ.Ext.reg('imagemultifieldsmartimage', ImageMultiField.SmartImage);
 
CQ.Ext.override(CQ.form.SmartImage.ImagePanel, {
    addCanvasClass: function(clazz) {
        var imageCanvas = CQ.Ext.get(this.imageCanvas);
 
        if(imageCanvas){
            imageCanvas.addClass(clazz);
        }
    },
 
    removeCanvasClass: function(clazz) {
        var imageCanvas = CQ.Ext.get(this.imageCanvas);
 
        if(imageCanvas){
            imageCanvas.removeClass(clazz);
        }
    }
});
 
CQ.Ext.override(CQ.form.SmartImage.Tool, {
    processRecord: function(record) {
        var iniValue = record.get(this.transferFieldName);
 
        if(!iniValue && ( this.transferFieldName.indexOf("/") !== -1 )){
            iniValue = record.get(this.transferFieldName.substr(this.transferFieldName.lastIndexOf("/") + 1));
        }
 
        if (iniValue == null) {
            iniValue = "";
        }
 
        this.initialValue = iniValue;
    }
});
 
CQ.Ext.override(CQ.form.MultiField.Item, {
    reorder: function(item) {
        if(item.field && item.field.xtype == "imagemultifieldpanel"){
            var c = this.ownerCt;
            var iIndex = c.items.indexOf(item);
            var tIndex = c.items.indexOf(this);
 
            if(iIndex < tIndex){ //user clicked up
                c.insert(c.items.indexOf(item), this);
                this.getEl().insertBefore(item.getEl());
            }else{//user clicked down
                c.insert(c.items.indexOf(this), item);
                this.getEl().insertAfter(item.getEl());
            }
 
            c.doLayout();
        }else{
            var value = item.field.getValue();
            item.field.setValue(this.field.getValue());
            this.field.setValue(value);
        }
    }
});
 
ImageMultiField.MultiField = CQ.Ext.extend(CQ.form.MultiField , {
    Record: CQ.data.SlingRecord.create([]),
    nextImageNum: 0,
    nextImageName: undefined,
 
    initComponent: function() {
        ImageMultiField.MultiField.superclass.initComponent.call(this);
 
        var imagesOrder = new CQ.Ext.form.Hidden({
            name: this.getName() + "/order"
        });
 
        this.add(imagesOrder);
 
        var dialog = this.findParentByType('dialog');
 
        dialog.on('beforesubmit', function(){
            var imagesInOrder = this.find('xtype','imagemultifieldsmartimage');
            var order = [];
 
            CQ.Ext.each(imagesInOrder , function(image){
                order.push(image.name.substr(image.name.lastIndexOf("/") + 1))
            });
 
            imagesOrder.setValue(JSON.stringify(order));
        },this);
 
        this.dropTargets = [];
    },
 
    addItem: function(value){
        if(!value){
            value = new this.Record({},{});
        }
        ImageMultiField.MultiField.superclass.addItem.call(this, value);
    },
 
    processRecord: function(record, path) {
        if (this.fireEvent('beforeloadcontent', this, record, path) !== false) {
            this.items.each(function(item) {
                if(item.field && item.field.xtype == "imagemultifieldpanel"){
                    this.remove(item, true);
                }
            }, this);
 
            var images = record.get(this.getName());
            this.nextImageNum = 0;
 
            if (images) {
                var oName = this.getName() + "/order";
                var oValue = record.get(oName) ? record.get(oName) : "";
 
                var iNames = JSON.parse(oValue);
                var highNum, val;
 
                CQ.Ext.each(iNames, function(iName){
                    val = parseInt(iName.substr(iName.indexOf("-") + 1));
 
                    if(!highNum || highNum < val){
                        highNum = val;
                    }
 
                    this.nextImageName = this.getName() + "/" + iName;
                    this.addItem(record);
                }, this);
 
                this.nextImageNum = highNum;
            }
 
            this.nextImageName = undefined;
 
            this.fireEvent('loadcontent', this, record, path);
        }
    }
});
 
CQ.Ext.reg('imagemultifield', ImageMultiField.MultiField);

Rendering Images


1) Images created in the CRX using MultiImage componet are rendered using /apps/imagemultifield/imagemultifield/imagemultifield.jsp. Add the following code in jsp
<%@include file="/libs/foundation/global.jsp"%>
 
<%@ page import="java.util.Iterator" %>
<%@ page import="com.day.cq.wcm.foundation.Image" %>
<%@ page import="org.apache.sling.commons.json.JSONArray" %>
 
<%
    Iterator<Resource> children = resource.listChildren();
 
    if(!children.hasNext()){
%>
 
        Click here to add images
 
<%
    }else{
        Resource imagesResource = children.next();
        ValueMap map = imagesResource.adaptTo(ValueMap.class);
        String order = map.get("order", String.class);
 
        Image img = null; String src = null;
        JSONArray array = new JSONArray(order);
 
        for(int i = 0; i < array.length(); i++){
            img = new Image(resource);
            img.setItemName(Image.PN_REFERENCE, "imageReference");
            img.setSuffix(String.valueOf(array.get(i)));
            img.setSelector("img");
 
            src = img.getSrc();
%>
            <img src='<%=src%>'/>
<%
        }
    }
%>