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 β
ββββββββββββββββββββββββββββββββββββββ
β 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 β
| Bundle | Role | Status in replacement |
|---|---|---|
core | Servlets, services, schedulers, listeners β most of the dynamic logic | Partially replaced by CAP services + scripts/fetch-tutorials.ts |
core.tutorial | Tutorial-specific REST endpoints, GitHub ingestion, IMS proxy, HANA Live Demo proxy | Partially replaced β significant gaps (see gap analysis) |
spa | Sling Models that emit .model.json for the SPA frontend | Not applicable β Hugo emits HTML directly |
spa.api, core.api | Public Java APIs consumed by other bundles | N/A |
responsive | Responsive image/asset generation | Replaced by Hugo image processing |
compat | Backward-compat shims for legacy templates | N/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 (
@Componentwithscheduler.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-tutorialsGitHub org, filters by topic/visibility, excludes a configurable deny-list. - Private
-Contributionrepos: Authenticated reads ofrules.vrvalidation files from private repos paired with each public tutorial repo. Token must havereposcope on the private repos. - Per-tutorial fetch:
- Reads
tutorial.mdand 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/....
- Reads
- Image proxying:
GitHubProxyServletfetches images from private repos at request time using a service token (avoids exposing the token to the browser). Public repo images are referenced directly viaraw.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:lastReplicatedmetadata.
4. Public-Facing Delivery β
4.1 Page Templates (14 total) β
| Template | Purpose | Hugo equivalent |
|---|---|---|
tutorial-page | Single-tutorial render | hugo/layouts/tutorials/single.html |
mission-page | Mission landing | hugo/layouts/missions/single.html |
group-page | Group landing | hugo/layouts/groups/single.html |
landing-page | Marketing landing pages with CF composition | Gap β Hugo has limited landing pages |
topic-page | Topic taxonomy listing | Gap β needs verification |
home-page | Homepage with Hero/SubNavigation/Resources fragments | Partial β hugo/layouts/index.html |
error-page | 404/500 templates (Handlebars-based legacy) | Gap |
redirect-page | Server-side redirect declaration | Gap β handled at AppRouter? |
event-page | Events landing | Replaced by AppSpace Vue app |
search-results-page | Solr-backed search UI | Replaced by SearchService + Hugo template (parity not verified) |
sitemap-page | XML sitemap generator | Gap |
robots-page | author-configurable robots.txt | Gap β sysadmin concern |
learning-page | Aggregated learning paths | Gap |
xf-page | Experience Fragment host | N/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.
4.5 Search β
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) β
| Job | Cadence | Purpose |
|---|---|---|
| GitHub fetcher | Hourly | Sync tutorials from sap-tutorials org |
| Tag sync (outbound) | Event-driven (JCR listener) | Push AEM tag changes to GitHub |
| Akamai purge queue drain | Event-driven | Coalesce purge requests after replication bursts |
| Solr indexer | Event-driven + nightly full | Maintain search index |
| Health check / metrics | Continuous | OSGi healthchecks consumed by infra monitoring |
| Asset rendition cleanup | Daily | Delete 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@Designateservices. 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 β
| Layer | Mechanism |
|---|---|
| Akamai CDN | URL-keyed cache, purged via AkamaiCachePurgeService |
| AEM Dispatcher | File-system cache in front of Publish, invalidated by replication agents |
| AEM Publish | In-memory + JCR query cache |
| Solr | Search-result cache |
| Browser | Cache-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-pagetemplate) so sysadmins can adjust without code changes.- Open Graph / Twitter Cards β Per-page meta tags from frontmatter.
- canonical URLs β Per-locale canonical with
hreflanglinks.
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.
compatbundle β Shims for legacy templates. If anything still depends on it, removal during cutover will surface those dependencies.- Per-bundle Sling Models β
.model.jsonoutputs 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:
| Item | Action required at cutover |
|---|---|
| GitHub fetcher | Replacement runs on GitHub Actions cron β verify schedule + token rotation |
| HANA Live Demo proxy | Either 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 purge | Configure in CAP publish-content.ts |
| Solr search | Verify SearchService parity |
| IMS proxy | Already migrated to CAP DeveloperService |
| Tag bi-directional sync | Decide: keep one-way (GitHub β CAP) or rebuild outbound |
| GitHub feedback β issues | Reimplement in CAP |
| NextStepsServlet | Reimplement in CAP |
| Siteimprove | Wire into Hugo build or skip |
| i18n locales | Phase plan needed |
| Redirects | Export from JCR, import into AppRouter |
| robots.txt | Move to repo as static file or Hugo template |
| Trials checksums | Owner: SAP downloads team β coordinate |
| Adobe Analytics | Wire into Hugo |
| Cookie consent | Wire into Hugo |
| Akamai DNS | Switch CNAME from AEM Dispatcher to AppRouter |
See aem-gap-analysis.md for a prioritized gap list and edge cases vs the tutorials-ims replacement.