Wednesday, November 6, 2024

Junit parallel execution Setup in pom.xml

 In maven based Java project, if your project has hundreds of Junit test cases it may take long time to run the application code build. To speed up build execution, it is recommended to setup your project to execute Junit Test in multi threaded parallel fashion. Below is sample code which you can put in your java module's pom.xml file:

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-surefire-plugin</artifactId>

<configuration>

    <parallel>All</parallel>

    <forkCount>2C</forkCount>

    <reuseForks>true</reuseForks>

    <useSystemClassLoader>false</useSystemClassLoader>

</configuration>

</plugin>


Refer official documentation link for more details.

Tuesday, November 5, 2024

Version Issue with Custom AssetMetadataPermissionProvider implementation

While implementing custom metadata based permission provider, you can follow this article to implement it. One issue I faced during this implementation that the version access was not working. To fix this modify below methods:

@Override

public TreePermission getTreePermission(Tree tree, TreePermission parentPermission) {

if (PermissionHelpers.isDamPath(tree) || PermissionHelpers.isDamAncestorPath(tree)) {

if (PermissionHelpers.findAncestorAsset(tree) != null) {

return PermissionHelpers.isAncestorAssetOwner(tree, principalNames) ?

             TreePermission.ALL : parentPermission;

} else {

return new EmptyAssetMetadataTreePermission(tree, TreeType.DEFAULT, this);

}

} else if (tree.getPath().startsWith("/" + JcrConstants.JCR_SYSTEM)) {

      // This condition added to allow version to path access

      // This is just an example code, optimize this condition before you use

return TreePermission.ALL;

}

return TreePermission.NO_RECOURSE;

}

Another method:

@Override

public boolean isGranted(Tree tree, PropertyState property, long permissions) {

TreeType type = treeTypeProvider.getType(tree);

switch (type) {

case HIDDEN:

return true;

case VERSION:

Tree evalTree = getEvaluationTree(tree);

if (evalTree == null) {

return false;

}

if (evalTree.exists()) {

return internalIsGranted(evalTree, property, permissions);

} else {

return false;

}

case INTERNAL:

return false;

default:

return internalIsGranted(tree, property, permissions);

}

} 

Core logic to test metadata conditions to meet business requirement is put in the below private method which is called in the above isGranted method.

private boolean internalIsGranted(@NotNull Tree tree, @Nullable PropertyState property,

   long permissions) {

boolean answer = false;

if (PermissionHelpers.isAncestorAssetOwner(tree, principalNames)) {

answer = true;

}

if (property != null) {

LOG.debug("isGranted: {}@{} ({}) = {}", tree.getPath(), property.getName(),

             permissions, answer);

} else {

LOG.debug("isGranted: {} ({}) = {}", tree.getPath(), permissions, answer);

}

return answer;

}

PermissionHelpers here is a general utility class like below:


Junit Mocking Guide

 1. Mock private member of a class

Example to mock a private member "treeTypeProvider" of class CustomProvider.java

Field field = CustomProvider.class.getDeclaredField("treeTypeProvider");

field.setAccessible(true);

field.set(provider, treeTypeProvider);


2. Mocking a Static class


Make sure you mock a Static class with a try-with-resources. Not doing so will affect Junit execution of other related classes using this Static class somewhere else in other Junit class.


try (MockedStatic<SampleUtils> utils = Mockito.mockStatic(SampleUtils.class)) {

utils.when(() -> SampleUtils.testMethod(node)).thenReturn(mockedNode);

......
......
 

}

3. Mocking object construction

Example to mock a construction statement like:

DocsProcessor processor = new DocsProcessor(); 

Mock it like below:

try (MockedConstruction< DocsProcessor > processor = Mockito.mockConstruction(DocsProcessor.class)) {

// Call any method that calls the DocsProcessor class constructor

......
......

        // Verify that constructor was called once 

        assertEquals(1, processor.constructed().size());

}

Thursday, April 27, 2023

AEMaaCS: Migration of Assets from Non-AEM sources | Using Bulk Import tool

 OOTB Bulk import tool facilitates a sophisticated content migration experience for non-AEM assets. This tool allows even non-tech users to import assets into AEM from cloud storage. To use this tool, you need all your source assets to be uploaded to a single cloud storage server i.e. Azure Blob Storage or Amazon S3, Google cloud storage etc. The process can be divided into 5 stages to simplify entire process:

1. AEM Pre-migration tasks- Like: Define/ Create base folder hierarchy, Define/ create taxonomy, Define/ Create Metadata Schema and apply those on migration folders.

