Skip to content

AEM Current State β€” Historical Reference ​

Purpose: Document how Adobe Experience Manager (AEM) currently serves developers.sap.com so the team can support production until cutover to the tutorials-ims replacement is complete. This is a snapshot of the live system, not a design document.

Source: D:\projects\com.sap.wcms.dx.developers (1,013 Java files, 6 OSGi bundles, 341 content XML/HTML files), reviewed 2026-05-20.

Scope filter: Tutorial authoring on developers.sap.com happens in GitHub via a VS Code extension, not in AEM. This document covers only:

  • Tutorial publishing/ingestion (GitHub β†’ AEM)
  • System administration tools (monitoring, cache busting, manual republish)
  • Public-facing delivery (templates, components, search, i18n)
  • Cross-cutting concerns (CDN, redirects, error pages, analytics)

AEM authoring dialogs, editable template policies, and content-fragment authoring forms used by content authors are out of scope.


1. System Topology ​

text
                                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                β”‚  Authors (GitHub + VS Code ext.)   β”‚
                                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                               β”‚ git push
                                               β–Ό
                                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                β”‚  sap-tutorials GitHub org          β”‚
                                β”‚  - tutorials repos (public)        β”‚
                                β”‚  - *-Contribution repos (private)  β”‚
                                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                               β”‚ GitHub API (OAuth)
                                               β”‚ hourly scheduler
                                               β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   JCR import   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚  HANA Live Demo    │◀───── proxy ───│        AEM         β”‚
       β”‚  (SQL/algorithms)  β”‚                β”‚  - Author instance β”‚
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                β”‚  - Publish instanceβ”‚
                                             β”‚  - Dispatcher      β”‚
                                             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                          β”‚ replicate
       β”‚      IMS (Java)    │◀──── proxy ─────────────── purge
       β”‚  progress tracking β”‚                          β”‚
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                          β–Ό
                                             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                             β”‚   Akamai CDN       β”‚
                                             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                       β–Ό
                                             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                             β”‚  developers.sap.comβ”‚
                                             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The tutorials-ims replacement collapses Author/Publish/Dispatcher into a single AppRouter+CAP stack on BTP Cloud Foundry, with HANA replacing JCR for tutorial HTML and progress data.


2. OSGi Bundle Inventory ​

BundleRoleStatus in replacement
coreServlets, services, schedulers, listeners β€” most of the dynamic logicPartially replaced by CAP services + scripts/fetch-tutorials.ts
core.tutorialTutorial-specific REST endpoints, GitHub ingestion, IMS proxy, HANA Live Demo proxyPartially replaced β€” significant gaps (see gap analysis)
spaSling Models that emit .model.json for the SPA frontendNot applicable β€” Hugo emits HTML directly
spa.api, core.apiPublic Java APIs consumed by other bundlesN/A
responsiveResponsive image/asset generationReplaced by Hugo image processing
compatBackward-compat shims for legacy templatesN/A

3. Tutorial Publishing & Ingestion ​

3.1 GitHub Fetcher (core.tutorial bundle) ​

The fetcher is the highest-risk replacement target. It is a multi-class, multi-token, scheduled OSGi service with the following characteristics:

  • Schedule: Hourly cron via Sling Scheduler (@Component with scheduler.expression).
  • Multi-token OAuth rotation: Maintains a pool of GitHub Personal Access Tokens. On rate-limit exhaustion (HTTP 403 with X-RateLimit-Remaining: 0), rotates to the next token in the pool. Token list configured via OSGi config admin.
  • Repo discovery: Walks the sap-tutorials GitHub org, filters by topic/visibility, excludes a configurable deny-list.
  • Private -Contribution repos: Authenticated reads of rules.vr validation files from private repos paired with each public tutorial repo. Token must have repo scope on the private repos.
  • Per-tutorial fetch:
    • Reads tutorial.md and any referenced images.
    • Parses frontmatter and ACCORDION-BEGIN/END (V1) or H3 step delimiters (V2) β€” the same parser logic ported into scripts/parsers/.
    • Stores parsed result as JCR resources under /content/developers/....
  • Image proxying: GitHubProxyServlet fetches images from private repos at request time using a service token (avoids exposing the token to the browser). Public repo images are referenced directly via raw.githubusercontent.com.
  • Failure handling: On individual repo failure, logs error and continues with the next repo (does not fail the entire batch).
  • Manual trigger: Sysadmin servlet exposed at a /bin/... path (auth-required) to force a refresh of a single repo or all repos.

