Wednesday, February 22, 2017

How to exclude the replication agent from manual replication actions - Adobe CQ5/AEM?

Exclude the replication agent from manual replication actions- Sometimes we may required to exclude the specific replication agents from manual action but only the actions through workflow or API.

Enable "Ignore Default" in Triggers tab of Agent configuration wizard.





Monday, February 20, 2017

How to display the dynamic popup in a page through GTM?

This post will explain how to display the dynamic popup in a page through GTM.

The assumption is the required JQuery scripts are included as part of the page.

Define the cookie type variable: - this will help us to display the popup for every new browser session(this step is not required if the popup should displayed every time visiting the page)

  • Click on Variable in left hand side menu.
  • Go to user defined variables and select new.
  • Select variable configuration as 1st party cookie 
  • Give cookie name as popUpDisplayed and check url decode cookie option.
  • Save the changes and give the name as popUpDisplayed.



Creation of Trigger/Timer:

  • Click on trigger on left hand side menu and choose trigger type as Timer.
  • In the Trigger configuration give the interval in milliseconds as required .(e.g:5000 millliseconds) - the delay to display the popup in the page
  • Enter the Trigger conditions as below:
                  urlpath contains /es/test.html
                  popUpDisplayed  does not equal true - this will make sure the popup is displayed once per                  browser session
  • Select this trigger fires on all timers.
  • Click on save and give the name accordingly(e.g:popuptimer)




Creation of Tag:

Click on tag on left hand side menu and choose tag type as customHTML.

In the HTML text area give the below content:
<!-- The Modal -->
<div id="myModal" class="modal">

<!-- Modal content -->
<div class="modal-content">
<span id="modal-close" class="close">&times;</span>
    test popup message
</div>
</div>
<script>
  $('#myModal').show();
  $('#myModal').parent().css('display','block');
  $('#myModal').parent().css('visibility','visible');
  $('#modal-close').click(function(){
  $('#myModal').hide();
  });
 document.cookie = "popUpDIsplayed=true";  
</script>

<style>
/* The Modal (background) */
.modal {
    display: none; /* Hidden by default */
    position: fixed; /* Stay in place */
    z-index: 1; /* Sit on top */
    padding-top: 100px; /* Location of the box */
    left: 0;
    top: 0;
    width: 100%; /* Full width */
    height: 100%; /* Full height */
    overflow: auto; /* Enable scroll if needed */
    background-color: rgb(0,0,0); /* Fallback color */
    background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
    background-image: url("paper.gif");/*change the background image */
  }

/* Modal Content */
.modal-content {
    background-color: #fefefe;
    margin: auto;
    padding: 20px;
    border: 1px solid #888;
    width: 60%;
}

/* The Close Button */
.close {
    color: #aaaaaa;
    float: right;
    font-size: 28px;
    font-weight: bold;
}

.close:hover,
.close:focus {
    color: #000;
    text-decoration: none;
    cursor: pointer;
}
</style>

Expand the advanced settings and check Enable custom tag firing schedule flag.Give the start date, time, end date and time as required. - This will help us to display the popup during purticular time
Click on save and give the name accordingly (e.g:popUpHtml).





Tuesday, February 7, 2017

How to monitor the Replication Queues through Java - Adobe CQ5/AEM

Monitoring the Replication Queues through Java - Adobe CQ5/AEM

This post will explain the approach to monitor the Replication Queues through java

Enable the Remote JMX in the server:

Add the following configurations as part of CQ_JVM_OPTS in AEM startup file (start.sh or start.bat)

-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=50055

e.g.

CQ_JVM_OPTS='-server -Xmx6144m -XX:MaxPermSize=512M -Djava.awt.headless=true -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=50055'

Restart the server.

Configuration.txt

ServerHost=xxxxxxxxxxx
ServerPort=50055
QueueNames=publish,publish_Dev
Environment=Development
EmailHost=xxxxxx
Email_To=xxxxxx
Email_From=xxxxxxx
QueueThreshold=15

