Ad Code

Sunday, January 31, 2021

CRX Cleaner and IntelliVault Plugins for AEM

 I have always been fond of plugins and tools which makes my life easier. In this blog, I will talk about two of my favorite plugins in IntellijIdea which makes my AEM development easier: CRX Cleaner and IntelliVault.

.

CRX Cleaner:  Recently a new plugin has been added in my development life which is CRX Cleaner. This is an IntellijIdea Plugin and absolutely free.


Use Case: In AEM, while syncing the content, the syncing of workflows, templates, policies or sometimes pages also need to be done from AEM to the codebase repository.


While syncing via packages/ plugin, it pulls all the unwanted properties like jcr:modifiedBy, jcr:modifiedDate, jcr:createdBy etc in the code base and makes git pushes really messy. Before this plugin, either I just avoided these properties and pushed the code having these properties or I used to clean up by myself one by one which was a super tedious task.


I installed this plugin in intellijidea, and now just one short key Ctrl+ALT+L does the magic for me. You just select the whole file and hit the short key. Super easy and handy.

How to install: Go to File-> Plugins-> search for CRX Cleaner-> Install


IntelliVault

I believe most of the people working in AEM are aware about this plugin, but for those that aren’t I highly recommend it.

Use Case: This plugin helps to sync the content from AEM to code repository or from repository to AEM.

Many times you just keep on changing the crx at so many places. So while moving the changes to the code repo, either make the package of all changes from CRX or sync the content with the help of this plugin.


To make this tool work, vault-cli dependency is needed. vault-cli zip initially used to come with the AEM Jar and it was easy to configure it, but it was removed (/crx-quickstart/opt/fileVault) in the later versions of AEM. 


So I need to find the vault-cli.zip and do the configuration. As there are many versions of vault-cli and I noticed for some versions, the syncing doesn’t work sometimes, This additional task always makes me lazy and I end up making packages to sync my code instead of making this tool work.


So If you are lazy like me, just click the link https://repo1.maven.org/maven2/org/apache/jackrabbit/vault/vault-cli/3.4.6/vault-cli-3.4.6-bin.zip  to download the vault zip and extract it to some place. This vault version works for me perfectly.


Now install the fileVault Plugin in IntellijIdea and after you install this, the plugin will start appearing in the Tools section. Click on FileVault and configure the Vault directory path pointing to the bin folder following the path where you extracted your zip, i.e., “ROOT_DIRECTORY\vault-cli-3.4.6-bin\vault-cli-3.4.6\bin”

When done with the configuration, you can start pushing and pulling the content.


Hope you find it a good read.


Sunday, October 25, 2020

Register Sling Servlet or Any Service Dynamically in AEM

 Hello Everyone,

While working on a project last week, I went through a very interesting use case and so many learnings while solving that. This blog is shared with you about that learning process I have been through.


Problem Statement: I was working on an integration tool with AEM, in which we are having many servlets registered with paths . Few of the servlets are being called by a third party and few are for internal calls.


All the servlets start with a specific prefix. Let’s suppose: /bin/aem-integration/events, /bin/aem-integration/tasks etc. So the prefix for all the servlets is "/bin/aem-integration" and it was as a constant in the code.


But while using that tool, a client requested to provide them the flexibility to define the prefix as per their requirement. So that, while they made calls from any external system to AEM, they could make their own choice of prefix or may be a very environment specific prefix like for dev it will be /bin/aem-integration/dev/tasks but for stage it will be /bin/aem-integration/stage/tasks. The requirement looks easier but while implementing, it was quite challenging.


How I approach this problem:

So if I mention the above problem precisely, it is registering the servlets based on dynamic paths.


How can we do that? 

Let’s suppose I have a servlet "RunModeServlet" using SlingSettings as a Reference and I want to register this servlet dynamically. So how to do that.



1.First I create a service "ServletRegistration" having an OSGI Configuration asking for Endpoint Prefix Configuration, because every service is a component, so on the activate method of the component we need to register all the servlets dynamically  and unregister and re-register with new values, if the author makes any change in configuration.



Now if you are creating reference of a component via new RunModeServlet() and if in the RunModeServlet is using any @Reference annotation, all the services injections will be null. To solve this, pass the service reference via the constructor to the servlet like shown in the code.