3.2 Tag Bi-Directional Sync (TagNodeListener) ​

A JCR observation listener watches the AEM tag tree (/content/cq:tags/...). When tags are added, renamed, or deleted in AEM, the listener writes the change back to the corresponding GitHub source (likely a tag-metadata file in the tutorials org).

This is bi-directional β€” GitHub changes flow in via the fetcher, AEM changes flow out via the listener.

3.3 Content Storage in JCR ​

Tutorial content lives at /content/developers/<locale>/tutorials/<slug>/... with these key structures:

  • Per-step nodes: Each step is a JCR child with text, image references, and option/quiz subnodes.
  • Asset references: Images stored under /content/dam/developers/... with derived renditions (responsive breakpoints).
  • Frontmatter as JCR properties: time, level, parser, tags, etc., promoted to typed properties.
  • Replication: Author β†’ Publish replication is event-driven; each tutorial node carries cq:lastReplicated metadata.

4. Public-Facing Delivery ​

4.1 Page Templates (14 total) ​

TemplatePurposeHugo equivalent
tutorial-pageSingle-tutorial renderhugo/layouts/tutorials/single.html
mission-pageMission landinghugo/layouts/missions/single.html
group-pageGroup landinghugo/layouts/groups/single.html
landing-pageMarketing landing pages with CF compositionGap β€” Hugo has limited landing pages
topic-pageTopic taxonomy listingGap β€” needs verification
home-pageHomepage with Hero/SubNavigation/Resources fragmentsPartial β€” hugo/layouts/index.html
error-page404/500 templates (Handlebars-based legacy)Gap
redirect-pageServer-side redirect declarationGap β€” handled at AppRouter?
event-pageEvents landingReplaced by AppSpace Vue app
search-results-pageSolr-backed search UIReplaced by SearchService + Hugo template (parity not verified)
sitemap-pageXML sitemap generatorGap
robots-pageauthor-configurable robots.txtGap β€” sysadmin concern
learning-pageAggregated learning pathsGap
xf-pageExperience Fragment hostN/A

4.2 Components (~80) and Content Fragments ​

Notable components that affect the public surface:

  • AdaptiveImage β€” Six responsive breakpoints with separate image renditions, lazy-loading, art-direction support. Hugo uses image processing but not the exact same breakpoints β€” verify against hugo/layouts/_default/baseof.html.
  • HeroBanner content fragment β€” Composed at the homepage level. Needs landing-page composition story in Hugo.
  • SubNavigation content fragment β€” Top-of-page secondary nav per topic. Hugo uses a single global nav.
  • Resources content fragment β€” "Related links" rail rendered next to articles.
  • VideoEmbed β€” YouTube/Vimeo wrappers with cookie-aware lazy iframes.
  • CodeBlock β€” Syntax-highlighted code with copy-to-clipboard. Hugo has the highlight via scripts/highlight-cds.ts.
  • Tabs / Accordion / Callout / Alert β€” Shortcode equivalents in Hugo (verify all are mapped).
  • Handlebars legacy components β€” A subset of error pages and product cards still use Handlebars templates rendered server-side. These are pre-migration leftovers.

4.3 Clientlibs (18 categories) ​

AEM ships ~18 clientlib categories: base CSS, base JS, fonts, analytics, search, video, code-syntax, etc. The Hugo replacement consolidates these into hugo/static/css/sap-fundamental.css (PostCSS-built) and small Vue islands in apps/. Specific JS behaviors to verify:

  • Cookie consent banner (likely OneTrust)
  • Analytics tracking (Adobe Analytics β€” not yet wired in tutorials-ims)
  • Search box autocomplete
  • Mobile nav / hamburger
  • Anchor-link smooth scroll on tutorial steps
  • Print styles

4.4 i18n / Multi-language ​

AEM has language masters under /content/developers/<lang-code>/... with translation copy/replication to localized branches. The site supports multiple languages (verify exact locale list β€” typically en, de, ja, zh, ko, fr, es, pt-br).

