Quantcast
Channel: Dan Klco – Perficient Blogs
Viewing all 49 articles
Browse latest View live

Comparing AEM as a Cloud Service and AEM 6.5

$
0
0

Adobe’s recent release of AEM as a Cloud Service has inspired a ton of questions as we start to explore the platform. One of the first questions I have is: how different is this than AEM 6.5?

Not a Simple Answer

Before we can answer that question, you need to consider how AEM as a Cloud Service differs from the classic AEM deployment model. In AEM as a Cloud Service, the instances are continually updated by Adobe and managed in transient containers in the cloud. This drastically differs from the classic model where either the customer or Adobe Managed Services would deploy a known released version of AEM to a virtual machine running in the cloud.

For developers and maintainers, this allowed us to easily set up local instances by just downloading the same AEM Jar, packages, and content, at least until the next Service Pack!

To give developers the ability to run a local AEM instance similar to AEM as a Cloud Service, Adobe released an AEM SDK Quickstart for local development. Unfortunately for the sake of comparison, the AEM SDK Quickstart and AEM as a Cloud Service running in the Adobe DataCenters are different. To compare the three, I downloaded the bundle list for all three versions of AEM to see what’s running under the hood.

To avoid the churn of version updates, I stripped out the version numbers. Please note that this is a snapshot and that Adobe will be updating AEM as a Cloud Service on a daily (if not more) basis.

Bundles in AEM 6.5 but not in AEM as a Cloud Service

AEM as a Cloud Service is considerably lighter than AEM 6.5 there are 87 bundles not in AEM as a Cloud Service. Most of the bundles are related to features not included in AEM as a Cloud Service, specifically, AEM Communities, Forms and Screens or upgrading from CQ5.

  • com.adobe.aem.transaction-core – AEM Forms Transaction Core Bundle
  • com.adobe.aemds.formsmanager.adobe-aemds-formsanddocuments-core – AEM Forms and Documents Core
  • com.adobe.aemds.guide.aemds-guide-core – Adaptive Forms Core API
  • com.adobe.aemds.guide.aemds-guide-core-impl – Adaptive Forms Core Implementation
  • com.adobe.aemfd.ccm.multichannel-ccm-multi-channel-core – Multi Channel Core
  • com.adobe.cq.commerce.cq-commerce-core – Adobe Communique 5 Commerce Core
  • com.adobe.cq.commerce.cq-commerce-pim – Adobe Communique 5 Commerce Product Information Manager
  • com.adobe.cq.commerce.cq-commerce-social – Adobe Communique 5 Commerce Social
  • com.adobe.cq.cq-activitymap-integration – AEM 6 ActivityMap Integration Bundle
  • com.adobe.cq.cq-pre-upgrade-backup – CQ Pre-upgrade Backup
  • com.adobe.cq.cq-pre-upgrade-cleanup – CQ Pre-upgrade Cleanup
  • com.adobe.cq.sample.we.retail.core – We.Retail – Core
  • com.adobe.cq.screens.com.adobe.cq.screens – AEM Screens – Core
  • com.adobe.cq.screens.com.adobe.cq.screens.apps.we_retail – Screens WeRetail
  • com.adobe.cq.screens.com.adobe.cq.screens.dcc – AEM Screens – DCC
  • com.adobe.cq.screens.com.adobe.cq.screens.mq.activemq – Adobe Screens Apache ActiveMQ Implementation
  • com.adobe.cq.screens.com.adobe.cq.screens.mq.core – Adobe Screens JMS implementation
  • com.adobe.cq.screens.com.adobe.cq.screens.sessions –
  • com.adobe.cq.social.cq-social-activitystreams – AEM Communities ActivityStreams – Bundle
  • com.adobe.cq.social.cq-social-as-provider – AEM Communities AdobeSocial Resource Provider – Bundle
  • com.adobe.cq.social.cq-social-badging-api – AEM Communities Badging API – Bundle
  • com.adobe.cq.social.cq-social-badging-basic-impl – AEM Communities Basic Badging – Bundle
  • com.adobe.cq.social.cq-social-badging-impl – AEM Communities Badging – Bundle
  • com.adobe.cq.social.cq-social-calendar-api – AEM Communities Calendar – API Bundle
  • com.adobe.cq.social.cq-social-calendar-impl – AEM Communities Calendar – Impl Bundle
  • com.adobe.cq.social.cq-social-commons – AEM Communities Commons – Bundle
  • com.adobe.cq.social.cq-social-commons-oauth – AEM Communities Commons Oauth – Bundle
  • com.adobe.cq.social.cq-social-console – AEM Communities Console – Bundle
  • com.adobe.cq.social.cq-social-content-fragments-impl – AEM Communities Content Fragments Impl – Bundle
  • com.adobe.cq.social.cq-social-enablement-api – AEM Communities Enablement Api – Bundle
  • com.adobe.cq.social.cq-social-enablement-impl – AEM Communities Enablement Impl – Bundle
  • com.adobe.cq.social.cq-social-filelibrary – AEM Communities File Library – Bundle
  • com.adobe.cq.social.cq-social-forum – AEM Communities Forum – Bundle
  • com.adobe.cq.social.cq-social-gamification-api – AEM Communities Gamification API – Bundle
  • com.adobe.cq.social.cq-social-gamification-impl – AEM Communities Gamification Impl – Bundle
  • com.adobe.cq.social.cq-social-graph-api – AEM Communities SocialGraph API – Bundle
  • com.adobe.cq.social.cq-social-graph-impl – AEM Communities SocialGraph Impl – Bundle
  • com.adobe.cq.social.cq-social-group – AEM Communities CommunityGroup – Bundle
  • com.adobe.cq.social.cq-social-handlebars – AEM Communities Handlebars Scripting Engine – Bundle
  • com.adobe.cq.social.cq-social-ideation-api – AEM Communities Ideation API – Bundle
  • com.adobe.cq.social.cq-social-ideation-impl – AEM Communities Ideation – Bundle
  • com.adobe.cq.social.cq-social-jcr-provider – Bundle –
  • com.adobe.cq.social.cq-social-jcr-provider-common – AEM Communities JCR Resource Provider Common Code – Bundle
  • com.adobe.cq.social.cq-social-journal – AEM Communities Journal – Bundle
  • com.adobe.cq.social.cq-social-livefyre – AEM Livefyre – Bundle
  • com.adobe.cq.social.cq-social-members-api – AEM Communities Members API – Bundle
  • com.adobe.cq.social.cq-social-members-impl – AEM Communities Members Impl – Bundle
  • com.adobe.cq.social.cq-social-messaging-api – AEM Communities Messaging – API Bundle
  • com.adobe.cq.social.cq-social-messaging-impl – AEM Communities Messaging – Impl Bundle
  • com.adobe.cq.social.cq-social-moderation – AEM Communities Moderation – Bundle
  • com.adobe.cq.social.cq-social-moderation-spamdetector-core – AEM Communities Spam Detection Core – Bundle
  • com.adobe.cq.social.cq-social-ms-provider – AEM Communities Mongo Storage Resource Provider – Bundle
  • com.adobe.cq.social.cq-social-notifications-api – AEM Communities Notifications – API Bundle
  • com.adobe.cq.social.cq-social-notifications-channels-web – AEM Communities Notifications – Web Channel
  • com.adobe.cq.social.cq-social-notifications-impl – AEM Communities Notifications – IMPL Bundle
  • com.adobe.cq.social.cq-social-qna – AEM Communities QnA – Bundle
  • com.adobe.cq.social.cq-social-rdb-provider – AEM Communities Relational Social Resource Provider – Bundle
  • com.adobe.cq.social.cq-social-reporting-management – AEM Communities Enablement Reporting Management- Bundle
  • com.adobe.cq.social.cq-social-review – AEM Communities Review – Bundle
  • com.adobe.cq.social.cq-social-scf-api – AEM Communities SCF – API Bundle
  • com.adobe.cq.social.cq-social-scf-impl – AEM Communities SCF – Impl Bundle
  • com.adobe.cq.social.cq-social-scoring-api – AEM Communities Scoring API – Bundle
  • com.adobe.cq.social.cq-social-scoring-basic-impl – AEM Communities Scoring Basic – Bundle
  • com.adobe.cq.social.cq-social-scoring-impl – AEM Communities Scoring – Bundle
  • com.adobe.cq.social.cq-social-serviceusers-api – AEM Communities Service Users – Bundle
  • com.adobe.cq.social.cq-social-serviceusers-impl – AEM Communities Service Users – Impl Bundle
  • com.adobe.cq.social.cq-social-srp-api – AEM Communities SRP Base – API Bundle
  • com.adobe.cq.social.cq-social-srp-impl – AEM Communities SRP – Impl Bundle
  • com.adobe.cq.social.cq-social-sync – AEM Communities Sync – Bundle
  • com.adobe.cq.social.cq-social-tally – AEM Communities Tally – Bundle
  • com.adobe.cq.social.cq-social-translation – AEM Communities Translation – Bundle
  • com.adobe.cq.social.cq-social-ugc-search-collections – AEM Communities UGC SearchCollections – Bundle
  • com.adobe.cq.social.cq-social-ugcbase-api – AEM Communities UGC Base – API Bundle
  • com.adobe.cq.social.cq-social-ugcbase-impl – AEM Communities UGC Base – Impl Bundle
  • com.adobe.cq.social.cq-social-user-ugc-management – AEM Communities User Ugc Moderation – Bundle
  • com.adobe.forms.common.adobe-xfaforms-common – Mobile Forms Common Bundle
  • com.adobe.forms.foundation-forms-foundation-base – Forms Foundation Base Bundle
  • com.adobe.forms.foundation-forms-foundation-cloud-config – Forms Foundation Cloud Configurations Bundle
  • com.adobe.granite.apicontroller – Adobe Granite API Controller
  • com.adobe.livecycle.formsportal-bundle – Adobe FormsPortal Services Bundle
  • com.day.cq.cq-compat-codeupgrade – Day Communique 5 Code Upgrade service
  • com.day.cq.cq-upgrades-executor – Adobe Communique 5 Upgrades Executor
  • com.day.cq.dam.cq-dam-pim – Day Communique 5 DAM PIM Integration Implementation
  • com.day.cq.dam.cq-dam-rating – Day Communique 5 Assets Rating
  • com.day.cq.pre-upgrade-tasks – AEM Pre-Upgrade Maintenance Tasks
  • forms-foundation-bundle – Forms Foundation Layer Bundle

New Bundles in AEM as a Cloud Service

Not surprisingly, AEM as a Cloud Service has some new bundles:

  • com.adobe.bp.bp-cloudservice – Adobe Brand Portal Cloudservice configuration
  • com.adobe.bp.bp-distribution – Adobe Brand Portal Distribution Implementation
  • com.adobe.cq.cq-ref-update-processor-util – AEM – Reference Update Processor Utility bundle
  • com.adobe.cq.dam.cq-dam-ui-models – AEM – DAM Sling Model Bundle
  • com.adobe.cq.inbox.cq-inbox-api – AEM – Inbox API
  • com.adobe.cq.remotedam.cq-remotedam-api – Adobe CQ Remote DAM API Bundle
  • com.adobe.cq.ups-integration – Unified Profile Service IMS integration
  • com.adobe.cq.ups-profile-lookup – Unified Profile Lookup routines
  • com.adobe.granite.jobs.async.ui.commons – Adobe Granite Async Jobs
  • com.adobe.granite.toggle.api – Adobe Granite Toggle API
  • com.adobe.granite.toggle.impl – Adobe Granite Toggle Implementation
  • com.adobe.granite.toggle.impl.static – Adobe Granite Toggle Implementation
  • com.day.cq.dam.cq-dam-asset-sourcing – Day Communique 6 DAM Asset Sourcing Implementation
  • com.day.cq.remote.content.renderer – Remote Content Renderer
  • org.apache.felix.configadmin.plugin.interpolation – Apache Felix Configuration Admin Values Interpolation Plugin
  • org.apache.felix.log – Apache Felix Log Service
  • org.apache.felix.rootcause – Apache Felix – Root Cause Analysis
  • org.apache.jackrabbit.oak-authorization-principalbased – Oak Principal-Based Authorization
  • org.apache.sling.feature.apiregions – Apache Sling Feature API Regions Runtime
  • rideau – Adobe Rideau (PDF API)

One of the more interesting libraries to me is Apache Felix RootCause, an interesting utility for debugging Declarative Services issues. Additionally, the bundle list also shows some of the new features in AEM as a Cloud Service such as an integration with Adobe IMS, an improved Brand Portal integration and Remote Assets.