GetReplicationQueueStatus.java

import java.io.*;
import java.util.*;
import javax.management.*;
import javax.management.remote.*;
import javax.mail.*;
import javax.mail.internet.*;

public class GetReplicationQueueStatus {
static JMXConnector m_connector=null;
public static void main(String[] args) {
FileInputStream in=null;
try
{
Properties configuration = new Properties();
in= new FileInputStream("Configuration.txt");
configuration.load(in);
String serverHost=configuration.getProperty("ServerHost");
String serverUrl = "service:jmx:rmi:///jndi/rmi://"+serverHost+":"+configuration.getProperty("ServerPort")+"/jmxrmi";
JMXServiceURL serviceURL = new JMXServiceURL(serverUrl);
m_connector =JMXConnectorFactory.newJMXConnector(serviceURL,null);
m_connector.connect();
MBeanServerConnection m_connection = m_connector.getMBeanServerConnection();
Set<ObjectName> queryResult =m_connection.queryNames(new ObjectName("com.adobe.granite.replication:type=agent,*"),null);
String queueNames=configuration.getProperty("QueueNames");
List<String> queueNameList = Arrays.asList(queueNames.split(","));
for(ObjectName objectName : queryResult) {
if(queueNameList.contains(objectName.getKeyProperty("id").replaceAll("\"", "")))
{
String  attrNames[] = 
                   { "Enabled",
                     "QueueNumEntries",
                     "QueueBlocked",
                     "QueuePaused"                      
                   };
AttributeList  attrList = m_connection.getAttributes(objectName, attrNames);                
           String isEnabled= ((Object) attrList.get(0)).toString().split("=")[1];
           String queueNumEntries= ((Object) attrList.get(1)).toString().split("=")[1];
           String isQueueBlocked= ((Object) attrList.get(2)).toString().split("=")[1];
           String isQueuePaused= ((Object) attrList.get(3)).toString().split("=")[1];
           System.out.println( "Values: " +isEnabled+" "+ queueNumEntries+" "+isQueueBlocked+" "+ isQueuePaused);
           String to=configuration.getProperty("Email_To");
           String from=configuration.getProperty("Email_From");
           String emailHost=configuration.getProperty("EmailHost");
           String queueThreshold=configuration.getProperty("QueueThreshold");
           String environment=configuration.getProperty("Environment");
           if(!isEnabled.trim().equals("true"))
           {
            String subject=environment+": Replication Queue "+"\""+objectName.getKeyProperty("id").replaceAll("\"", "")+"\""+" is Disabled";
            sendEmail(to,from, subject,subject+"\n"+"Server: "+serverHost,  emailHost);
           }else if(isQueueBlocked.trim().equals("true"))
           {
            String subject=environment+": Replication Queue "+"\""+objectName.getKeyProperty("id").replaceAll("\"", "")+"\""+" is Blocked";
            sendEmail(to,from, subject,subject+"\n"+"Server: "+serverHost,  emailHost);
           }else if(isQueuePaused.trim().equals("true"))
           {
            String subject=environment+": Replication Queue "+"\""+objectName.getKeyProperty("id").replaceAll("\"", "")+"\""+" is Paused";
            sendEmail(to,from, subject,subject+"\n"+"Server: "+serverHost,  emailHost);
           }else if(Integer.parseInt(queueNumEntries.trim())>Integer.parseInt(queueThreshold.trim()))
           {
            String subject=environment+": Replication Queue "+"\""+objectName.getKeyProperty("id").replaceAll("\"", "")+"\""+" is Queued with "+queueNumEntries+" Requests";
            sendEmail(to,from, subject,subject+"\n"+"Server: "+serverHost,  emailHost);
           }
               
}
                
}
}catch(Exception e)
{
e.printStackTrace();
}finally {
try {
if(m_connector!=null)
{
m_connector.close();
}
if(in!=null)
{
in.close();
}
}catch(Exception e)
{
}
}
}
static void sendEmail(String to,String from,String subject,String body,String host)
{
 Properties properties = System.getProperties();
     properties.setProperty("mail.smtp.host", host);
     Session session = Session.getDefaultInstance(properties);

     try {
          MimeMessage message = new MimeMessage(session);
        message.setFrom(new InternetAddress(from));
           message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
           message.setSubject(subject);         
           message.setText("Hi Team\n\n"+body+"\n\n"+"Regards\nAdmin");
           Transport.send(message);
           System.out.println("Email send successfully...");

     }catch (MessagingException mex) {
        mex.printStackTrace();
     }
}

}