The Hugo replacement is currently English-only. Multi-language is a known gap.

SolrSearchServlet proxies to a Solr cluster with faceted search across tutorials, missions, blog posts, and other developers.sap.com content. Facets typically include: content type, technology tag, level, time-to-complete, language.

The replacement has SearchService in CAP β€” parity (facets, weighting, typo tolerance, multi-language stemming) is not yet verified.


5. Integrations ​

5.1 IMS (Internal Management System) ​

IMSProxyServlet and related code in core.tutorial route /api/ims/* to the Spring Boot IMS app at the URL stored in OSGi config. Used for:

  • Mission progress lookup
  • Task completion writes
  • User identity bridging (SAP IDP β†’ IMS user)
  • Leaderboards / event-mode counters

Replacement: CAP DeveloperService reimplements IMS endpoints; srv/lib/ims-proxy.js (if present) handles legacy fallback during migration.

5.2 HANA Live Demo (LiveDemoProxyServlet) ​

A servlet proxying SQL execution and algorithm runs to a HANA instance for embedded tutorial demos (e.g., "run this query against a sample dataset"). Authenticated, rate-limited, with sandbox per-user temp schemas.

This is entirely missing from the tutorials-ims replacement. Tutorials that depend on it will break unless the proxy is kept running pointing at the AEM URL during cutover, or the feature is reimplemented.

5.3 GitHub Feedback β†’ GitHub Issues ​

Three feedback servlets (tutorial / group / mission) take user-submitted feedback and open GitHub issues against the source repo using a service token. URL routing rules per repo are configurable via OSGi config admin.

5.4 Akamai CDN ​

AkamaiCachePurgeService integrates with AEM's replication framework. On cq:lastReplicated change, the service computes affected URLs and calls Akamai's Fast Purge API to invalidate the CDN cache.

The replacement has no equivalent β€” content updates flow publish-content.ts β†’ HANA, and HTTP responses set Cache-Control + ETag headers, but there is no CDN purge call.

5.5 Siteimprove ​

SiteimproveServlet and a clientlib snippet provide accessibility and SEO scoring via the Siteimprove SaaS. Used by sysadmins to monitor content quality.

5.6 NextStepsServlet ​

A recommendation engine endpoint that takes a current tutorial slug and returns suggested follow-on tutorials based on tag overlap, mission membership, and (likely) collaborative-filtering data from IMS.


6. Scheduled Jobs (Sling Scheduler) ​

JobCadencePurpose
GitHub fetcherHourlySync tutorials from sap-tutorials org
Tag sync (outbound)Event-driven (JCR listener)Push AEM tag changes to GitHub
Akamai purge queue drainEvent-drivenCoalesce purge requests after replication bursts
Solr indexerEvent-driven + nightly fullMaintain search index
Health check / metricsContinuousOSGi healthchecks consumed by infra monitoring
Asset rendition cleanupDailyDelete unused image renditions from DAM

7. Sysadmin Surface ​

7.1 Manual Tools (/bin/... servlets) ​

  • Force tutorial refresh β€” re-pull from a single GitHub repo or all repos.
  • Force Akamai purge β€” invalidate by URL pattern.
  • Force Solr reindex β€” rebuild full index or single content tree.
  • Replication queue inspector β€” view pending replications, retry failed.
  • OSGi console (/system/console/...) β€” config admin, bundle status, scheduler view, healthchecks.

7.2 Configuration Model ​

  • OSGi configs (/apps/.../config/*.xml) β€” runtime configuration for all @Designate services. Includes GitHub tokens, IMS URL, HANA Live Demo URL, Solr cluster URL, Akamai credentials, feedback issue templates.
  • Per-environment overlays (/apps/.../config.author, config.publish, config.dev, config.prod) β€” environment-specific overrides selected by Sling run modes.

7.3 Trials & Downloads ChecksumServlet ​

A servlet that reads a JSON file maintained in the DAM (sysadmin-edited) and computes/serves SHA checksums for downloadable trial software. Used on /trials/... pages.

The DAM file is not in D:\projects\com.sap.wcms.dx.developers source β€” it's content authored by the SAP downloads team. This is a sysadmin touch point.


8. Cache & Performance ​

LayerMechanism
Akamai CDNURL-keyed cache, purged via AkamaiCachePurgeService
AEM DispatcherFile-system cache in front of Publish, invalidated by replication agents
AEM PublishIn-memory + JCR query cache
SolrSearch-result cache
BrowserCache-Control + ETag headers on responses

The replacement has Cloud Foundry β†’ AppRouter β†’ CAP β†’ HANA with no Akamai or Dispatcher in front. The bounded LRU cache in srv/lib/content-store.js (50MB) is the only hot-path cache.


9. Authentication & Identity ​

  • Public surface: Anonymous (no login required for tutorial reading).
  • Progress tracking: SAP IDP via the Spring Boot IMS app's auth handler. AEM forwards the identity token in IMS proxy calls.
  • Author instance: AEM-internal users (LDAP-bridged) β€” out of scope.
  • Sysadmin tools: AEM admin role.

10. URL Structure & Redirects ​

Tutorial URLs follow /tutorials/<slug>/<step-or-page> with locale prefix in some markets. The redirect map is maintained in JCR (/conf/.../redirects/...) β€” rules can be CSV-imported. AEM has a RedirectFilter Sling filter that intercepts requests and serves 301s.

The replacement currently has no redirect map. Any URL pattern changes during cutover (e.g., locale-prefixed β†’ flat) need redirects to preserve SEO.


11. Error Pages ​

  • 404 β€” Custom Handlebars template with site search box and "popular tutorials" rail.
  • 500 β€” Generic apology page.
  • 403 β€” Likely the SAP IDP login redirect, not a static page.

The replacement has Hugo defaults β€” verify against AEM templates if SEO matters.


12. Sitemap & SEO ​

  • sitemap.xml β€” Generated by AEM's sitemap servlet, walks /content/developers/... and emits per-locale <urlset>s.
  • robots.txt β€” Authored as a page in AEM (robots-page template) so sysadmins can adjust without code changes.
  • Open Graph / Twitter Cards β€” Per-page meta tags from frontmatter.
  • canonical URLs β€” Per-locale canonical with hreflang links.

13. Analytics ​

Adobe Analytics tags injected via clientlib. Page-view, tutorial-step-view, and outbound-link events tracked.


14. Known Quirks / Carry-Forwards ​

  • Handlebars legacy layer β€” Some components still render via Handlebars (not HTL). Pre-migration leftovers; works in production but not maintained.
  • compat bundle β€” Shims for legacy templates. If anything still depends on it, removal during cutover will surface those dependencies.
  • Per-bundle Sling Models β€” .model.json outputs were intended for an SPA frontend that was never fully realized. Rendering today is HTL β†’ HTML.
  • Magic OSGi configs β€” Several services have hardcoded fallbacks if config is missing. Audit before turning AEM off.

15. Cutover Reference Checklist ​

When tutorials-ims reaches production parity, the following items must be migrated, kept dual-running, or explicitly retired:

ItemAction required at cutover
GitHub fetcherReplacement runs on GitHub Actions cron β€” verify schedule + token rotation
HANA Live Demo proxyEither keep AEM proxy running or reimplement in CAP
GitHubProxyServlet (private repo images)Reimplement in CAP or migrate to raw.githubusercontent.com for public repos
Akamai purgeConfigure in CAP publish-content.ts
Solr searchVerify SearchService parity
IMS proxyAlready migrated to CAP DeveloperService
Tag bi-directional syncDecide: keep one-way (GitHub β†’ CAP) or rebuild outbound
GitHub feedback β†’ issuesReimplement in CAP
NextStepsServletReimplement in CAP
SiteimproveWire into Hugo build or skip
i18n localesPhase plan needed
RedirectsExport from JCR, import into AppRouter
robots.txtMove to repo as static file or Hugo template
Trials checksumsOwner: SAP downloads team β€” coordinate
Adobe AnalyticsWire into Hugo
Cookie consentWire into Hugo
Akamai DNSSwitch CNAME from AEM Dispatcher to AppRouter

See aem-gap-analysis.md for a prioritized gap list and edge cases vs the tutorials-ims replacement.