2. Source Pre-migration tasks: Reorganize DAM hierarchy, Content cleanup, Delete unused/ test assets

3.  Upload assets to a cloud storage server i.e. Azure Blob Storage or Amazon S3. Bulk import tool ingests assets in AEM in the same structure that you put in cloud storage.

4. Configure and Run the Bulk Import

5. Prepare for the metadata import (Assuming that the assets ingested in step 3 did not contain necessary metadata added) and run metadata import in AEM

In order to configure Bulk Import which is an option available on AEMaaCS cloud servers (not available in local SDK) follow below:

a. Go to Tools -> Assets -> Bulk Import -> Create

b. Fill in necessary information to your cloud storage like- bucket id, access key, secret key/ token etc.



 c. Save the settings and click on "check" to test the connection. 


With this you set be all set to run the asset ingestion into AEM. You can even schedule it to tun at specific time as per business need. While configuring the import tool, you can also, configure how to handle existing assets in same location:


Refer this link for more information on this- https://experienceleague.adobe.com/docs/experience-manager-learn/cloud-service/migration/bulk-import.html

Monday, April 10, 2023

Enable SMTP host and port in AEM Cloud Service

AEM Cloud Service blocks communication to any external host and port by default. As SMTP run on external servers, it need to be allow listed.  Sample SMTP email configuration:

{
"smtp.host": "$[env:AEM_PROXY_HOST;default=proxy.tunnel]",
"smtp.port": 30587,
"smtp.user": "$[env:EMAIL_USERNAME]",
"smtp.password": "$[secret:EMAIL_PASSWORD]",
"from.address": "xxxx@xxxx.com",
"smtp.ssl": false,
"smtp.starttls": true,
"smtp.requiretls":true
}

This article just talks about setting up host and port of SMTP configuration, rest configuration should be done using OSGi configuration file and environment variables.

SMTP User Name and Password should be stored as environment variables as shown below:

SMTP host and port is set using advance networking option which is explained in this article below. Necessary configurations are done through the Advance Networking Options to your cloud service program. Follow below steps to do that: 

Step 1- Create a new project in Adobe I/O - . Inside that add an API integration -> select type as Cloud Manager. 

Step 2- Open the integration and click on Edit Product Profiles. Select appropriate access like below:

Step 3- In left menu, click on "Service Account (JWT). On this screen generate a public private key pair. Download the certificate details.

Step 4- Using this private key, "generate access token" as shown in Step 2.

Step 5- Use this access token to configure Advance Networking Options to your cloud service program and environments.

Step 6- You can use Postman to enable these network settings. The actions are in below sequence:
a. Figure out your server's primary region. Go to Cloud Manager -> Environment Details. This is needed to create primary network. Get region id from this API - API Link.

GET https://cloudmanager.adobe.io/api/program/{program-id}/regions


e.g. region id might be "va7" for East US.

b. Next thing check if Primary Network has been created or not. 

GET https://cloudmanager.adobe.io/api/program/{program-id}/networkInfrastructures

If network infrastructure has been created, you will see response like below:


c. If you don't see primary region network infrastructure in previous step then go ahead and create a new one using this - Create network infrastructure

POST https://cloudmanager.adobe.io/api/program/{program-id}/networkInfrastructures

Your post body should follow below format:

{
"kind": "flexiblePortEgress",
"region": "va7", #Set to value found in step 6#a
"isPrimaryRegion": true
} 

d. First check if advanced networking exists or not for your particular environment using this API.

GET https://cloudmanager.adobe.io/api/program/{program-id}/environment/{environment-id}/advancedNetworking
e. If networkingConfigurations is not configured or you do not see portForwards details in previous step d, then create a networkingConfiguration using Enable Environment Advanced Networking Configuration API.

PUT https://cloudmanager.adobe.io/api/program/{program-id}/environment/{environment-id}/advancedNetworking

Your post body should be in below format. Change smtp host and port values under portForwards as per your SMTP details. Sample Format:
{
"nonProxyHosts": [
"*.my-website.com"
],
"portForwards": [
{
"name": "your-smtp-server.com",
"portDest": 587,
"portOrig": 30587
}
]
}
That's it! Now you can test email functionality in your AEM application. Also, check logs for any further
debugging.

CDN | Clearing Cloudflare cache

In order to clear Cloudflare cache automatically via code, follow below steps: 1. Develop Custom TransportHandler Develop a custom Trans...