Even More Differences: AEM SDK Quickstart to “true” AEM as a Cloud Service

One of the challenges I see with Adobe’s current model for development enablement is that the deployment model for AEM as a Cloud Service is completely different than the SDK Quickstart they provide. Some of the differences are:

  • The AEM SDK Quickstart runs natively on the computer, AEM as a Cloud Service runs in Docker containers.
  • We don’t know what JVM release or parameters are used for running AEM as a Cloud Service.
  • AEM as a Cloud Service is backed by MongoDB Atlas, not TarFS.
  • AEM as a Cloud Service uses Sling Content Distribution instead of traditional replication like the SDK Quickstart.

None of these issues will be problems during the normal course of development. However, if you run into performance or integration issues, not having an environment that matches the target architecture quickly becomes a challenge.

Adding to this, there are 32 bundles found only in the “real” version of AEM as a Cloud Service.

  • aem-api-metrics-feature-model-addon – AEM API metrics agent add-on for feature model analysis
  • com.adobe.granite.distribution.journal.pipeline – Adobe Granite Pipeline Distribution – Adobe Pipeline messaging implementation
  • com.adobe.granite.docker.version – Docker Version Metric Registry
  • com.adobe.granite.ims-yaml-loader – Granite ims yaml loader Bundle
  • com.adobe.granite.metrics.red – Granite RED Metrics
  • com.adobe.granite.prometheus.pull – Prometheus Pull Bundle
  • com.adobe.granite.uriprovider.dba – Granite URIProvider for Direct Binary Access
  • com.fasterxml.jackson.dataformat.jackson-dataformat-smile – Jackson dataformat: Smile
  • com.fasterxml.jackson.dataformat.jackson-dataformat-yaml – Jackson-dataformat-YAML
  • com.google.protobuf – Protocol Buffers [Core]
  • io.dropwizard.metrics.core – Metrics Core
  • io.jsonwebtoken.jjwt-api – JJWT :: API
  • io.jsonwebtoken.jjwt-impl – JJWT :: Impl
  • lz4-java – LZ4 Java Compression
  • org.apache.commons.lang – Commons Lang
  • org.apache.sling.distribution.journal – Apache Sling Journal based Content Distribution – Core bundle
  • org.apache.sling.distribution.journal.messages – Apache Sling Journal based Content Distribution – Messages bundle
  • org.apache.sling.feature – Apache Sling Feature
  • org.apache.sling.feature.apiregions.model – Apache Sling Feature Model API Regions
  • org.apache.sling.feature.diff – Apache Sling Feature Model diff tool
  • org.apache.sling.feature.inventoryprinter – Apache Sling Feature Inventory Printer
  • org.apache.sling.feature.io – Apache Sling Feature IO Module
  • org.apache.sling.feature.r2f – Apache Sling Feature Model runtime creator
  • org.apache.sling.jcr.packageinit – Apache Sling JCR Package Initializer module
  • org.apache.sling.launchpad.api – Apache Sling Launchpad API
  • org.apache.sling.launchpad.startupmanager – Apache Sling Launchpad Startup Manager
  • org.yaml.snakeyaml – SnakeYAML
  • pattern-database-aem65 – AEM Pattern Database for Pattern Detector
  • pattern-database-skyline – AEM Pattern Database for Pattern Detector
  • pattern-detector – AEM Pattern Detector
  • pipeline-client-core – pipeline-client-core
  • pipeline-protocol – pipeline-protocol

Most of these bundles seem to be related to Adobe’s deployment model for AEM as a Cloud Service, either via the IMS integration, monitoring, data loading or the Sling Feature Model. Most of these dependencies are internal and not for our use, but it’s worth mentioning:

  • Granite Toggles – Feature flag functionality, meant for internal Adobe use
  • Prometheus Pull – internal metric gathering
  • Unified Profile Service – currently only supports authors, but AEP / SSO support is on the roadmap

Continuing to add differences, the RELAX NG Object Model / Parser, Joda Time and Gson libraries are actually different between the AEM SDK Quickstart and AEM as a Cloud Service. While they should both provide the same API surface, there’s no guarantee of this nor that the underlying bundling process was the same.

Conclusion

AEM 6.5 and AEM as a Cloud Service are pretty similar under the hood, however, there are just enough differences to trip you up, especially when diagnosing thorny technical issues. I would advise care in dependency management, as AEM SDK Quickstart and AEM as a Cloud Service do have some differences.

My hope is that Adobe will release a version of their AEM SDK Quickstart as a Docker Swarm setup so developers can run a “lite” version of AEM as a Cloud Service on their local computer to really end to end test their applications.

 

The post Comparing AEM as a Cloud Service and AEM 6.5 appeared first on Perficient Digital.


Website Performance with the Adobe Experience Cloud

$
0
0

 

Performance is a critical concern for any web implementation. Users expect high performance, responsive web experiences and will abandon experiences that don’t deliver. Here are some things you need to consider to ensure Adobe solution performance:

  • Scaling of Underlying Hardware
  • Development Best Practices
  • Author Instance Performance
  • Publish Instance Performance
  • Dispatcher Caching Percentage
  • Website Performance

 

Sizing the Underlying Hardware

 

Properly sizing and scaling the underlying server instances is a critical initial step to ensuring AEM performance. Perficient recommends consulting with Adobe Managed Services and/or Adobe Launch Foundation Services on properly sizing instances.

In general, the key performance needs are sufficient processing and memory. An important point to consider is AEM will allocate Sling Job pools according to the number of cores on a machine, so for Job-heavy use cases, such as ingesting assets or processing forms, having enough cores is critical.

In addition, since AEM leverages disk storage when using the TarFS, provisioning with IOps-optimized SSDs vs spinning hard drives provides a significant performance improvement. Leveraging S3 for blob storage can provide better cost and resiliency, but at the cost of higher latency.

Beyond individual servers, provisioning reasonable supporting hardware/services such as Load Balancers and a Content Distribution Network (CDN) will ensure the performance and scalability of the overall application.

 

Development Best Practices

 

There are a number of development best practices to ensure performance. These include avoiding JCR Queries as much as possible, evaluating OAK Indexes for any queries, ensuring all Resource Resolvers are closed, ensuring as much of the content of the website can be cached, caching API responses and avoiding data structures which create too many child nodes of a single node.

One important interaction a lot of team miss are background and non-web services, such as servlets. All non-trivial services should be monitored for failure and slow responses. Beyond just simple success/failure monitoring, in combination with Commons StopWatchSling / Felix Health Checks or Sling Metrics can be used to ensure background processes are executing in a reasonable timeframe.

During the development process, Static Code Analysis tools can help identify memory leaks and best practice compliance. Leveraging performance testing tools such as Tough Day and/or JMeter to performance test every release will ensure that applications should perform well under load.

Note: For customers using Cloud Manager, both Static Code Analysis and performance testing are included in the Cloud Manager deployment process.

 

AEM Instance Performance

 

Regular maintenance tasks should be configured such as configuring cleanup tasks such as Version Purging, Workflow Cleanup, Online Compaction and Audit Log cleanup. All production and pre-production environments should be installed in Production-Ready mode.

 

AEM Author Performance

Author performance is difficult to scale since AEM instances generally can’t scale authors horizontally without creating a separate application stack. Generally speaking, the AEM Author instance should be 2x the specs of the Publisher instances.

It is especially critical to ensure maintenance tasks are run on author as there is more repository churn on the authors than publishers in most cases.

The most common performance issues with Author instances are workflows and blocked activations so make sure the activation queue, job queue, and workflow instances are monitored. Too many of any of these will bring an author instance to a crawl.

Content structure and taxonomy are important considerations for author instances. How pages are assembled, navigated and edited can have a significant effect on author productivity. Effectively leveraging Editable Templates and Experience Fragments can reduce authoring time by separating out common content from page-specific content and thus reducing content entry time.

 

Publisher Performance

Unlike authors, publishers can be scaled horizontally, the primary limitation here is licensing. Beyond following development and server best practices, to ensure performance in publisher performance, ensure that the Publisher is configured for production and has the Link Checker disabled.

If your application does serve a significant amount of non-cacheable content, such as an intranet or logged in site, Publisher instances should be correspondingly expanded in CPU Cores and Memory to accommodate.

For applications with a significantly large number of users and groups, using external group membership can have a significant performance improvement. This will be the subject of a future blog post.

 

Dispatcher Performance

 

The most critical aspect of dispatcher performance is to ensure as much content on the site is cached as possible. Sling Selectors and Suffixes can be used in the place of query strings to create dynamic, but still cachable pages. All query strings not needed for server-side processing (such as campaign ID’s), should be ignored by the dispatcher.

Blocking non-authorized requests will help prevent DDOS attacks as well as reducing server time serving invalid requests.

Balancing the needs of referential content and creating a structure to allow a lower stats level will help ensure that replication does not need to invalidate the entire cache.

From a delivery perspective, the dispatcher web engine should be configured to optimize the delivery of the website to the user’s browser. This includes client-side caching, compression and HTTP/2 support. mod_pagespeed can provide a “quickstart” for optimizing dispatcher performance, though not all options are appropriate for every site.

 

Performant Personalization

For content that needs to be individualized to the user, leveraging Ajax or Sling Dynamic Includes can allow for creating a personalized experience. For personalizing content via segments, leveraging Adobe Target brings a much more powerful, marketing-friendly tool for personalizing content, so we would recommend using Target over personalizing via segments in AEM.

Though AEM does support an integration to edit Targeted content in AEM, generally we would recommend authoring Targeted content in Target as it offers a richer, nicer experience and requires less client-side code to execute.

 

Managing Redirects

Managing redirects from the legacy system can also be an issue when there is a large number of legacy redirects. Perficient contributed a tool for managing these redirects to ACS AEM Commons called Redirect Map Manager. With this tool, each migrated page can indicate its legacy URL, marketers can define vanity URLs and lists of redirects can be loaded as flat files.

 

Website Performance

 

In the end, what users care about is how fast the website loads. Optimizing the server performance will not overcome a poorly developed HTML page.

A few key performance best practices include combining and minifying CSS and JavaScript via Client Libraries or NPM Builds (Note: HTTP/2 does influence this, however without ensuring two different copies of the stylesheets can be served for HTTP/1.1 vs HTTP/2, this is still a best practice until HTTP/2 support becomes more prevalent) and avoiding document.write or inline styling/scripts.

 

Optimizing Images

Images represent the largest assets served on most websites, thus resizing images to the smallest workable size represents one of the biggest wins for optimizing website performance. 

There are a number of options to resize images in Adobe, including:

No matter which option you choose, optimizing image sizes is a must for delivering a performant site.

 

Optimizing Digital Marketing Tags

The most basic step is optimizing marketing tag delivery via a Tag Management System such as Launch by Adobe. The Tag Management System should deliver all tags and avoid multiple includes. To avoid page flicker, Adobe Target should be integrated into the body or head, as high as possible.

Consider integrating Launch via Asynchronous deployment instead of the default synchronous method.

Teams should careful consider which tags to execute immediately and which to defer to balance the needs of the marketing teams to measure and optimize and the performance needs of users.

 

Single Page Applications

Single Page Applications such as AngularJS or React are a solution for delivering a “reload-less” experience, however, they come with significant downsides from a Content Management, Digital Marketing and SEO perspective vs traditional server-rendered websites. By developing with ES6 / TypeScript and transpiling to vanilla JavaScript we can develop experiences with similar usability but without the bloat or downsides of Single Page Applications.

To determine whether or not to implement an application in a SPA, consider what kind of application you are creating. Is this a content application or a functional application? Functional applications such as profile editors, social interactions and customer portals fit well into the SPA model, but managing a content-heavy site such as a marketing website, documentation portal or intranet proves cumbersome with a SPA.

 

Where to Start

 

With all of these potential ideas on how to ensure website performance with Adobe, where should you start? It’s best to start front to back. Use a performance testing tool such as Google Page Speed Insights or GTMetrics to identify issues directly impacting users and work backward through the stack identifying and optimizing issues.

 

Additional Reading

 

Acknowledgments

Thanks to the following people for contributing ideas and feedback for this post:

 

The post Website Performance with the Adobe Experience Cloud appeared first on Perficient Digital.

How AEM as a Cloud Service Scales

$
0
0

 

With AEM as a Cloud Service, Adobe has solved some extremely challenging problems in order to make AEM scale in the cloud. These challenges relate to the underlying implementation and concepts behind AEM and are:

  • Each AEM instance stores the application code in the repository which is, therefore, mutable
  • Each AEM instance stores the content repository on the filesystem
  • Publishing content from an author to publish instances is not journaled and therefore cannot be reproduced on new instances

