The previous article mapped Adobe Campaign’s tiers and URLs and parked β€œTomcat :8080” in the application layer. But Tomcat isn’t what runs your JavaScript. This article opens that layer up to show the five environments where server-side JS actually executes, workflow, delivery, JSSP, web app, and typology rule, and the single engine sitting underneath them all.

Whatever the environment, every Adobe Campaign server-side script is executed by the same interpreter: SpiderMonkey, embedded in the native nlserver core. Here is that application layer opened up:

  ═══════════════════════════════ APPLICATION LAYER ════════════════════════════════════
  ╔════════════════════════════ nlserver (C++ process) ═══════════════════════════╗
  β•‘                                                                               β•‘
  β•‘  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ SHIM Java (light) ─────────────────────────────┐  β•‘
  β•‘  β”‚ JVM OpenJDK + Tomcat (Jakarta EE)   :8080 http Β· :8005 ctrl             β”‚  β•‘
  β•‘  β”‚ JSP OOB :  soaprouter.jsp  logon.jsp  s.jsp  schemawsdl.jsp  ping.jsp   β”‚  β•‘
  β•‘  β”‚ JSPContext + SoapMethodCall(SOAP_ENCODING_NATIVE) β†’ executeSOAPCall()   β”‚  β•‘
  β•‘  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β•‘
  β•‘                                      β”‚  JNI in-process  (soapRouter="http://jni")
  β•‘                                      β–Ό                                        β•‘
  β•‘  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ NATIVE CORE (C++) ────────────────────────────┐  β•‘
  β•‘  β”‚ libnlsrvmod.so                                                          β”‚  β•‘
  β•‘  β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚  β•‘
  β•‘  β”‚ β”‚ libmozjs.so  β‡’  SpiderMonkey  (E4X)                                 β”‚ β”‚  β•‘
  β•‘  β”‚ β”‚   executes .jssp  +  AC JSAPI / xtk  (NLWS Β· NL Β· E4X Β· ES3/ES5)    β”‚ β”‚  β•‘
  β•‘  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚  β•‘
  β•‘  β”‚ libxmldom.so (DOM XML)      libxslproc.so (Xalan XSLT)                  β”‚  β•‘
  β•‘  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β•‘
  β•‘   Shared modules: web Β· wfserver Β· mta Β· stat Β· inMail Β· syslogd              β•‘
  β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

One engine, five environments

These are not five runtimes, but five entry points into the same SpiderMonkey interpreter. Two rules follow from that:

  • The language is identical everywhere. ES3/ES5 with E4X, var/const but no let, function but no arrow, no template literals β€” a snippet valid in a workflow is valid in a JSSP. See modern best practices.
  • Only the exposed objects change. request/response in a JSSP, instance/vars in a workflow, ctx in a web app…

So the question is never β€œdoes this JavaScript run?” but β€œis this object available *here?”* β€” and the answer lives in the reference.

Unit tests

Tested with acc by running acc instance exec -f test/integration/environments.spec.js in a sandbox:

loadLibrary("/nl/core/shared/nl.js"); // import NL.assert

context.es3.parseInt = NL.assert(parseInt("08") !== parseInt("08", 10));
context.es5.keys = NL.assert(typeof Object.keys === "function");
context.es5.isArray = NL.assert(typeof Array.isArray === "function");
context.es5.bind = NL.assert(typeof Function.prototype.bind === "function");
context.es5.stringify = NL.assert(typeof JSON.stringify === "function");
context.es5.strict = NL.assert(
  (function () {
    "use strict";
    return typeof this === "undefined";
  })() === true
);
context.es6.map = NL.assert(typeof Map === "undefined");
context.es6.promise = NL.assert(typeof Promise === "undefined");
context.es6.assign = NL.assert(typeof Object.assign === "undefined");
context.spiderMonkey.uneval = NL.assert(typeof uneval === "function"); // SpiderMonkey uneval() is Mozilla-only
context.spiderMonkey.oldProxy = NL.assert(typeof Proxy !== "undefined"); // 8. SpiderMonkey Proxy API, gone with ES6 Proxy
context.e4x.xml = NL.assert(typeof XML === "function"); // 3. E4X - 1 of four native XML globals
context.e4x.xmlType = NL.assert(typeof new XML("<x/>") === "xml"); // 4. E4X - 2 of four native XML globals
context.mozillaIter = NL.assert(typeof Iterator === "function"); // 9. Legacy Mozilla iteration protocol (pre-ES6)
context.nlserver.native = NL.assert(typeof getUUID() === "string");
context.nlserver.xtk = NL.assert(getOption("XtkDatabaseId").length > 0);
context.nlserver.sql = NL.assert(sqlGetInt("SELECT 1") === 1);

Going further

Ready to call the API? Start with πŸ“• queryDef in the πŸ“• AC JS Reference, or step back to the πŸ“• N-tier architecture to see how the layer above routes requests down here.