Configure the data in Configuration.txt file accordingly.
Email will be triggered whenever the configured Queue is disabled, blocked, paused or the pending request is more than the configured threshold.

Executable jar file can be generated for the java class and scheduled for continuous monitoring.



Saturday, August 20, 2016

How to invalidate the dispatcher cache for dam assets referred from multiple sites? - Adobe CQ5/AEM

How to invalidate the dispatcher cache for dam assets referred from multiple sites? - Adobe CQ5/AEM

Sometimes, we may have the scenario like the same DAM asset is referred from multiple sites and cached locally(site specific) in dispatcher for all the sites. The local cache of all the sites should be invalidated whenever the asset is modified.

This post will explain how to achieve the above mentioned scenario.

Create a user in all the publisher and provide only the access to DAM- /content/dam


Create a flush agent for individual sites with the following details in all the publishers(repeat the below step for all the sites)




Agent user Id: Specify the user created in the first step
URI : Specify the URL with dispatcher server name
HTTP headers : Specify the site name(DNS for the site) as Host and specify CQ-Action-Scope: ResorceOnly(this will inform the dispatcher to only invalidating the dam asset by deleting it and not touching the .stat file)

Now, whenever the asset is modified all the local cache will be invalidated.




Saturday, July 9, 2016

How to populate the current page path to the dialog through EXT JS - AEM 6.1

This post will explain, how to populate the current page path to the dialog through EXT JS - AEM 6.1

Add the listener for the widget and use the below script.

function(widget) {
        var currentPath;
if(CQ.wcm.SiteAdmin.hasListSelection()) {
        var grid = CQ.wcm.SiteAdmin.getActiveGrid();
        var selections = grid.getSelectionModel().getSelections();
currentPath = selections[0].id;
console.log(currentPath);
widget.setValue(currentPath)
}
}








Friday, June 24, 2016

Caused by: org.apache.sling.api.resource.PersistenceException: Resource is not modifiable - Adobe CQ5/AEM

The below exception will be thrown most of the cases while accessing the servlet with post from AEM.

The actual issue is AEM could not ale to resolve the servlet and assumes it as a resource path but this is not a valid resource path, the below exception will be thrown when AEM tries to set the property values.

Caused by: org.apache.sling.api.resource.PersistenceException: Resource at '/services/formservlet' is not modifiable.
    at org.apache.sling.servlets.post.impl.helper.SlingPropertyValueHandler.setProperty(SlingPropertyValueHandler.java:153)
    at org.apache.sling.servlets.post.impl.operations.ModifyOperation.writeContent(ModifyOperation.java:411)
    at org.apache.sling.servlets.post.impl.operations.ModifyOperation.doRun(ModifyOperation.java:101)

The steps to narrow down the issue:

Resolve the Servlet:

Go to http://<<AEM Host>>:4503/system/console/configMgr - Sling--> Sling Servlet Resolver and resolve the servlet path, if the servlet path is resolved then there is no issue if not the servlet is not configured properly.

Path resolved to a Servlet


Path not resolved a Servlet

Verify the Servlet configuration:

Cross verify and make sure the servlet is configured properly

e.g servlet