How did Adobe overcome these challenges? And what’s so different between AEM as a Cloud Service’s approach from AEM “Classic”?

As I discussed in my previous blog post dissecting the bundles in AEM as a Cloud Service, AEM “Classic” and AEM as a Cloud Service share most of the same underlying technologies. The key difference is how two tools use these technologies. There are some key differences that enable Adobe to dynamically scale AEM as a Cloud Service vs the fixed model for AEM “Classic”.

 

Apache Sling OSGi Feature Model

 

AEM as a Cloud Service is provisioned with the Apache Sling OSGi Feature Model (or Feature Model). The Feature Model is a different provisioning model than the Sling Provisioning Model it replaced which has been used for the previous versions of AEM.

The key difference is that in the Sling Provisioning Model, the application was assembled using configuration files to define the dependencies, initial content, and configurations. From that built release, the customizers and customers would make changes to the application, installing packages, bundles, etc.

The Feature Model, on the other hand, has a rich grammar, which allows for the creation of “Features” or aggregations of Bundles, Packages, initial content, configurations, etc. Each Feature can then be combined with other features to create the end application. This allows Adobe to create a complete representation of the configured application with the customer’s customizations and the base AEM application code.

 

Apache Sling OSGi Feature Model

 

It’s reasonable to ask, why does this matter? The use of Feature Model has a number of implications.

First, Content Package projects deployed to AEM as a Cloud Service are converted by Cloud Manager to Feature Models. You can see this if you check the Package Manager in your AEM as a Cloud Service instance.

 

Package Manager in AEM as a Cloud Service

 

For most situations, this shouldn’t cause any issues, but it’s a good thing to know just in case.

Next and more importantly, this allows Adobe to make the instances effectively immutable. Unlike AEM “Classic” hosted by Adobe Managed Service, AEM as a Cloud Service does not support making ad-hoc configuration and code changes outside the Cloud Manager process. This is because of the scaling model for AEM as a Cloud Service. Since AEM as a Cloud Service auto-scales with Kubernetes, each instance’s configurations must be consistent.

Just scaling instances, however, doesn’t solve the big problem with scaling AEM. How do I keep the repository contents in sync for new instances without copying Gigabytes or even Terabytes of data?

 

Composite NodeStore

 

Adobe’s solution to the problem of synchronizing the repository contents while scaling is a new feature in Apache Jackrabbit Oak, the Composite NodeStore. The Composite NodeStore exposes multiple different NodeStore providers using a singular interface at the JCR level.

Using the Composite NodeStore, AEM as a Cloud Service combines Segment NodeStores for the /apps and /libs directories and fragments of /oak:index with a MongoDB backed Document NodeStore for the remainder of the repository.

This enables four important things:

  • New instances can connect to the same MongoDB instance to instantly synchronize repository content
  • The /apps/libs, and /oak:index are provided by the Feature Model based on the Feature definition built by Cloud Manager
  • It resolves performance problems associated with using the Document NodeStore by keeping the most read paths (due to Resource Resolution) in a local Segment NodeStore
  • Finally, this makes the base AEM and custom code immutable as the /apps and /libs directories are mounted as read-only NodeStores. Even in CRXDE, you cannot modify anything under those paths

 

AEM as a Cloud Service Read Only Mounts

 

One interesting side effect of the Composite NodeStore and Feature Model is that you can no longer mix the immutable and mutable paths. Projects must strictly separate out updates for /content and /conf from /apps and /libs as shown in the AEM Project Archetype.

It is also worth mentioning, that at least our sandbox instance of AEM as a Cloud Service uses Azure DataStore for Blob (large file) storage. There are a number of different Blob storage options supported by Jackrabbit Oak, so I am not sure if this is a standard for all AEM as a Cloud Service instances (the most likely option) or if they also use Mongo or Amazon S3, but it is safe to assume they are using non-instance blob storage.

 

Sling Content Distribution

 

The final piece to the puzzle is solving the problem of synchronizing the replication state for new publish instances. Adobe solves this with the new Sling Content Distribution library and specifically the Journaled implementation. This replaces AEM’s legacy replication API.

Sling Content Distribution Journaled Deployment

 

The Sling Content Distribution Journaled documentation has quite a bit of detail on exactly how this is implemented, one would assume in AEM as a Cloud Service. In essence, the implementation keeps a Journal of the replicated content stored in a shared blob store. The publish instances subscribe to the journal for receiving publication events and then processing each publication event.

New publish instances would similarly subscribe and would receive all of the journaled entries to restore a state consistent with the existing publish instances. There is quite a bit more happening behind the scenes, so if you’d like to understand this in-depth, definitely read the documentation.

 

What All This Means

 

Adobe Engineering has clearly put in some excellent work to solve the challenging problems in scaling AEM. For implementors though, AEM as a Cloud Service does present challenges in having significant differences between the local development and AEM as a Cloud Service deployment model.

I’ll just keep repeating my hope is that Adobe will release a version of their AEM SDK Quickstart as a Docker Compose / Kubernetes setup so developers can run a “lite” version of AEM as a Cloud Service on their local computer to really end to end test their applications.

 

Acknowledgments

 

Thanks to Ruben Reusser for feedback and fact-checking the author vs publish storage configuration.

 

The post How AEM as a Cloud Service Scales appeared first on Perficient Digital.

New with AEM as a Cloud Service: Adobe IMS Login by Default

$
0
0

AEM as a Cloud Service finally consolidates the login experience between AEM and the rest of the Adobe Experience Cloud. AEM as a Cloud Service comes pre-configured with Adobe Identity Management Service (IMS) for authentication. Previously, with AEM, customers would have to configure an Identity Provider (IDP) such as Active Directory or IMS.

For users experienced with the Adobe Experience Cloud, this finally aligns the sign-in between AEM and the rest of the tools, but it does change the authentication paradigm for users mostly familiar with AEM.

 

What’s Different for AEM Admins?

 

As an administrator, instead of first logging into the instance with local authentication, then configuring the IDP, AEM as a Cloud Service instances comes pre-configured with IMS authentication. To grant access, head to adminconsole.adobe.com and add the user’s into the relevant groups.

As the Adobe documentation elaborates, to be granted access users must belong to one of two groups ASM User-xxx or AEM Administrators-xxx. Note, you must be granted permissions to the instance, permissions to administer the associated Cloud Manager will not suffice.

From there, AEM as a Cloud Service authentication is relatively similar to a standard Single Sign On (SSO) integration in that you login with the SSO and then are redirected to AEM.

 

Bring your own SSO

 

It certainly helps that IMS consolidated the sign-in with AEM and the rest of the Adobe Experience Cloud, but what if your organization wants to use their own SSO? Adobe IMS supports three account types:

  • Adobe ID – identity owned by the individual
  • Enterprise ID – identity owned by the organization, managed by Adobe
  • Federated ID – identity owned by the organization and managed by the organization’s SSO

Enterprise ID is the default, so to use the organization’s SSO, you must configure Federated ID.

 

Managing Groups with IMS and AEM as a Cloud Service

 

Beyond the base Administrators and Users groups, IMS syncs all of the group memberships the user is assigned to into AEM. These groups are not assigned any permissions, but since they are AEM groups, they can be assigned permissions.

This allows administrators to create users and assign membership in a central source and then enable permissions at the group level. Organizations with multiple Adobe products can especially benefit by creating role-based groups and then assigning access to multiple products including AEM as a Cloud Service via IMS.

 

New things Coming in AEM as a Cloud Service

 

While AEM as a Cloud Service is similar to AEM 6.5, there are key features, particularities, and capabilities with AEM as a Cloud Service. The changes in authentication are just one part of the larger change in the Adobe Experience Manager ecosystem with the introduction of AEM as a Cloud Service. Stay tuned for more and read the rest of the AEM as a Cloud Service blog series.

The post New with AEM as a Cloud Service: Adobe IMS Login by Default appeared first on Perficient Digital.

Crossing the Performance Chasm with Mass Users / Groups in AEM

$
0
0

 

In my previous article, Improving Bulk User Creation in AEM 200x, I discussed how we improved a process importing thousands of groups and users into Adobe Experience Manager. This drastic speedup enabled our project to pass the development tests and we looked good going into production.

Unfortunately, as soon as we got access to the production user set, we ran into another performance trap. Our non-production data set had approximately 2,000 groups, while production had more than 4,000. Even worse, there were a factor fewer user <-> group associations in non-production. The production data had nearly 100,000,000 user <-> group associations, while non-production only had less than a tenth as many.

While our user creation job would complete in approximately 30 minutes, the job to synchronize group members took over a week to complete.

Observing the job execution, the job always followed the same pattern, the job would make hundreds of changes / second for approximately the first 30 minutes, completing up to half of the updates, but then performance would drop, leading to only a handful of updates per second for the remaining execution time, which took days.

 

Graph of Updates over Time

 

Our first attempt was to optimize the existing code, however pretty soon we found that micro-optimizations weren’t going to get us past the blocking problem, it took too long to add the members to the group, so we got creative on looking for different solutions.

 

Solution Attempt #1 – Multi-Threading

 

Our first fix attempt was to create multiple threads, each to handle the membership for a single group, thinking that the problem was that there were particular groups causing the performance problems and if we could get around those groups, we could resolve the issue.

On our local environments, against the non-production data, this seemed like a fix, however, it was shredded when running against the production data set and indeed ran worse than our initial single-threaded solution.

Interestingly, we found that this actually causes significant I/O blocking due to excessive reads, unfortunately, our limited monitoring was not able to determine what was being read so much.

 

Solution Attempt #2 – importXML

 

Knowing that we were facing an I/O problem, we looked into options to reduce the amount of I/O. After investigation of the Jackrabbit Oak code, we found Group.addMembers was traversing the full group membership on each add to detect if the Authorizable was already a member of the group.

To alleviate this, we investigated using Workspace.importXML to instead import a fully-formed Sysview XML representation of the group and the group’s membership.

Once again, this showed tremendous promise during local tests:

 

Graph of importXML Perforamance

 

And once again, when faced with production data, importXML was reduced to a crawl. While faster, it still took days to complete, well outside our three-hour window.

 

Eureka! Dynamic Group Membership!

 

While this is going on, we’d been steadily escalating through Adobe, eventually getting time with Adobe Engineering in Basel. One the call, Adobe Engineering dropped a bomb, this is a known issue with AEM.

The issue is with the number of user <-> group connections. Specifically, with how Jackrabbit Oak internally uses indexes to store the group membership and is not something we could optimize. After some back and forth, the Adobe Engineering team suggested Dynamic Group Membership as an alternative solution.

Dynamic Group Membership works entirely differently than the default Jackrabbit Oak Group Membership. In Dynamic Group Membership, the user’s groups are stored in a property rep:externalPrincipalNames and resolved at runtime.

This approach eliminates the need to add the users to the groups, thus resulting in a massive performance increase.

The best thing is this option is easy to configure, set the Identity Sync Type in the Adobe Granite SAML 2.0 Authentication Handler OSGi configuration to oak external idp sync for SAML-based authentication. For LDAP-based authentication, check the User Dynamic Membership checkbox in the Apache Jackrabbit Oak Default Sync Handler for your LDAP configuration.

Once you have configured Dynamic Group Membership, existing relevant users will need to be re-synced (e.g. deleted and re-added) as the default Jackrabbit Oak Group Membership will take precedence.

 

Configuring the Granite SAML Authentication Handler

 

What if the Group Membership isn’t in the IDP?

The default implementation of Dynamic Group Membership, as implemented in the Granite SAML Authentication Handler, assumes that the SAML Assertion contains all of the relevant information, including the group membership.

Unfortunately, in our case, the group membership is not stored in Active Directory as the Active Directory is used corporation-wide, whereas these groups were specific to our application. This presented a problem as the rep:externalPrincipalNames attribute is protected at the JCR level and can only be accessed by whitelisted services.

Enter the Principal Provider

 

Under the hood, Dynamic Group Membership registers a PrincipalProvider service to expose the user’s group membership based on the user’s rep:externalPrincipalNames attribute. The Principal Provider interface is part of Jackrabbit Oak’s Principal Management API. Principal Provider implementations are called to expose principals (users and groups) and their group membership.

To support having the group membership external to the SAML Assertion without having to overlay significant portions of the Granite SAML Authentication Hander, we simply had to implement our own Principal Provider instance. To avoid impact during deployments, we implemented this as a separate bundle from the main project code and had the bundle’s Activator set the bundle Start Level to 15.

 

Diagram of the User Group Synchronization Solution

 

What does this mean for me?

 