Note: In this case in the ServletRegistration class need to have all the service injections using @Reference annotation and need to pass to the servlets via the constructor new RunModeServlet(slingSettings).


2. In the RunModeServlet, we remove all the annotations and get the services via the constructor in place of @Reference annotation.


Note: This approach works well but if you are registering many servlets dynamically, you always need to manage all the constructors for every servlet and pass the service reference from ServletRegistration OSGi service. To see so many service injections in this service (ServletRegistration ) can be overwhelming.

Is there any alternative way to handle it?
The answer is yes.
Use a componentFactory to create instances of a component.
[Preferred Approach]

1. Use this on the component/servlet you want to create programmatically:

@Component(factory= "aem.servlet.runModeServlet")

public class RunModeServlet extends SlingAllMethodsServlet {


 @Reference

   private SlingSettingsService slingSettingsService;

@Override

protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response){

}

}


2. Then in another component/service (ServletRegistration) you can get the ComponentFactory:

@Reference(target = "(component.factory=aem.servlet.runModeServlet)")

private ComponentFactory runModeServletFactory;


3. Create an instance from it and register the servlet like this.

Dictionary<String, Object> propertiesMap = getPropertiesMap(runModeServletPaths, "json", "GET");

RunModeServlet runModeServlet = (RunModeServlet) runModeServletFactory.newInstance(null).getInstance();

bundleContext.registerService(Servlet.class, runModeServlet, propertiesMap);



Note: The way we have registered a servlet, you can register any service, filter or any authenticationHandler dynamically.

Dictionary<String, Object> propertiesMap = new Hashtable<>();

propertiesMap.put(AuthenticationHandler.PATH_PROPERTY, new String[]{endpoint_prefix, DamConstants.MOUNTPOINT_ASSETS});

propertiesMap.put(Constants.SERVICE_RANKING, 100000);

propertiesMap.put(Constants.SERVICE_DESCRIPTION, "Custom – Authentication Handler");

bundleContext.registerService(AuthenticationHandler.class, customAuthenticationHandler, propertiesMap);


I hope you find it interesting and useful.


Thanks and Happy sharing.


Tuesday, September 15, 2020

How to Use Encrypted Password for OOTB Configurations

Hello Everyone, 

It’s been so long since I wrote something. So here is a new blog about some issues I faced
recently and what are my leanings from there.

1. Encrypted Password for OOTB Configurations: We always talk about making
passwords in an encrypted value as part of the code base to secure the password.
To tackle this issue, we always talk about CryptoSupport in AEM. So basically if there is a custom configuration and a need to use an encrypted password, I can use the concept of cryptosupport in AEM.

But there are few OSGi Configurations in AEM like “JDBC ODBC Connection Pool” or “Adobe Granite SAML 2.0 Authentication Handler” having password fields, So can we use cryptosupport concept here too??

So the answer is yes, you can keep encrypted value in the “password fields”  directly in these configurations and for the decryption part, you don’t need to worry. AEM will take care of it.To encrypt a password, go through the cryptosupport console in AEM.

Conclusion: We need to understand this very clearly that if we define a field as "Password type" either in OOTB or custom configurations, we don’t need to worry about decryption part, just configure the encrypted value in the field and AEM will understand that because it is a password field, it may need to decrypt it.
But if your field is a plain text, then you have to take care of the decryption part as mentioned in the previous post.
Fig1: For a password field, no need to write logic for decryption.

2. AEM SAML Configuration in the Code Repository Issue: Recently while working on a project, I configured AEM SAML integration on the author instance and it just works fine. Now this was the time to put the config in the code repository. So,

a) I put that config in the code repository (at /apps/<project-folder>/config.author.dev/com.adobe.granite.auth.saml.SamlAuthenticationHandler-myproject.xml

b) Deleted the manually created SAML configuration in Felix Console and performed a build and I was able to see the SAML config added by code.

c) I hit the Author URL and it doesn't take me to the IDP login page.
d) I go to felix, open SAML config, click Save (without touching anything else) and I hit Author URL again and now it takes me to the IDP login page.

Exactly the same issue I found on Adobe Community and the solution work for me.