@Component(
immediate = true,
label = "Servlet",
description = "Servlet",
metatype = true)
@SlingServlet(
name = "FormServlet",
description = "FormServlet",
methods = "POST",
generateComponent = false,
paths = "/services/formservlet")
@Service(value = Servlet.class)
public class FormServlet extends SlingAllMethodsServlet implements Serializable {

@Override
protected void doPost(SlingHttpServletRequest request,
SlingHttpServletResponse response) throws ServletException,
IOException {
        }
protected void doGet(SlingHttpServletRequest request,
SlingHttpServletResponse response) throws ServletException,
IOException {
doPost(request, response);
}

}

Verify the servlet resolver configuration:

Go to http://<<AEM Host>>:4503/system/console/configMgr - Sling--> Apache Sling Servlet/Script Resolver and Error Handler and make sure the servlet base patch is added under Execution paths.


Apache Sling Referrer Filter:

If the servlet is invoked externally then make sure the host name of the source system is added to the Allow Hosts section of the Apache Sling Referrer Filter


CSRF Filter(above AEM 6.1):

Adobe added CSRF (Cross-Site Request Forgery) protection with version 6.1 and we need to ensure that the CSRF token is included while submiting the form.

This should be handled automatically if we are using AEM's version of jQuery. This library has the code to get the token and add it to all XHR and forms.

If we are using a different version of jQuery or not using the jQuery, then we have to include the 'granite.csrf.standalone' client library and it will do the same functionality.

Refer the following link for more details - https://helpx.adobe.com/experience-manager/using/custom-sling-servlets6_1.html


Also, if the servlet is working properly from publisher/author and not only working from dispatcher then the issue might be with some of the cached scripts in dispatcher is not updated, try to clear the dispatcher cache for JS scripts and try the scenario again.




Wednesday, June 22, 2016

Dispatcher Cache Invalidation for Multi Site Configuration - Adobe CQ5(AEM)

This post will explain the approach to invalidate the cache for particular site in multi site configuration - Adobe CQ5(AEM).

Recently, i was working on a dispatcher caching issue with multi site configuration - The cache was not getting invalidated in all the dispatcher.

The setup is with multiple dispatcher connected via load balancer. The load balancer will route the request to individual dispatchers via round robin algorithm. The dispatcher is configured with individual virtual host and farm for every site(Load balanced DNS is used).

The flush agent was configured in publisher for every site with load balanced DNS(the flush agent was equal to number of sites configured in dispatcher) - The issue here is when ever publisher send the invalidation request that is not reaching the corresponding dispatcher as the flush agent is configured with load balanced DNS for invalidation URI - The request will be send to random dispatcher from the load balancer due to this the cache will not be invalidated in all the dispatcher.

Solution:

Create a flush agent in individual publisher that points to individual dispatcher(based on the renders configuration). The flush agent should be configured with dispatchers server name(one flush agent in one publisher)

http://dispatcher1.server.name:80/dispatcher/invalidate.cache


Make sure the following HTTP Headers are configured.


The flush agent can be configured in author also, if we are configuring in author then the number of flush agents should be equal to number of dispatchers.

Create a Location Match in all the dispatchers httpd.con file for all the websites.

<LocationMatch "^/dispatcher/invalidate.cache$">
 
    # Site Finance
    SetEnvIfNoCase CQ-Path ".*/content/sample1/.*" FLUSH_HOST=sample1.com
    RequestHeader set Host %{FLUSH_HOST}e env=FLUSH_HOST

    # Site TR
    SetEnvIfNoCase CQ-Path ".*/content/sample2/.*" FLUSH_HOST=sample2.com
    RequestHeader set Host %{FLUSH_HOST}e env=FLUSH_HOST
</LocationMatch>

Based on the CQ-Path the corresponding sites cache will be invalidated.

Referred from  - http://www.netcentric.biz/blog/2016/01/aem-dispatcher-cache-invalidation-for-multiple-dispatcher-farms.html



Contact Form

Name

Email *

Message *