For AEM customers who need to support large numbers of users/groups (100,000+ users, 2,000+ groups) switching to Dynamic Group Membership will significantly reduce the time required to synchronize the users and groups versus default Jackrabbit Oak group membership.

Using the PrincipalProvider / Principal Management API, you can even support providing group membership beyond what is available from the IDP.

My Themes and Recommended Sessions for Adobe Summit 2020

$
0
0

Monday evening I was notified by Outlook that it was time for Perficient’s annual pre-Summit dinner, the first time I’ve not been to Las Vegas in March since Summit moved from Salt Lake City.

While I am glad that Adobe has taken measures for the safety of our community, colleagues and family, I will miss meeting up with you at Adobe Summit 2020.

As we cannot meet up in person, Adobe has re-launched Adobe Summit as an online experience. Now we can watch all of the Adobe-goodness at our leisure. If only we were stuck inside with nothing better to do…

To help you keep busy during the quarantine, I’ve put together a list of my themes for Summit 2020 and the top videos I’m going to be watching!

Theme #1: End of Cookies

Safari represents 56% of mobile browser visitors in the US, so if you care about mobile visitors ITP is already the reality. Even if not, Chrome, which represents 65% of the global browser market share, is announced similar measures coming soon. Clearly, relying on 3rd party cookies is not a winning strategy, so what do we do next?

Theme #2: The Next Generation of Content Management

I’ve been working with Adobe Experience Manager for a long time and frankly, while Adobe has been making iterative investments and UI changes, the tool is recognizably similar to how it was 10 years ago. Now with AEM as a Cloud Service, Adobe is changing the game and I for one am very interested to see where they are going.

In addition to AEM as a Cloud Service, there’s also some great AEM Classic vs Cloud Service agnostic content:

 

Theme #3: Get the Most out of Adobe Analytics

I always enjoy seeing what other practitioners and organizations are doing with Adobe Analytics, this year there are some great sessions around getting the most out of Adobe Analytics.

 

Theme #4: Adobe Experience Platform

Adobe Experience Platform represents the next generation of customer data management and orchestration on the Adobe Experience Cloud. I’m excited to see the promise meet reality.

 

And of course: Sneaks!

Sneaks is always my favorite session at Adobe Summit, who doesn’t like seeing cool stuff and technologists heckled by comedians? The hardest things about sneaks is realizing the cool stuff you saw won’t be available immediately. There’s tons of neat topics today, but here are the two I’m most excited about.

 

 

Bonus: All 120 Summit Breakouts

In addition to my top sessions, when researching for this post, I also created a list of the 120 breakout sessions for Summit 2020. Here’s the full list for your easy perusal!

Advertising

As the Cookie Crumbles: Advertising in a cookie-less world

Get insights into the new world of data collection and identity management as we move into a cookie-less era.

 

Disrupt the 2020 upfront with Adobe Advertising Cloud TV

Learn advanced strategies in TV advertising to maximize your upfront investment

 

Paid search campaigns in an auto-optimized world

Using AI and machine learning to build models, Adobe Sensei optimizes paid search campaigns to improve ROI.

 

Real-time audiences in advertising

Use seamless workflows across Adobe Experience Cloud to exceed customer expectations and power advertising like never before.

 

B2B

20/20 vision into reporting and attribution

Learn how to pass EVARs to Marketo Engage forms, as well as best practices for hard- and soft-gating content.

 

A crash course in marketing and sales integration

Learn to use Marketo Engage to create an alignment strategy that fosters efficiency, scalability, and results for everyone.

 

All about the before, during, and after of lead scoring

Get best practices on how to design, implement, report and monitor your lead scoring strategy with Marketo engage.

 

Anatomy of a modular marketo engage template

Get the most out of your templates, including how to work with an agency, and email and landing page optimization must-haves.

 

B2B customer experience: out with the old, in with the new

Competing in the new B2B world requires agile digital capabilities. Learn how to use Experience Cloud to deliver top experiences.

 

Build a model to develop ideal sales-qualified leads

Learn how to use all the information that you have to develop and send sales the right lead.

 

Building the dream team for the perfect martech deployment

Gain an in-depth understanding of all processes behind a MarTech deployment to plan, implement, and launch a new platform.

 

Data science and Marketo Engage drive robust ROI

Learn how integrating data science into the core of your marketing strategy not only helps your team but also your bottom line.

 

Designing a smart nurture program

Create nurture programs that are insightful and intuitive. How to set up Marketo Engage programs for attribution and reporting.

 

Don’t Be a Basic Batch: Test Nurture Program Success

Construct your way to success by building and testing your most engaging nurture program yet.

 

Expand your Marketo Engage capabilities

Uncover the inner workings of automation and receive golden nuggets from industry insiders.

 

Give prospects content they want with data-driven engagement

Reach the right audience at the right time with the right content for a truly unique experience that leaves your buyers satisfied.

 

Growing your ROI with strong attribution

Learn the theory of attribution from first touch all the way to AI-driven and best practices from implementation to overhaul.

 

Keep your Marketo Engage instance healthy

Learn how to perform an audit of your Marketo Engage instance, set up customized smart lists, and devise a targeted project plan.

 

Leading marketing during digital transformation

Join Valerie Beaulieu, U.S. CMO at Microsoft, for a talk about her experience leading during Microsoft’s digital transformation.

 

Manage Marketo Engage for sustainable growth

Learn how to scale your instance of Marketo Engage as your team grows, including changes to make now to prevent problems later.

 

New opportunities in the email enlightenment algorithm

Get a deep understanding of how the algorithm works and actionable insights to making improvements to your emails today.

 

Omnichannel nurturing and customer engagement

Engage customers with relevant personalized content, create an integrated nurture system, and coordinate across multiple channels.

 

Practical uses of AI for today’s enterprise marketers

Learn about the latest AI innovations designed to help you take your demand marketing to the next level.

 

Practices in marketing operations to earn a seat at table

Become a trusted advisor, measure the right information, and show returns to solidify marketing as a revenue powerhouse.

 

Privacy compliance and legal requirements in Marketo

Build a privacy compliance program with segmentation, region updates, merging duplicates, and controlling implied consent.

 

Resuscitate and revive dying leads

Ensure you’re getting the most engagement out of your database with this session’s guide to nurture leads back into the hot seat.

 

Special Events Advanced Tactics to fill every S.E.A.T

Get tips on Marketo Engage to nurture leads, fill seats and interact with your attendees before, during, and after your event.

 

Techniques for solving errors in Marketo Engage

Uncovering common issues that users come across and how to locate the source of an error and fix it quickly.

 

The best-kept Marketo secret: the Campaign Requested feature

Learn how to simplify complex workflows, flexibility to change and update campaigns, and build a scalable infrastructure.

 

Commerce

Adobe Sensei powers Magento product recommendations

Learn how Adobe Sensei will soon bring the power of creating and managing intelligent product recommendations to Magento Commerce.

 

Allbirds meteoric success: omnichannel retail in China

Allbirds has seen huge growth due to a personalized digital strategy in one of the largest digital commerce market in the world.

 

Carrier Enterprise drives B2B experiences with PWAs

Learn how progressive web apps (PWAs) helped Watsco’s Carrier Enterprise business pioneer a headless commerce solution.

 

Going headless with Magento Commerce

Find out how Adobe and Magento Commerce help businesses operate headless commerce to streamline experiences across touchpoints.

 

Integrating Magento with external systems

Get a high-level view of using Magento Web to handle large volumes of data while integrating external systems.

 

Larson-Juhl modernized their business with Magento Commerce

Learn about the decisions that Larson-Juhl made to revolutionize their customer experience across the globe.

 

Page Builder, PWA Studio, and Magento Commerce Experience

Learn about upcoming innovations to Page Builder and PWA Studio that will enhance your Magento Commerce experience.

 

Content

A well-oiled machine: Adobe Experience Manager integrations

Use Experience Manager integrations to get powerful analytic and website optimization products to drive online initiatives.

 

Adobe Experience Manager: Top five digital screen features

Take your digital experience into the physical world and streamline workflows with powerful digital signage, kiosks and more.

 

Albertsons powers personalization with Adobe

Albertsons personalized e-commerce experiences with out-of-the-box and custom Adobe Experience Manager and Adobe Target tools.

 

Better workflows for designers in Adobe Experience Manager

Streamline digital authoring without compromising design integrity or UX governance with tools in Experience Manager.

 

Commerce-led, experience-led, or headless: the best approach

Meet enterprise needs and take advantage of scale, efficiencies, and cross-brand insights with a content and commerce platform.

 

Content Strategy and Architecture

Learn how content architecture is a crucial part of the infrastructure your brand needs to achieve content velocity.

 

Esri’s digital transformation and Adobe Experience Cloud

Learn how Esri, a 50 year old company, transformed their marketing technology, resulting in higher revenue and web traffic.

 

Fantastic Metadata and Where to Find It

Make leaps in content productivity by breaking down the siloes between Creative Cloud tools and Digital Asset Management.

 

Get on the fast track to modernized forms

Learn why a modernized forms experience matters, and how you can do so while gaining 379% ROI for your organization.

 

Get the most out of dynamic media and master rich media

Put your time and energy to better use. Adobe Experience Manager Assets and Dynamic Media AI can free you from tedious tasks.

 

Get to market fast with Adobe Experience Manager Sites

Don’t delay your product launch. Experience Manager has out-of-the-box tools and production-ready components to speed timelines.

 

Learn how others are using data to drive retail growth

Data is arguably the most important asset retailers can use to drive growth. Learn how it’s being optimized in a new study.

 

Make omnichannel experiences a reality — easily

Experience a demo reusing content to see how to easily flow your brand experience across channels with Adobe Experience Manager.

 

Omnichannel shopping experiences using headless commerce

Create streamlined omnichannel shopping experiences using an API-first and microservices approach from Magento Commerce.

 

Retailer Session – Rite Aid and Principal Forrester Analyst

A popular summit session, the retail industry session delivers expert thought leadership for your marketing program.

 

The power of traditional and headless content management

Learn how to deliver localized, real-time content using the Content Services Framework to power any application or channel.

 

The U.S. Census digital transformation

Learn how the U.S. Census Bureau is using Adobe technology to collect the nations’s leading source of quality data.

 

Top innovations in Adobe Experience Manager Sites

Stay in front of the evolving digital experiences revolution with these must-have innovations from Experience Manager Sites.

 

Top innovations: Adobe Experience Manager as a Cloud Service

Achieve SaaS-like agility and meet ever-changing customer expectations using Experience Manager as a Cloud Service.

 

Transitioning Adobe Experience Manager to Cloud Manager

Customers expect new experiences more often. Cloud Manager helps overcome the technological challenges of rapid release cycles.

 

Under Armour and Adobe Experience Manager as a Cloud Service

Hear how Under Armour migrated to Adobe Experience Manager Assets as a Cloud Service to maximize marketing flexibility and scale.

 

Customer Journey Management

Adobe & Econsultancy experience index: digital trends 2020

Explore how leading businesses are taking advantage of digital transformations to set them apart from the mainstream.

 

Create engaging mobile experiences with Adobe Campaign

Learn mobile messaging tips and tricks from Adobe and Telus Communications on how to drive personalization for your customers.

 

Customer Retention with Adobe Campaign and Adobe Analytics

Learn how to integrate Adobe Campaign with Adobe Analytics to connect your customer communication and data insights practices.

 

Get more out of Adobe Campaign Classic

Get concrete guidance and takeaways on Campaign product capabilities that you can use to drive higher results.

 

Get the Best from Your Adobe Campaign Implementations

Learn the workflows and deliveries of Campaign to help your team save time and build a better experience.

 

Healthcare demands exceptional experience

Learn what today’s healthcare companies need to do to connect emotionally with customers through real-time personalization.

 

How to deliver memorable customer experiences through people

Learn how Virgin Atlantic implemented new practices in consistent service while maintaining the friendly spirit they’re known for.

 

If you want to be loved, be lovable

Hear Tim Arthur’s success principles, and about the pitfalls he nearly fell in on his path. Simple solutions to complex problems.

 

Marketing trends that will impact business most in 2020

Get the details on five marketing trends that will impact businesses most in the next 12–18 months.

 

Maximize the latest features in Adobe Campaign

Learn how to take advantage of underused key features in Campaign to increase your customers’ time to value.

 

Prioritize data for today’s cross-channel needs

Get tips and tricks from the Adobe Campaign Value Acceleration team to prioritize and optimize your data.

 

Relevant Sports champions real-time experiences

Get an introduction to Journey Orchestration in Adobe Experience Platform and learn how it helps Relevant Sports succeed.

 

Supercharge your customer journeys with Adobe