Solution:
a) Name the file as: "com.adobe.granite.auth.saml.SamlAuthenticationHandler-<Project_Identifier>.config" (Make sure it is a regular file in Eclipse or IntelliJ); no need to add extension ".xml" at the end.
b) Inside this file, just add the configuration like a regular text. Please see below:
# Configuration created by Apache Sling JCR Installer
// Storing keyStorePassword as an encrypted manner.
keyStorePassword="{41bdcd34d9a34ae1c68bafa6b7b647443c429ad97e00a9f2cb5f876b2433}"
service.ranking=I"5002"
idpHttpRedirect=B"false"
createUser=B"true"
defaultRedirectUrl="/content/project/en/aem-assets.html"
userIDAttribute="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
assertionConsumerServiceURL=""
defaultGroups=["contributors"]
signatureMethod="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
idpCertAlias="certalias___xxxxxxxxxxxx"
addGroupMemberships=B"true"
path=["/content/projects"]
digestMethod="http://www.w3.org/2001/04/xmlenc#sha256"
synchronizeAttributes=""
clockTolerance=I"60"
groupMembershipAttribute="groupMembership"
idpUrl="IDP URL GOES HERE"
logoutUrl="logouturl"
serviceProviderEntityId="service_provider_entity_id"
handleLogout=B"true"
spPrivateKeyAlias=""
useEncryption=B"false"
nameIdFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"

You can also copy the content of the file from CRXDE, after you manually save the OSGi config thru /system/console/configMgr.

Note: This solution really worked for me,but logically it didn’t make any sense to me as while working on another project on AEM 6.5.4, the xml file used to work for me. I face this issue only  in AEM 6.5.5 till now, So can’t really comment if this is an upgrade issue or something else.
So,If you face this issue in any of the AEM versions, you can directly try this solution.

I hope you find it a good read.Thanks and Happy Learning.

Wednesday, April 22, 2020

AEM Workflow: Request for Deletion Workflow in AEM

Hello Everyone,

Request for Deletion is an AEM OOTB Workflow. In this blog, we will talk about,
1. What is a request for deletion workflow?
2. How it works?
3. What are challenges with this workflow?

Request for Deletion Workflow: This workflow gets initiated when the content-authors don’t have replicate permissions and try to delete a page.

Request for Deletion OOTB workflow has four steps:

Fig1: OOTB Request for Deletion Workflow 

Step1: When an author delete a page, "Request for Deletion" step gets executed and it initiates a notification to the administrators by default (you can change it to approver group).
Step2: When the approver approves the request, the page gets deactivated from the publisher.
Step3: Again a request goes to the administrator or content-approvers to take the approval to delete the page in the author instance.
Step4: When the request gets approved, the page gets deleted from the author server.

Product Bug: When the author deletes the page, a request for deletion notification is visible to the approver inbox, but after the approver approves the request and workflow completes deactivation of page step, the workflow itself aborted after step2.

Because of this issue, the page is getting deactivated from publish but the page is not getting deleted from author CRXDE and because of that all the pages authors deleted in author instance are not visible in Sites console but still sitting in CRXDE.
Fig2: Request for Deletion workflow history to show aborted scenario
I raised it as a day care and this is what they replied.
Solution: “I looked at the code that gets triggered in this event (an event handler). What it actually does is, trigger a Request for deletion workflow with system user wcm-workflow-service, when the user deleting the page does not have enough permission.
Now if you provide delete and replicate permission to that user "wcm-workflow-service" at the /content level the workflow is getting completed successfully.
Always remember that Request for deletion workflow has two participant steps, so you need to approve twice, once for deactivating the page, then for the deletion of the resource step.”

And the above mentioned solution worked for me.
Fig2: Request for Deletion workflow history after fixing permissions
Note: I face the above issue in AEM 6.5.2, so I am not sure what all AEM version has this
issue.So check accordingly.

Client Concern:
Deletion is a very crucial activity which can lead to loss of content/pages if done unintentionally.
That’s why AEM provided approvals at two levels. But in our project, client concern was
that it’s quite annoying for the content-authors, when they just delete a unpublished page
from the author, for this also it asks for two level approvals.