Learn how to use Adobe Campaign and Adobe Experience Platform to deliver real-time, individual engagements across channels.

 

The customer experience management mandate

Get an in-depth view of the state of CXM, including the challenges, opportunities, and tips on building a CXM strategy.

 

The revolution of the experience economy

Learn what it takes to innovate in the experience economy through examples of brands that are leading the way.

 

Understanding the “Hackonomy” Era

Discover how professionals can hack their business to create immense value by thinking about and doing things differently.

 

Using data to scale the experience business of travel

Learn about Econsultancy’s 2020 travel industry trends and how travel leaders use data to drive customer experiences.

 

Vitamix achieves relationship marketing with Adobe Campaign

Learn about Vitamix’s approach to customer relationship marketing and how they’ve improved engagement and loyalty.

 

Data & Insights

A crash course in data management platforms (DMPs)

Learn what a DMP is, what it can do, and how it can help make your marketing technology stack even more powerful.

 

A deep dive on data democratization

Actionable tips and tricks to establish and improve data governance in your organization.

 

Accelerating the use of AI in digital transformation

The lessons top marketing executives have in learned throughout their AI journey.

 

Adobe Experience Platform Identity Service

Find out how we’re making it easier to solve personalization in an era of privacy challenges and fragmented data.

 

Adobe Experience Platform powers the future of experience

Experience Platform helps you identify, understand, and engage customers to deliver the future of compelling experiences.

 

Adobe Experience Platform Query Service clarifies insights

Unlock richer analysis and insights about your customer experience through direct query access to omnichannel customer data.

 

Advanced analysis with Adobe Experience Platform and R

Whether you’re new to R or an expert, make exciting data discoveries using interactive visualizations and machine-learning models.

 

Advanced customer data management for marketers and IT

Watch our Real-time Customer Data Platform, powered by Adobe Experience Platform, in action and learn how it helps business grow.

 

Advanced techniques to get the most out of Adobe Analytics

Get real-world examples of how organizations use implementation and analysis techniques to accelerate their analytics programs.

 

Analytics tribe of mentors: achieving rockstar status

Gain practical advice from industry-leading analytics professionals on how to achieve rockstar status in your organization.

 

Attribution game plan for marketing channel measurement

Only organizations with a clear strategy for effective marketing channel measurement will be able to maximize their investments.

 

B2B personalization in VMware’s journey to scale and success

Watch Pranay Goyal, personalization and A/B testing lead for VMware.com, discuss how they’re personalizing for a global audience.

 

Balancing the competing KPIs of your digital business

Learn how Samsung outlined multiple business goals and made razor-sharp definitions of how each webpage contributed to that goal.

 

Become a citizen data scientist with augmented analytics

Learn the tips, tricks for maximizing the value of anomaly detection, contribution analysis, and other AI-powered technologies.

 

Behind every good report is solid data

Get insights into the key concepts and raw data behind many Adobe Analytics reports to improve interpretation and analysis.

 

BMO’s blueprint for proving personalization value

Learn how the teams at the Bank of Montreal grew delivering value through quality, personalized experiences.

 

Breaking Down the Walls between DMPs and Walled Gardens

Learn how Adobe Audience Manager bridges the gap between device-based and people-based channels with people-based destinations.

 

Build customer journeys on Adobe Experience Platform

Find out how Experience Platform can fit into your existing data infrastructure and implementations to get up and running faster.

 

Building and executing a home page personalization strategy

Learn from iconic tech brands as they share strategies for refreshing and revitalizing their brands.

 

Data governance in customer experience workflows

Learn how Adobe Experience Platform provides an industry-first, extensible governance framework for governance policies.

 

Establishing scalable testing in regulated industries

Learn about the role that technology, processes, and people play in supporting brand transformation initiatives.

 

First steps to visualize the customer journey

Analysis Workspace lets you use powerful visualizations to discover valuable insights into the customer journey.

 

Getting started with segmentation

Learn how Adobe Analytics gives you the ability to build, manage, share, and apply powerful audience segments in your analysis.

 

How best-in-class companies do real-time customer experience

Join Deloitte Digital to learn how businesses take back ownership of their data to harness the power of customer information.

 

How to start with Adobe Analytics

Gain an understanding of the building blocks of Adobe Analytics, including metrics, dimensions, campaign attribution, and more.

 

Implementation tips for Adobe Analytics

Tips, tricks, and pro secrets for implementing Adobe Analytics, including data layers, processing rules, and more.

 

Incorporating Adobe Experience Platform into your business

Learn about the integration options with Experience Platform and how it can help you deliver real-time customer experiences.

 

Our best Adobe Analytics tips and tricks

Learn advanced methods for breaking down data silos in Analysis Workspace using the latest updates from Adobe Analytics.

 

Panera reimagined ordering experiences with Adobe Target

Learn how Panera implemented Adobe Target and Adobe Launch to perform powerful A/B testing and geotargeted personalization.

 

Power real-time customer experiences with AI

Learn how Adobe Experience Platform Data Science Workspace solves the challenges you face in delivering personalized experiences.

 

Preparing for a world without cookies

Join Adobe experts as they discuss their takes on a future with or without the cookie, including how we’re tackling the challenge.

 

Tackle complex customer journeys with cross-device analytics

With cross-device analytics in Adobe Analytics, your business can understand customer behavior across mobile, desktop, and more.

 

The future of personalization with Adobe

Join Adobe experts as they discuss how Adobe Target and Adobe Experience Platform are evolving to power personalized experiences.

 

The minimalist approach to more insights

Learn how everyone on your team can use Adobe Analytics to track and use customer behavior, even new employees.

 

The omnichannel future with customer journey analytics

Use Adobe Analytics to explore and understand the entire customer journey, from mobile app to brick-and-mortar interactions.

 

What data storytelling is, is not, and how to do it

Maximize the impact of the insights you’ve uncovered and garner support from stakeholders and executives to sponsor your ideas.

 

With Alloy.js, never tag for an eVar or Mbox again

Learn about the hard work our teams have done to build a new library, Alloy.js, that you can use across Adobe solutions.

 

Personalization

Let Experience League help you develop skills any time

Helps users learn, connect, and grow along a personalized path to success that includes self-help and instructor-led training.

 

Sneaks

Adobe Sneak: Project Access Ace

Give people with visual or cognitive disabilities increased access to your digital content with this AI tool concept.

 

Adobe Sneak: Project Bon Voyage

Use Bon Voyage to see real-time customer-journey performance and take corrective action as needed.

 

Adobe Sneak: Project Clothes Swap

An AI tool that will streamline the content workflow for clothing retailers and optimize the online shopping experience.

 

Adobe Sneak: Project Dually Noted

Project Dually Noted allows collaboration across physical and digital documents by using augmented reality to create a bridge.

 

Adobe Sneak: Project Gluestick

A no-code, drag-and-drop visual interface that will help build custom apps and create automated cross-application workflows.

 

Adobe Sneak: Project Segment Scout

An experimental, natural language-based tool that will allow users of any technical ability to build powerful customer segments.

 

Adobe Sneak: Project Snippets

Project Snippets uses AI to automatically personalize teasers — titles, descriptions, and images — across audiences.

 

The Focus and the F-150

$
0
0

My favorite car is still my second car after college, a 2001 bright yellow Ford Focus ZX3. The reason is simple: it was the absolute minimum car required for my needs. 

Manual cranks served to both to open the windows and as the sole method of air conditioning in the car and the manual transmission enabled me to have fine grained control over how the engine’s meager torque was applied to the wheels.

Do you really need a F-150?

Recently, I re-posted a circa-2015 post about a script I use on a nearly daily basis to manage multiple AEM installations concurrently. I received several responses on different platforms to the effect of “it’s 2020, use Docker”

Using Docker to manage local AEM development to me is like using a F-150 for grocery shopping. Sure, you can toss an infinite number of bags into the back of the cab, but do you need a light truck to haul 50lbs of groceries? 

We can apply this same logic to many different trends within the industry:

  • Do your users benefit from your marketing site being implemented as a SPA?
  • Do your content authors like publishing standard web-pages via a headless architecture?
  • Does converting your tightly integrated service into a web of microservices make it easier to understand and manage?

One of the most memorable experiences in the rubber ducky, as my Focus was affectionately christened, was creeping over a frozen overpass in a near whiteout blizzard south of downtown Chicago. I crept past spun out SUVs in third gear, carefully providing just enough gas to make it up the incline without spinning out myself. Based on the sparse traffic on the toll-pike to Gary, I was one of the few people able to make it out of Chicago that evening.

All abstractions and features have cost, whether it’s an automatic transmission or a hypervisor. Taking the simplest option over the more complex buzzword is usually the best choice, at least, until you can prove that you need a more complex solution.

When a Focus isn’t Enough

While my Ford Focus was the best car to carry me around town, it had obvious deficiencies for some activities. I could drop the clutch and get 0-35 in a hurry, but getting to highway speeds was like coaxing a reticent teenager to get ready for school. And while the hatchback could hold more than anyone would reasonably expect, moving, yard-work or carrying more than two adults was a cramped affair.

Knowing when to replace your simple-as-possible solution for a more complex solution requires a clear-eyed assessment of what the needs are now and what they will be in the near future. Projecting too far out usually ends with over-estimating your future needs and required complexity. Instead of assuming your app will become the next Netflix or Amazon, instead, think of what it will need if growth doubles over the next year?

Once you have the needs, balance that against the cost of the more complex solution, again being realistic. Does anything as complex as Kubernetes “just work” without any care or feeding? Often, you may find that an overly-simple solution will work much longer and with a lower cost than a more complex solution.

There are of course solutions which because of their nature require complexity. A banking app or customer portal probably should be a Single Page Application, just like leveraging Adobe I/O or Amazon Lambda for Microservices may allow you to avoid creating servers or running unrelated code on AEM, but you’ll have to work really hard to show that your marketing website needs a deployment of OpenShift.

The “Right” Architecture

One of the hardest things to grasp in architecture is to grasp how important context is to defining the correct architecture. As humans, we have a tendency to find similarities between situations and use patterns as a mental short-cut. 

The most important part of identifying the correct architecture is to approach the current context without bias and chose the simplest solution that meets the need. 

Locked Down? Learn AEM this April

$
0
0

At Perficient and in my household, our kids and friend’s kids are learning from home. While balancing the needs to keep ourselves and our families happy and healthy we have to ask ourselves, what comes next after these unprecedented times?

For those of us in the Digital Marketing space, the best way to manage this downturn is to build skills and value. Whether you are laid off or slowed down, having additional knowledge will give you an edge. 

Adobe Experience Manager or AEM is Adobe’s market-leading Digital Experience Management platform and as a special this April, you can learn all about AEM with a free month of Pluralsight’s training courses!

Getting a License

Of course, to run AEM you need the binary and a license. If your organization already has AEM, reach out to whomever manages AEM, if not, you can request a copy from Adobe.

Existing Partners:
https://adobe.allegiancetech.com/cgi-bin/qwebcorporate.dll?idx=82357Q

Potential Customers:
https://www.adobe.com/products/request-consultation/marketing-cloud.html?s_osc=70114000002JNwKAAW&s_iid=70114000002JHs3AAG

What’s the Best Track for Me?

AEM projects involve a number of different roles and skillsets. Click on the
roles below to learn more about the role and what courses will help you
reach the next level.


AEM Author

Authors create the content in AEM, leveraging templates, components and
assets to create pages, emails, mobile apps and other experiences.

Learn how to use AEM’s authoring interface to it’s maximum extent and what
features of AEM you can leverage to make your content more flexible and
maintainable.


AEM Business Practitioner

Business Practitioners are experts of the use of the Adobe Experience
Manager platform and help the business translate requirements into
technical stories.

Learn how to use the AEM platform and how to solve business problems with
the AEM technology.


AEM Back-End Developer

Back-End Developers work inside the CMS to create services and components
to render content to different channels including websites, mobile apps,
emails, etc.

Learn how to use OSGi, the JCR and Apache Sling to structure and create
powerful content-driven applications in AEM.


AEM Front-End Developer

Back-End Developers create components, HTML, CSS and JavaScript to create
experiences for different channels including websites, mobile apps,
emails, etc.

Learn how to use HTL and the AEM component framework to create dynamic but
flexible content-driven experiences.


AEM Technical Overview

(53 mins)
Adobe Solutions Consultant Diana Breen
demonstrates some of the key functionality in Adobe Experience Manager.

AEM Authoring Fundamentals

(2h 5min)
Discover how you can use Adobe Experience Manager
to create and manage the content of rich responsive web experiences.

AEM Authoring Building on the Fundamentals

(1h 57m)
Building upon AEM Authoring Fundamentals course,
this course will expand your skills and knowledge for authoring in AEM
into more advanced AEM topics.

AEM Developer Skill Assessment

(20 min)
Are you a novice or expert? Find out in about 20
questions.

AEM Developer Fundamentals

(2h 35mn)
An introductory developers course about how to get
started with AEM 6.2. You’ll learn the tools that developers commonly use,
the terminology, the history, and how to install and use AEM in practice.
Software required: Adobe Experience Manager 6.2.

AEM Building Full Stack Components

(2h 5min)
Learn to leverage AEM’s rendering lifecycle to architect solutions based
on designs. You’ll work with components, editable templates, and page
properties to build content structures that are returned as JSON from an
API endpoint.

Fundamentals of building from Projects in AEM

(3h 34 min)
This course will teach you how to build and
debug Adobe Experience Manager (AEM) packages and how you can use them to
import and export content to and from AEM’s Java Content Repository (JCR.)
Software required: Adobe Experience Manager, Maven.

Extending AEM Foundations

(2h 9 min)
Adobe Experience Manager or AEM is Adobe’s market
leading web content management and digital marketing platform. This course
is ideal for backend developers and focuses on developing services,
servlets, and jobs in AEM.

Extending Adobe Experience Manager Advanced

(6h 14m)
AEM is built on OSGi, Sling, and a Java Content
Repository. This course will teach you to harness these technologies to
extend AEM.


Develop Websites and Components in AEM

(3h 32m)
This course will teach you the basics for developing websites using AEM,
including structure component development, understanding Apache Sling and
the JCR, and using HTL.


Develop Websites and Components in AEM Advanced

(3h 37m)
This course teaches you the advanced methods for developing websites,
including using AEM’s responsive grid, advanced content component
development, debugging, and testing.

Where do I get help?

Learning AEM is no simple thing, the platform, while easy to use is large and complex.

Some great resources for people looking to get started with AEM include:

And as always feel free to leave a comment or send me a line on twitter @KlcoDanR and I’ll do my best to respond! Best of luck in your learning of AEM and getting through the lockdown!


Become an Adobe Digital Marketing Expert this April

$
0
0

In my previous post, Locked Down? Learn AEM in April, I talked about how PluralSight’s #FreeApril program gives you access to great training on Adobe Experience Manager.

For those of us also working with more than Content Management, #FreeApril provides some great training for you too! PluralSight has great courses on Adobe Analytics and Target for you to build expertise on these powerful solutions. Unfortunately, there’s not a Pluralsight track on Launch, by Adobe so I pulled from the great free training Adobe has published on YouTube for this integral Tag Management solution.

Getting a Sandbox

To use Analytics and Target you will need a Sandbox or non-Production instance. If your organization already has Adobe, reach out to whomever manages the access to the Adobe Experience Cloud, if not, you can request a copy from Adobe.

Existing Partners:
https://solutionpartners.adobe.com/sandboxrequest/

Potential Customers:
https://www.adobe.com/products/request-consultation/marketing-cloud.html?s_osc=70114000002JNwKAAW&s_iid=70114000002JHs3AAG

What’s the Best Track for Me?

Digital Marketing projects involve a number of different roles and skillsets. Click on the roles below to learn more about the role and what courses will help you reach the next level.

 


 

Business Practitioner / Analysts

Business Practitioners are experts of the use of the Adobe Analytics and Target and help the business translate requirements into stories and leverage the tools to measure and derive value on campaigns and marketing efforts.

Learn how to use the Adobe Analytics and Target solutions and how to solve business problems with the Adobe technologies.

 


 

Developer

Developers setup, configure and integrate the Adobe and 3rd party digital marketing tools across channels including websites, mobile apps, emails, etc.

Learn how to develop solutions to track, measure and optimize Digital Marketing solutions using Adobe Analytics and Target.

 


 

Multi-Solution Architect

Multi-Solution Architects design and lead the implementation of end-to-end solutions for Digital Marketing problems.

Learn how to develop solutions to track, measure and optimize Digital Marketing solutions using the Adobe Experience Cloud.

 


 

Introduction to Launch, by Adobe

(20m)
Launch by Adobe is a next-generation tag management system that does much more than tag management. Launch by Adobe allows any web technology provider, such as LinkedIn Marketing Solutions, to build and own integrations with Launch.

Introduction to Adobe Analytics

(57m)

Introduction to Adobe Target

(1h)
Overview of the Launch by Adobe Platform.

Adobe Analytics Implementation Fundamentals

(1h 16m)
Adobe Analytics is the industry leading digital analytics solution for reporting and insights of your company’s customer experience. This course teaches you the basics of designing, configuring, and coding a robust implementation of Adobe Analytics.

Adobe Target Optimize Digital Experiences Fundamentals

(1h 36m)
Adobe Target empowers companies with tools needed to drive more relevant experiences and engagement for customers. This course teaches you the basics of conversion optimization, activity creation, and managing offers stored in the customer library.

Adobe Analytics Building on the Fundamentals

(2h 29m)
Adobe Analytics is an industry-leading analytics solution. In this course, you will learn about the array of reports available and how to break them down, as well as creating calculated metrics, performing segmentation, and customizing dashboards.

Adobe Analytics Analysis Workspace

(2h 5m)
In this course, Adobe Analytics Analysis Workspace, you’re going to uncover everything you need to know about Analysis Workspace including: building projects, creating data visualizations, and creating complex segments and metrics.

Adobe Target Optimize Digital Experiences Building on the Fundamentals

(1h 49m)
Adobe Target provides you the necessary tools to start driving growth, personalization, and more relevant experiences to your customers. This course teaches you the necessary steps to build your audience segments, analyze report results, and more.

Adobe Target Premium Recommendations

(1h 59m)
Adobe Target is designed to make testing and adopting a culture of optimization straightforward and scaleable. This course covers the basics of using Target Recommendations including basic navigation and creation, syncing data feeds, and more.

Adobe Target Automated Personalization

(1h 36m)
Every day, millions of people visit websites. Everyone has different tastes and interests, and each has a different likelihood of engaging and buying. This course teaches you the power of automated personalization in web marketing using Adobe Target.

Adobe Analytics Implementation Intermediate

(1h 23m)
Adobe Analytics one of the most popular digital analytics tools in the world and product of the Adobe Marketing Cloud. This course will teach you some of the more advanced methods of implementing Adobe Analytics on your website.

Adobe Analytics Implementation Advanced

(2h 17m)
Implementing Adobe Analytics properly is one of the most crucial things you can do for your business. This course will teach you advanced implementation of Adobe Analytics, including data manipulation, processing rules, validation, and more.

Launch by Adobe Playlist

(3h 42m)
Overview of the Launch by Adobe Platform.

Using Adobe Analytics and Shared Audiences with Adobe Target

(1h 11m)
Learn how to use Adobe Analytics reporting to analyze Target activities and how to use the People core service to achieve a complete, 360-degree understanding of customers to drive more relevant experiences for better engagement and improved ROI.

 

Where do I get help?

 

Learning the Adobe Experience Cloud solutions is no simple thing, the platform, while easy to use is large and complex.

Some great resources for people looking to get started with Adobe Experience Cloud include:

And as always feel free to leave a comment or send me a line on twitter @KlcoDanR and I’ll do my best to respond! Best of luck in your learning of about the Adobe Experience Cloud and getting through the lockdown!

Deploying Sling CMS Apps with GitHub Actions

$
0
0

I use Apache Sling CMS for my personal site, so I can easily author my blog posts from nearly anywhere. Unfortunately, since this is a personal site, I’m not exactly spending the dollars on building out a full Continuous Integration / Delivery process.

Enter GitHub Actions

GitHub provides Actions, a free for limited use solution for executing code on Git repository events. With GitHub Actions, I can set up a pipeline to automatically deploy my code on changes to the master branch.

Actions Pipeline Definition

The pipeline is defined as a YAML file, deploy.yaml, under the directory .github.workflows in my code repository.

# Deploys code updates to my personal site on push to the master branch

name: Deploy on Push
on:
  push:
    branches:
      - master
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/[email protected]
    - name: Set up JDK 1.8
      uses: actions/[email protected]
      with:
        java-version: 1.8
        server-id: github
        settings-path: ${{ github.workspace }}
    - name: Build and Deploy
      run: mvn install -B -s $GITHUB_WORKSPACE/settings.xml -PautoInstallBundle,ci
      env:
        SLING_HOST: ${{ secrets.DEPLOY_HOST }}
        SLING_USERNAME: ${{ secrets.DEPLOY_USERNAME }}
        SLING_PASSWORD: ${{ secrets.DEPLOY_PASSWORD }}
        SLING_PROTOCOL: https
        SLING_PORT: 443 

A few things to note about the pipeline definition:

  • The definition on object contains my invocation definition, in this case, pushes to master
  • In the Maven command, I’ve added a custom profile ci, this profile will copy the environment variables seen in the env object
  • The env parameter to Build and Deploy contains the parameters, including secret parameters for deploying the code

Keeping Secrets, Secret

In order to deploy my code, I have to provide the CMS authoring URL and the username / password for an Administrative user. Naturally, I don’t want to put this information on the web. With GitHub Actions, I can create secrets within the project:

Configure GitHub Action Instances

And then use the ci profile in the POM to copy those secrets as environment variables from the env parameter in the Github Actions pipeline into the Maven project properties.

<profile>
    <id>ci</id>
    <properties>
        <sling.host>${env.SLING_HOST}</sling.host>
        <sling.port>${env.SLING_PORT}</sling.port>
        <sling.username>${env.SLING_USERNAME}</sling.username>
        <sling.password>${env.SLING_PASSWORD}</sling.password>
        <sling.protocol>${env.SLING_PROTOCOL}</sling.protocol>
    </properties>
</profile>

Once this is all wired up, you can push in a small change to execute the pipeline and you should see the deployment succeed. Note that the secrets are hidden from the logs so even if a user can see the pipeline, they cannot see the secret values.

Execution Result from GitHub Actions

With Github Actions, you can not only host a small Apache Sling CMS website for free, but you can even have a reliable CI/CD process!

Adding Marketo Forms in AEM Pages

$
0
0

Adobe’s acquisition of Marketo brings exciting opportunities to extend Adobe’s considerable marketing capabilities with a tightly integrated B2B and ABM solution. As a leading partner of both Adobe and Marketo, Perficient is leading the push to integrate these solutions.
One of the main integration points between AEM and Marketo is via Marketo’s JavaScript forms. We built a component to integrate Marketo JavaScript forms into AEM and contributed it into ACS AEM Commons so now you can add Marketo forms into AEM pages as simply as adding and editing any other component.

See How Easy it is to Embed a Marketo Form

 

 

Installing the Marketo Form Embed AEM Component

The Marketo Form Embed Component is implemented as a AEM WCM Core Components Embed Embeddable and therefore requires the following dependencies:

Once you have installed the dependencies, you can use the Marketo Form Embed on any page you have enabled the Marketo Cloud Configuration.

Using the Marketo Form Embed AEM Component

The Marketo Form Embed AEM Component allows you to search and select from the forms available in Marketo. But the Marketo Form Embed can do far more than just embedding a form, you can set the redirect URL, set variables and even add custom scripts.
Read more on using the Marketo Form Embed on the ACS AEM Commons website.

1st Party Adobe Launch with ACS AEM Commons

$
0
0

Adobe has supported CNAME configuration for Adobe Analytics and Adobe Target for some time now, but unfortunately doesn’t provide the same for Launch by Adobe. The latest version of ACS AEM Commons, version 4.4.0, includes a new feature to fetch remote files and serve them via AEM. The primary use case I implemented this feature for was to automatically download the Launch by Adobe script so it can be served from the primary domain.

Why do we need to serve Launch from our domain?

Protecting consumer privacy and providing an excellent consumer experience is a delicate balance. The confluence of Safari’s Intelligent Tracking Prevention, the widespread adoption of adblockers and Google Chrome’s coming blocking of third party cookies have limited Digital Marketer’s ability to optimize and personalize experiences as progressively they make it harder to effectively measure and optimize user interactions.
Research has shown, however, that the problem is not that consumers are blanket against personalization or even advertising but that they despise badly done advertising and personalization.
Types of Digital/Mobile Ads US Consumers Find Annoying, Jan 2019 - eMarketer
How do we as Digital Marketer’s deliver better ads and experiences? Again, based on market research, the major pain points for consumers are irrelevance and performance. Both of which we can address with the Adobe Experience Cloud suite of tools.
Performance is a full, separate discussion, so let’s focus on the basics we need to be able to create personalized, relevant experiences. With the Adobe Experience Cloud, this means having Adobe Analytics, Launch and Target working to effectively personalize our digital experiences. But how do we ensure that if AdBlockers or even the browser themselves can block the cookies we use or even the tools themselves?