So here we removed the second level approval by considering that if approver approved
at the first step, it means that he really doesn't want that page/content in author as well as
publish.
And i think it really make sense too as it is very difficult to manage so many approvals also.

Fig4:  Workflow model after removing the approval step
Note: Always remember, before you hand over the production server to content-authors, always decide the workflow for request for deletion, make the authors aware about it and test it once because after authors start authoring, and you face this kind of product bug, where pages still exist in author after deletion, you have to fix them by going to the crx and this is hectic task also for a developer and authors.

I hope you find it a good read.Thanks and Happy Learning.

Tuesday, April 21, 2020

Troubleshooting in AEM with real time use cases


Hello Everyone,

In this blog, I will talk about some of the real time issues, I faced while working on a project.
Here we will talk about the problem statement and how to tackle these problems.

1. The multiple PDFs were not able to move to a specific location:  In AEM, ideally you can select multiple PDF’s and move to a specific location. While working on a project in AEM 6.5.2, The authors in this project were able to move a PDF to a certain location but they were not able to move multiple PDFs in one go.

When I found the issue, I checked the console and realized that the OOTB JS
(/libs/dam/gui/coral/components/admin/moveassetwizard/clientlibs.js) is breaking.

I raised this as a Product Bug to Day Care and below is the solution which they provide me.
“This is an issue with 6.5.1 and 6.5.2 which should be fixed in 6.5.3 release”.

Workaround:
The present workaround is to replace line 251 at
/libs/dam/gui/coral/components/admin/moveassetwizard/clientlibs/js/wizard.js
with the below code.
Old code snippet:
var newName = $(".rename-item-name").val().toLowerCase();

New code Snippet:
var newName = $(".rename-item-name").val();

you can create an overlay for the above mentioned JS file and later uninstall it when you plan to install 6.5.3.

2. While using reference components, the authors are not able to decide which component they need to refer in the pathbrowser.

There is a real time scenario where we need to use the same component in several pages and for that particular use case reference component is very helpful. 

Let's suppose we are having some tables which are being shown in many pages. Now suppose the author created a page "P" to put all the tables and try to reference the tables in actual pages from that centralized page.

Now as the page "P" is having so many table components, so while using reference
components to point to a specific table, it is difficult to identify by the content-authors which table they need to point, because in the pathbrowser, the component node name is being shown.

Fig1: Reference Component dialog for choosing a table component

While debugging out the pathbrowser behavior, I figured out that in
“/libs/granite/ui/components/foundation/form/pathbrowser/render.jsp”, line No 42,
the pathbrowser shows the “jcr:title” of the resource value and if not available then shows
resource name.
Fig2: PathBrowser component render.jsp logic
So to solve this issue;  We can provided a title field with name property “jcr;title”
in table component so that if the authors can provide a “title” to the table to define what this
table is about, then in the pathbrowser, the title will be visible and authors are able to
point to the correct component while using reference component.

You can do it for all the components by making a generic tab, so that whenever authors
face this issue, they can immediately provide a “jcr:title” to the component.
Fig3: After providing jcr:title the view in pathbrowser in reference component


3. While login via a user which is part of "content-authrs" group, the “Modified By”

for Asset is visible as “External User” but not as the actual person name:
Usually to set up content-authors permission for a specific project, we take reference
from OOTB content-authors and then add more permissions based on the additional
hierarchies for the specific project.

But there can be some permissions which AEM OOTB content-authors don’t have but you need to provide them.
For the assets, content authors are not able to see who modified the image last, but can
see “External User” as ” Modified By”  for all the images.
Fig4: The "Modified By" as "Extenal User"  while logging from content-authors


Solution: I go through the code and found out that the column preview ‘s HTML is
getting rendered from here:
/libs/dam/gui/coral/components/admin/contentrenderer/column/columnpreview/columnpreview.jsp
Fig5: The logic from where modified by is visible in Assets columnnview


So basically to fetch the value of "Modified by" the code is trying to get the formatted Name which can only be accessible when the author has "/home/users" permission.
To solve this issue, you need to give read permission to “/home/users” hierarchy to content-authors and it will start showing the actual user who modified the assets last.
Fig6: After fixing the permission, the "Modified By" is showing "Actual User"

I hope you find it a good read.Thanks and Happy Learning.