CNAMEs with Analytics and Target

Adobe has an answer for Adobe Analytics and Adobe Target. Each tool supports setting up and serving the scripts from a CNAME record on your own domain. This changed the cookies from third to first party from the browser’s perspectives and bypasses the domain name lists in Ad Blockers. For more information read:

All of this is good, but if an Ad Blocker blocks adobedtm.com the Launch doesn’t fire and the whole thing falls apart.
Unfortunately, Adobe does not provide a CNAME’ing solution for Launch, so we have to either proxy or otherwise download the Launch script, which is where the new ACS AEM Commons feature comes in.

Syncing Launch into AEM with the ACS AEM Commons File Fetch

The File Fetch service is configured via OSGi, so work with development teams to configure the URL for the Launch script and the path within AEM Assets to save the file. Based on the configuration, the File Fetch service will fetch a file from a URL, load the file into AEM Assets as a dam:Asset and then replicate the asset. It uses the HTTP responses status and will not perform any updates if Launch returns a 304 status code so it will only update when needed.
Since the Launch script is automatically replicated on update, you should not need further Dispatcher cache configuration, however you should ensure that the client-side cache headers will not cache the Launch script.
File Fetch Configuration
From there, include the Launch script from AEM Assets via the path like you would for any other Launch script and to browsers and visitors, Adobe Analytics, Launch by Adobe and Adobe Target are all indistinguishable from any other scripts on the website.

Checklist for a Successful Website Migration

$
0
0

Relaunching or migrating a website is an intensive process and it’s very easy for small issues to slip through the cracks. Especially when teams either lack institutional knowledge, are gapped on critical skills or are working against an aggressive timeline.

Here are some things to watch out for I’ve seen observing and being involved in several dozen migrations over the course of my career.

Get the Experience Right

The most critical part of relaunching or migrating an experience is the experience itself. 

Coordinate with Content Authors

Content authors frequently know the site better than business stakeholders. During the migration / refresh / whatever you call it, make sure to pull content authors into the process, after all they know the content and use the system of the daily basis.

Rather than surprising content authors with a new launch and hoping it matches their requirements, pull them in ahead of time to validate website authoring functionality as well as the content. A good content author is passionate about their content and willing to pitch in to validate so you can fix issues ahead of time instead of scrambling afterwards.

Confirm Content Renders Correctly

This may seem like an obvious basic, but it’s surprising how often someone has not checked every page on the website to make sure that it’s rendering correctly. Automated tools can certainly help here, a good crawler like Screaming Frog, is a must have in every migration toolbox.

If you are doing a 1:1 migration, you can leverage AI based automated testing tools to compare the website before to after migration, however if you are changing the UI this is not a viable option.

In the end, nothing compares to having good ol’ fashioned eyeballs looking at the website and making sure that it’s subjectively rendering correctly. 

A simple, but effective mechanism for verifying website rendering is to crawl the website, create a spreadsheet of the URLs in a share such as Sharepoint or Google Drive and then coordinate with a QA team / content authors to review and check off each URL. 

While testing, ensure to use multiple browsers and screen sizes to ensure the content appears correctly no matter the device. Responsive design is table stakes so there’s no good reason to not have images scale down to the viewport.

Validate Website Interactivity

Beyond initial content rendering, pay special attention to interactive functionality on the website such as:

  • Web Forms
  • Quizzes
  • Tabs / Content Switchers
  • Personalization

Prior to migration, the team should identify all such functionality, document a list of interactive functions and perform regression testing prior to re-launch.

Don’t forget Web Search!

One frequent miss is website search. This is one of the top features in most websites, but can easily be missed in preparation, planning or testing. It’s especially important to remember if the content needs to be re-indexed once the site is cut-over to plan enough time.

Track Consistently

You need reliable data to prove that a migration is successful and identify any issues. Therefore, it’s critical to ensure tracking is consistent and accurate pre and post-relaunch. 

Validate Analytics Tracking

Along with testing the experience, the QA team should be validating that Analytics tags are firing correctly and conveying the correct data. This should be comprehensive, not just testing a small sub-set, but validating across at least a large, representative sample of the experiences.

Preserve Tracking Consistency

There’s no good reason to abandon the website data from before migration so make sure to use the same Google Analytics Property or Adobe Analytics Report Suite. If there are issues with the way tracking is implemented, it’s better to fix them before migrating to ensure good data than wait until the migration is complete.

Put the Website through the Wringer

Before the site goes live, check with every tool you can think of to ensure the website will not crash, be compromised or otherwise not work as expected.

Search Engine Optimization

Crawl the whole site to identify metadata issues, bad redirects and other SEO issues. Make sure to use tools like a SERP Simulator and Rich Results tester to ensure the metadata is correct on the pages.

Validate Redirects

Prior to migrating / relaunching the site, you should ensure you have a full set of the URLs and legacy redirects on the site. Prior to migration, validate that each redirect leads to the expected page.

Security Testing

Security and penetration testing is a whole realm of expertise and the implementation varies drastically between platforms. From a basics perspective, during the migration and relaunch development, make sure to comply with the OWASP Top 10. On top of development, ensure you have all of the recommended security headers to enforce SSL, prevent XSS and prevent framejacking and are following best practices for securing the web server and infrastructure access. 

System Performance

There are a number of tools for testing system performance from Apache Benchmark to JMeter. Find the appropriate tool for your use case, test the site until it breaks and improve until you are a few standard deviations above the expected traffic.

User Performance

Having a well-performing server is great, but the front-end code will drastically influence the perceived website performance. Google PageSpeed Insights and Google Lighthouse are great tools for evaluating perceived performance.

Accessibility

Building a website accessible to everyone is both a business and moral imperative. Browser tools such as WAVE or ANDI can help perform technical accessibility compliance tests for websites and should be used to validate the delivered code as well as the authored content.

Some common Accessibility issues include designs that are difficult to read, insufficient contrast and non-required accessibility metadata.

Not Just a Website

Finally, a relaunch / migration should consider more than just the website. Make sure that 3rd party services, internal / external branding and communications are aligned to support the relaunch. 

Ensure a Successful Relaunch

There’s a lot to consider when approaching a website migration or relaunch. Perficient’s expert consultant teams can support your success no matter the platform.

4 Useful Features of Composum Browser

$
0
0

Composum Browser is an Open Source JCR Node browser for Apache Sling which bills itself as “the Swiss knife for the repository”. Composum Browser is included in Apache Sling since Sling 9 and is available in all versions of Apache Sling CMS.

While Composum Browser certainly supports navigating the JCR repository structure in Apache Sling, it can do much more. Here are four useful features in Composum Browser:

Edit Files

Editing files in the repository using Composum is pretty straight-forward. First select the node for the file, select the Text / Code View and then select the Edit Text button which will open the editor.

Edit a File in Composum

Run Groovy Scripts

Groovy scripts are tremendously helpful to run ad-hoc scripts for extracting report data, performing bulk updates on the fly or debugging issues.

To execute a Groovy script, create a file with the .groovy extension and then execute the script in the Composum editor. One important thing to note is that you want to make sure your return is not session-bound, because it closes the resource resolver before printing the result. 

Using Composum Groovy Executor

Default Variables

The Composum Groovy script executor binds the following variables for your use:

OSGi Services

Since Groovy is executing in the context of an OSGi container, you may want to retrieve an OSGi service, unfortunately, it’s not quite as easy as the AEM Groovy Console, but it can be done:

import org.osgi.framework.*;
import org.apache.sling.api.resource.*;

BundleContext bundleContext = FrameworkUtil.getBundle(ResourceResolverFactory.class).getBundleContext();
def serviceReference = bundleContext.getServiceReference(ResourceResolverFactory.class);
def service = bundleContext.getService(serviceReference);
println service;
bundleContext.ungetService(serviceReference); 

Installing Groovy

Naturally, to use the Groovy script execution feature in Composum Browser, you need Groovy installed. At the time this post was written, Groovy is not installed in the Apache Sling Starter 11 or Sling CMS 0.16.2. The easiest way to install Groovy is to install Groovy All 2.4.19. In future versions of Sling CMS and Sling Starter the expectation is Groovy 3 will be pre-installed.

View System Nodes

In more recent versions of Composum (at least 1.12+) you can see the system nodes in the JCR, including:

  • JCR Namespaces
  • JCR Node Types
  • Supported Privileges
  • Privilege Storage
  • Version Storage

To see the system properties change the Filter for tree option to unfiltered and then expand /jcr:system.

View System Configuration in Composum

Download Query File

Composum supports executing queries in the search bar. In addition, you can download the results of the query by selecting the Export Query Result dropdown on the right-hand side of the search bar. The export option supports JSON (full tree), CSV (summary), and TSV (summary). 

Exporting Query Composum

Composum Node Browser is a powerful tool for managing Apache Sling based repositories. Check it out via the Apache Sling Starter or Sling CMS  or download Composum directly from GitHub.

One Tool to Configure Them All: Sling RepoInit

$
0
0

The core paradigm for the Java Content Repository (JCR), the repository for Adobe Experience Manager (AEM) is Everything is Content. This principal drives the flexibility which made AEM a market-leading solution. It does, however, come with a downside, managing the initial repository state is challenging since the repository state is a combination of the content, code and configuration for an AEM application.

Managing this initial state is important for developers and administrators to be able to stand up local instances, standing up new environments and keeping environments in sync.

Multiple teams have build parts of a solution to this problem, including:

Recently though, another solution has come to the fore for configuring the initial repository state.

Apache Sling RepoInit

Apache Sling RepoInit has been a feature of Apache Sling since 2016, but historically has been used for base repository initialization as a part of slingstart / launchpad starter, not for project-level repository initialization.

Version 1.1.6+ of Sling JCR RepoInit includes the ability for registering configurations with RepoInit scripts or references. This brings Apache Sling Repoint out from just being a base repository initializer to enable use by project teams.

With Sling Repoint, we have a consolidated grammar to:

  • Create groups
  • Assign permissions
  • Create paths
  • Set properties
  • Assign group membership
  • Create OSGi configurations
  • Create service users

Enabling RepoInit in AEM

Note: this is based on AEM 6.5.5, newer service packs and AEM Cloud Service may have different dependency versions.

AEM includes a fairly old version of Sling RepoInit, in order to leverage the full power of Sling RepoInit, you need to upgrade to the following dependencies for AEM 6.5.5:

Configuring RepositoryInitializer

We can configure RepoInit scripts to be executed by registering a configuration of the org.apache.sling.jcr.repoinit.impl.RepositoryInitializer Service Factory, however there is a challenge with how this service resolves the references. Each reference is expected to be in the form of a URL and OSGi supports exposing bundles as URLs, however when Apache Felix resolves the URL of a bundle in the URLHandlersBundleStreamHandler, it expects the URL host to be the UUID of the bundle, not a stable ID such as the Bundle’s Symbolic Name.

I have opened a ticket to get this resolved, but until that’s complete and Generally Available, the best option is to create an OSGi Component to resolve the Bundle URL and initialize the configurations when the bundle starts.

Below is a sample implementation showing how this could be done, showing how to support multiple RepoInit files in a single bundle:

Now that you have Sling RepoInit setup and working, you can create any number of RepoInit scripts in the ./src/main/resources/repoinits folder and on bundle installation they’ll be live reloaded.

With the RepoInit scripts, you can set up your repository completely with commands like:

# Create paths
create path /content/my-site(cq:Page)

# Create a group
create group my-site-admin
set ACL for my-site-admin
    allow crx:replicate, jcr:lockManagement, jcr:versionManagement, rep:write on /content/my-site
end

# Add group members
add my-site-admin to group administrators

# And More! 

Apache Sling RepoInit is the consolidated way to initialize a repository for any Apache Sling-based system including AEM 6.5, AEM as a Cloud Service and Apache Sling CMS. With RepoInit, you can be assured that your solution will work on both the current version of AEM as well as AEM as a Cloud Service.


Upcoming Webinar – Sling RepoInit

$
0
0

Curious about using Sling RepoInit? Want to learn more in depth about how Sling RepoInit can enable your AEM DevOps team to manage the initial repository state in code?

I’ll be leading a virtual discussion on Sling RepoInit with the Detroit AEM Meetup on Thursday July 9th from 6:00 – 6:50 PM EST.

This talk will:

  • Introduce the benefits Sling RepoInit as a provisioning method
  • Compare Sling RepoInit to other methods of initializing a repository
  • Show how to manage permissions, configurations, and initial content using RepoInit
  • Demonstrate using Sling RepoInit for both AEM as a Cloud Service and AEM 6
  • Discuss a method for making it easier to manage Sling RepoInit configurations and future options 

More info about Repoinit: https://sling.apache.org/documentation/bundles/repository-initialization.html 

This talk will be useful for AEM Technical Experts, Architects, and Developers, especially those interested in AEM as a Cloud Service.

Register Here

Download / Watch the RepoInit Detroit Adobe Experience Meetup

Pulling Private Dependencies with Cloud Manager

$
0
0

For customer’s using Adobe Managed Services, Cloud Manager has become the de facto system for deploying code to their Adobe Experience Manager environments. Among Cloud Manager’s many deficiencies is the inability to fetch artifacts from private remote repositories.

The good news is there’s a workaround for internet-facing artifact repositories. Unfortunately, non-internet facing artifact repositories are not supported as Cloud Manager spins up Docker containers to run the actual build process and they do not support a pool of IPs or virtual networks for the containers. According to feedback from Adobe, the Cloud Manager team is waiting on a feature from Microsoft Azure to allow all Cloud Manager Containers to be assigned an IP address out of a defined range. Once this feature is added to Azure, AMS will be able to provide an IP range to allow access to the artifact server.

Why Would I Want to Do This?

I think it’s certainly worth asking: why not just follow Adobe’s model with Cloud Manager and put everything in one big Maven project?

There are a few good reasons you may not want to follow the Adobe pattern:

  • You are a larger enterprise and have multiple teams working on multiple different projects in parallel and consolidating on a single project would make releases and merging very difficult to manage
  • You have integrations or dependencies on upstream teams for things like service connections, UI frameworks or custom integrations
  • You want to integrate additional checks into your code not supported in Adobe Cloud Manager, but don’t want to have to run a build twice

Connecting to a Private Artifact Repository

The solution involves two parts:

  • Working with your CSE to configure Environment Variables
  • Overriding the settings.xml in the project

Custom Environment Variables

First, Adobe has a feature where your CSE can request the provisioning of Custom Environment variables, this allows you to specify private variables such as the connection URL, username or password. This will require your CSE to open a support ticket with an internal team at Adobe, so you will simply need to provide the keys and values to the CSE to open the ticket.

According to an inside source, this may come as a feature in the API, which is currently available but not supported in Cloud Manager.

Setting Your Artifact Server

In order to resolve the dependencies from your private repository, Maven needs three configuration steps.

First, add the repository into the pom.xml, for example:

Next, you’ll need to override the settings.xml to do this, create a file in the root of the project called .mvn/maven.config with the contents:

-s ./settings.xml

This will instruct Maven to use a settings.xml file located in the project directory.

Finally, create a settings.xml and use the Custom Environment variables to set the username and password for the server (the ID must match the pom.xml) as well as providing the default adobe-public artifact repository:

Once all the configurations are in place, trigger a Cloud Manager build and you should see Cloud Manager connecting to your private artifact repository to download the dependencies for the project.

Debugging Dispatcher Deployment Issues with Cloud Manager

$
0
0

Adobe’s Cloud Manager is quite feature rich in unexpected features. One of these features is that it is in no way obvious how one can diagnose dispatcher deployment issues when run through Cloud Manager.

Note: due to AMS’ inconsistent environment setup, this article may not apply to everyone, but if you rely on your CSE to debug dispatcher deployment issues, this probably does for you. 

Why did my Cloud Manager Build Fail to Deploy Dispatcher?

The problem is that to test your deployment, Cloud Manager will unpack the deployment on the instances and restart to validate that the configuration is correct. If this fails, it rolls back the changes and logs a single message to the Cloud Manager log.

2020-07-07T17:15:40+0000 Failed to deploy dispatcher on instance dispatcher1eastus. ActionId=[some-action-id]

Based on that you’re clear on what you need to do? Of course not! Normally, I’d expect the details of restart / issues to be written to a log accessible via the build tool, but Adobe Managed Services in their endless desire to defy normal expectations choose not to do this. 

Apparently, Adobe Engineering is considering on a fix for this, but have not specified a timeframe or committed to the fix. At this point, you could reach out to your Customer Success Engineer, but really?? Isn’t this something you should be able to self service?

Digging into the Systemd Journal

We know that Cloud Manager is using systemd to restart the Apache httpd web server powering the dispatcher, so theoretically the status will be written to the systemd journal. Unfortunately, as AMS will only provision non-privileged users, our user can only see their own journal, not the journal for the full system including Cloud Manager.

Seemingly a dead end. However, if you execute sudo -l, you will see a number of interesting things commands you can execute: 

Among these is:

(ALL) NOPASSWD: /bin/journalctl -* *

Note the format, we need to specify two parameters including a flag to call journalctl with sudo… No idea why and I would not expect AMS to set up every environment the same way, so your milage will vary.

Based on this information, we can execute the following command, which will pull the most recent systemd journal entries for httpd, including the reason why Apache httpd failed to start with your dispatcher changes:

sudo /bin/journalctl --system -u httpd

For example, if I missed a variable DISP_LOG_LEVEL, I would see something like:

[date] [instance] httpd[8753]: [Wed Jul 22 21:47:34.643291 2020] [core:warn] [pid 8753] AH00111: Config variable ${DISP_LOG_LEVEL} is not defined
[date] [instance] httpd[8753]: AH00526: Syntax error on line 21 of /etc/httpd/conf.d/dispatcher_vhost.conf:
[date] [instance] httpd[8753]: Invalid value for directive DispatcherLogLevel, expected integer
[date] [instance] systemd[1]: httpd.service: main process exited, code=exited, status=1/FAILURE
[date] [instance] kill[8755]: kill: cannot find process ""
[date] [instance] systemd[1]: httpd.service: control process exited, code=exited status=1
[date] [instance] systemd[1]: Failed to start The Apache HTTP Server.
[date] [instance] systemd[1]: Unit httpd.service entered failed state.
[date] [instance] systemd[1]: httpd.service failed.
[date] [instance] systemd[1]: Starting The Apache HTTP Server...
[date] [instance] systemd[1]: Started The Apache HTTP Server.

Note that we see the dispatcher starting, this is because Cloud Manager watches to see if Apache restarts as expected with your dispatcher changes and will roll back the build if it fails. 

Now that you can pull dispatcher deployment logs, you can diagnose dispatcher issues without waiting for hours for a CSE to respond to email.

Want to learn more about Cloud Manager?

You may want to check out my previous Cloud Manager post on pulling private dependencies in Cloud Manager.

Exploring the Sling Feature Model: Part 1

$
0
0

In my previous post about AEM as a Cloud Service, How AEM Scales, I discussed the new Sling Feature Model which is a key part of how Adobe scales their new AEM as a Cloud Service solution using Kubernetes. 

The goal of the Feature Model is to create a better way to provision Sling instances by creating a more descriptive, flexible grammar for building targeted instances instead of building a fat JAR file and adding bundles after the fact.

This week / weekend, I took some time to finally upgrade my pet project, Sling CMS to use the Sling Feature Model and wanted to take a moment to document the process and my learnings.

Once you understand the process and model, the process of converting a Provisioning Model-based project to Feature Model is straight-forward. To get started, I followed along with the Feature Model How-To Guide and then dug into the code for the Sling Kickstart and (Feature Model compatible branch of) Sling Starter

Converting a Provisioning Model Project to Feature Model

The first step was using the sling-feature-converter-maven-plugin to convert the legacy Provisioning Model configuration to the Feature Model. To do this, I created a temporary sub-module to execute the conversion process:

https://github.com/apache/sling-org-apache-sling-app-cms/blob/SLING-8913-multiple-instance-types/converter/pom.xml

I’ve modified this POM based on the Sling Kickstart POM to look up the Provisioning files in the builder sub-module of Sling CMS, configured it with my desired artifact information and added a step to create an aggregated Feature Model so I could validate the conversion.

Interestingly, I found that mvn clean after failed builds seemed to often result in Maven dependency resolution issues (I’m assuming because it was evaluating the Feature files) so rm -rf target/ was my go to cleaning action.

To run the conversion, I can execute mvn clean install -Dbnd.baseline.skip=true. Once the process is complete, the feature files can be found under target/fm and you can validate by downloading the Feature Model Launcher and running:

java -jar org.apache.sling.feature.launcher-1.1.4.jar -f target/slingfeature-tmp/feature-slingcms.json

One feature file will be created per Provisioning Model file and the contents of the Provisioning Model file will be transformed into the format used by the Feature Model.

During this step, I also found that the Sling Feature Model is more strict than the Provisioning Model and called out a few mistakes in the Provisioning Model, including duplicate configurations (for example I had duplicated the configuration org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-sling.rewriter).

Once I successfully converted the Provisioning files to Feature Models, I copied them from target/fm into a new features submodule and updated the Artifact ID accordingly. 

Aggregating the Feature Model

The purpose of the new feature submodule is to produce the final artifacts based on the Feature Model files. The first and easiest artifact is to create an aggregate feature to execute with the Sling Feature Model Launcher. 

To do this, I added the following goals from the slingfeature-maven-plugin plugin:

  • aggregate-features – aggregate the feature files into a single feature file
  • analyse-features – analyse the feature files to ensure they didn’t have any issues
  • attach-features – attach the feature models as Maven artifacts
  • attach-featurearchives – attach the feature archives as Maven artifacts

The outcome of all this is a consolidated Feature Model and Feature Archive for Sling CMS based on the input Feature Model files we migrated from the Provisioning Model. Before we can complete the process however, there are a few issues which need to be resolved. 

Feature Models vs. Feature Archives

Quick segue about Feature Archives. By default the Sling Feature Model will download dependencies as artifacts from the Maven repository. To avoid having to download files at runtime, package private artifacts or to enable Sling Features to run offline Sling Feature Model supports Feature ARchives or FAR files. The FAR file packages the Feature descriptor and binary dependencies into a ZIP archive.

Feature Archives can be used in the same manner as Feature Models, reference the path to the Feature Archive instead of the Feature Model in the -f or -sf flags for the Sling Feature Launcher.

Resolving Feature Errors

The analyse-features goal of the slingfeature-maven-plugin flagged a number of issues with the legacy Sling CMS Provisioning Model setup around the start order of the bundles. Each error will be logged with a message like the following:

[ERROR] Artifact org.apache.sling:org.apache.sling.servlets.resolver:2.7.2 requires [org.apache.sling.servlets.resolver/2.7.2] osgi.ee; filter:="(&(osgi.ee=JavaSE)(version=1.8))" in start level 20 but no artifact is providing a matching capability in this start level.

Blindly updating start order to resolve the build issues caused Sling to not be able to render content. I would recommend making changes one bundle (or set of bundles) at a time and validating with every change. In the Sling CMS setup, the primary problem I had was with the ordering of the Apache Sling integration with Apache Oak and the bundles org.apache.sling.jcr.jackrabbit.usermanager and org.apache.sling.jcr.jackrabbit.accessmanager.

Version Variables

One of the conveniences in the Provisioning model is Variables, which allow you to set shared variables for things like version numbers when you have multiple bundles from the same project (Oak, Jackson, Composum, etc) you want to keep in sync. The Feature Model has a similar concept called Placeholders.

The great thing about Placeholders is they can be injected from the POM. For example, I created the following properties in my POM’s properties elements:

<composum.version>1.12.0</composum.version>
<jackrabbit.version>2.20.0</jackrabbit.version>
<jackson.version>2.11.1</jackson.version>
<oak.version>1.26.0</oak.version>
<slf4j.version>1.7.25</slf4j.version>

Each instance of these variables will be replaced within all of the Feature Model files.

Creating a Runnable JAR

While the goal of the Feature Model is to create a more flexible model and get away from running a fat JAR, but for local development and getting started quickly having a runnable JAR is just easier. 

To create a runnable JAR as a part of the same build, I configured Maven to combine the Sling Feature Model Launcher and my Sling CMS Feature Archive at build time into a standalone JAR using the Maven Assembly Plugin.

The Main class / method is quite brief, it:

  • Gets the version number passed via a properties file
  • Resolves the URL for the Feature Archive from the classpath
  • Appends (if not present) the -f flag with URL of the Feature Archive to the arguments
  • Calls the Sling Feature Model Launcher Main method

With that, we now have a standalone JAR which provides a double click run of Sling CMS as well as the rich Feature Model based aggregation in the form of Feature Models and Feature Archives all in one build.

In the next blog post in the Exploring the Feature Model series, I will be setting up an Oak Composite Node Store

Viewing all 49 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>