Skip to content

Transport service

Transport service

Since version 4.1.0, the transport service (TransportService) lets a Content Script work with the Content Server Transport Warehouse. A script can:

  • create and load Workbenches
  • add source objects to a Workbench as Transport Items, and refresh them when the source changes
  • inspect the dependencies of a Transport Item and map them to targets on the current system
  • list and remove global dependency mappings
  • create a Transport Package from a Workbench, and unpack a package into a Workbench

The service is available in the execution context as the transport variable.

Service mode

The transport operations run in JNI mode only. In any other mode, the service methods throw an ExecutionFaultException with the message "Transport Management API is available only in JNI mode".

Object model

Workbenches, Transport Items, and Transport Packages are Content Server nodes. The CSWorkbench, CSTransportItem, and CSTransportPackage objects extend CSNode, so the standard node methods and properties (for example ID, name, path, GUID, lastVersion) are also available. See Content Management Object.

flowchart LR
    transportService["transport (TransportService)"]
    warehouse["Transport Warehouse (CSNode)"]
    workbench["CSWorkbench"]
    item["CSTransportItem"]
    dependency["CSTransportDependency"]
    globalMapping["CSTransportGlobalDependencyMapping"]
    transportPackage["CSTransportPackage"]
    addResult["CSTransportAddResult"]
    addOutcome["CSTransportAddOutcome"]

    transportService -->|getTransportWarehouse| warehouse
    transportService -->|createWorkbench / getWorkbench| workbench
    transportService -->|listGlobalTransportDependencyMappings| globalMapping
    workbench -->|addItem| addResult
    addResult -->|transportItem| item
    addResult -->|outcomes| addOutcome
    workbench -->|listItems / updateItem| item
    item -->|listDependencies| dependency
    workbench -->|createTransportPackage| transportPackage
    transportPackage -->|unpackToWorkbench| workbench

Transport service API

All methods are available since 4.1.0. Name parameters are trimmed: a null or empty name raises an InvalidParamException. A null Boolean parameter is treated as false.

Method Description Since
CSNode getTransportWarehouse() Returns the singleton Transport Warehouse volume. Throws ExecutionFaultException if the Transport Warehouse cannot be resolved. 4.1.0
CSWorkbench createWorkbench(String name) Creates a Workbench in the Transport Warehouse and returns it. 4.1.0
CSWorkbench getWorkbench(Long ID) Returns a Workbench node by ID. Throws InvalidParamException if the ID is null or the node is not a Workbench. 4.1.0
CSTransportItem getTransportItem(Long ID) Returns a Transport Item node by ID. Throws InvalidParamException if the ID is null or the node is not a Transport Item. 4.1.0
CSTransportPackage getTransportPackage(Long ID) Returns a Transport Package node by ID. Throws InvalidParamException if the ID is null or the node is not a Transport Package. 4.1.0
List<CSTransportItem> listWorkbenchItems(Long ID) Lists all Transport Items in the Workbench. 4.1.0
List<CSTransportItem> listWorkbenchItems(Long ID, Boolean needUpdateOnly) Lists the Transport Items in the Workbench. When needUpdateOnly is true, only the items whose original source changed are returned. 4.1.0
List<CSTransportItem> listWorkbenchItems(CSWorkbench workbench) Lists all Transport Items in the Workbench. 4.1.0
List<CSTransportItem> listWorkbenchItems(CSWorkbench workbench, Boolean needUpdateOnly) Lists the Transport Items in the Workbench, optionally only the items whose original source changed. 4.1.0
CSTransportAddResult addItemToWorkbench(Long workbenchID, Long sourceID, Boolean includeContents, Boolean replace) Adds a source node to a Workbench by IDs. includeContents includes container descendants. replace replaces an existing matching item. 4.1.0
CSTransportAddResult addItemToWorkbench(CSWorkbench workbench, CSNode sourceNode, Boolean includeContents, Boolean replace) Adds a source node object to a Workbench. 4.1.0
Long createTransportPackage(Long workbenchID, String name) Creates a Transport Package that contains all the Transport Items in the Workbench and returns its ID. 4.1.0
Long createTransportPackage(CSWorkbench workbench, String name) Creates a Transport Package that contains all the Transport Items in the Workbench and returns its ID. 4.1.0
List<CSTransportGlobalDependencyMapping> listGlobalTransportDependencyMappings() Lists the persisted global transport dependency mappings. 4.1.0
Boolean removeGlobalTransportDependencyMapping(String mappingKey) Removes one persisted global transport dependency mapping by key. A null or empty key raises an InvalidParamException. 4.1.0

Package visibility

createTransportPackage returns the ID of the package that the Transport subsystem creates. The Warehouse Manager permissions control who can see the package, so the calling user may not have permission to load the package node with getTransportPackage.

Workbench (CSWorkbench)

A Transport Warehouse Workbench node with helper methods to add, list, and remove Transport Items.

Method Description Since
CSTransportAddResult addItem(CSNode sourceNode, Boolean includeContents, Boolean replace) Adds a source node to this Workbench as a Transport Item. 4.1.0
CSTransportAddResult addItem(Long sourceID, Boolean includeContents, Boolean replace) Adds a source node ID to this Workbench as a Transport Item. 4.1.0
List<CSTransportItem> listItems() Lists all Transport Items currently in this Workbench. 4.1.0
List<CSTransportItem> listItems(Boolean needUpdateOnly) Lists the Transport Items in this Workbench, optionally only the items whose original object changed. 4.1.0
CSTransportItem updateItem(CSTransportItem transportItem, Boolean includeContents) Rebuilds a Transport Item in this Workbench from its original object. Returns the replacement item with its new node ID. 4.1.0
CSTransportItem updateItem(Long transportItemID, Boolean includeContents) Rebuilds a Transport Item in this Workbench from its original object. Returns the replacement item with its new node ID. 4.1.0
Boolean deleteItem(Long transportItemID) Deletes a Transport Item from this Workbench. 4.1.0
Long createTransportPackage(String name) Creates a Transport Package that contains all the Transport Items in this Workbench. The caller may not have permission to load the package node. 4.1.0

updateItem returns a new node

Transport replaces the item with a new node. Always use the CSTransportItem that updateItem returns: the previous object refers to the replaced node.

Transport Item (CSTransportItem)

A Transport Warehouse item node with transport-specific metadata and update operations.

Property Description Since
originalItemID Long ID of the original source node that this Transport Item represents 4.1.0
originalSubType Long subtype of the original source node 4.1.0
originalGUID Clean GUID string of the original source node 4.1.0
originalVersion Long version number of the original source node at export time 4.1.0
originalPath String path of the original source node at export time 4.1.0
originalName String name of the original source node 4.1.0
originalAvailable Boolean, true if the original source node is available on this system 4.1.0
needUpdate Boolean, true if this Transport Item should be refreshed from its original source 4.1.0
partCount Integer number of Transport Item parts currently stored for this item 4.1.0
Method Description Since
List<CSTransportDependency> listDependencies() Lists the dependency rows of this Transport Item. The rows come from the same fresh scan as the transport overview page. 4.1.0
Boolean mapDependencyByID(String dependencyKey, Long targetID) Maps an editable dependency row to an ID target. The target ID must be greater than 0. 4.1.0
Boolean mapDependencyByID(String dependencyKey, CSNode targetNode) Maps an editable dependency row to a CSNode target. 4.1.0
Boolean mapDependencyByID(String dependencyKey, CSMember targetMember) Maps an editable dependency row to a CSMember target (user or group). 4.1.0
Boolean mapDependencyByText(String dependencyKey, String targetValue) Maps an editable dependency row to a text value. The value is trimmed and must not be empty. 4.1.0
Boolean clearDependencyMapping(String dependencyKey) Clears an editable dependency mapping by key. 4.1.0

The dependencyKey parameter is always a key returned by listDependencies(). After a mapping change, the next call to listDependencies() reads the dependencies again.

Dependency (CSTransportDependency)

A read-only dependency row, with the values that the transport dependency overview shows and additional diagnostics. Beautiful WebForms views are included in the transport dependency scan, so the dependencies of a view are returned as rows like those of any other item.

Property Description Since
dependencyID Long row-local ID of the dependency 4.1.0
dependencyKey String opaque dependency key. Pass it back unchanged to the mapping methods 4.1.0
dependencyType String dependency class key from the transport scanner 4.1.0
type String display type shown in the dependency overview 4.1.0
field String dependency field label shown in the dependency overview 4.1.0
originalName String original dependency name or value in the exported object 4.1.0
originalGUID Clean source GUID for node dependencies, otherwise null 4.1.0
currentName String currently mapped dependency name or value 4.1.0
existsInWorkbench Boolean, true if a Workbench item resolves the dependency 4.1.0
existsInInstance Boolean, true if a match on this system resolves the dependency 4.1.0
existsInTransportItem Boolean, true if another Transport Item part resolves the dependency 4.1.0
numberOfMatchesFoundInInstance Integer number of matches found on this system 4.1.0
numberOfMatchesFoundInWorkbench Integer number of matches found in the Workbench 4.1.0
isOptional Boolean, true if the dependency is optional 4.1.0
isFuzzy Boolean, true if the match is ambiguous 4.1.0
isMapping Boolean, true if a manual dependency mapping is used 4.1.0
isEditable Boolean, true if the row accepts manual mapping changes 4.1.0
mappingValueKind String expected mapping value kind: ID, TEXT, or UNKNOWN 4.1.0
mappingTargetKind String advisory target kind. Transport on Content Server performs the actual validation 4.1.0
mappedID Long ID of the mapped target, where available 4.1.0
mappedGUID Clean GUID of the mapped target, where available 4.1.0
statusText String localized status text shown in the dependency overview 4.1.0
statusCode String status code that does not depend on the language of statusText 4.1.0

statusCode has one of these values:

Value Meaning
not_applicable No resolution information is available for the row
resolved_in_transport_item Another part of the same Transport Item resolves the dependency
resolved_in_workbench A Workbench item resolves the dependency
resolved_in_workbench_only A Workbench item resolves the dependency, and no match exists on this system
custom_mapping A manual mapping resolves the dependency on this system
exact_instance_match One match on this system resolves the dependency
fuzzy_instance_match More than one match exists on this system
optional_missing The dependency is not resolved and is optional
missing The dependency is not resolved

Choose the mapping method from mappingValueKind

Call mapDependencyByID when mappingValueKind is ID, and mapDependencyByText when it is TEXT. Map only rows where isEditable is true.

Global dependency mapping (CSTransportGlobalDependencyMapping)

A read-only global transport dependency mapping, with the key and the value that Transport persists.

Property Description Since
mappingKey String opaque mapping key. Pass it back unchanged to removeGlobalTransportDependencyMapping 4.1.0
mappingValue String opaque mapping value 4.1.0

Transport Package (CSTransportPackage)

A Transport Package node that contains a packaged Workbench or Transport Item.

Method Description Since
Boolean unpackToWorkbench(Long workbenchID, Boolean replaceItems) Unpacks this Transport Package into a Workbench. When replaceItems is true, existing Transport Items with matching object GUIDs are replaced. Returns true if the package was unpacked. 4.1.0

Add results and outcomes

addItem and addItemToWorkbench return a CSTransportAddResult: the resulting root Transport Item and one CSTransportAddOutcome for each source node that Transport processed. When includeContents is true, the descendants of a container are processed too, so the result can contain more than one outcome.

CSTransportAddResult

Property Description Since
transportItem CSTransportItem created for the requested root source 4.1.0
outcomes List of CSTransportAddOutcome, one per source node 4.1.0
addedCount Integer number of new Transport Items 4.1.0
skippedCount Integer number of source nodes skipped because a matching item existed 4.1.0
replacedCount Integer number of existing Transport Items replaced 4.1.0
errorCount Integer number of source nodes that Transport reported an error for 4.1.0

CSTransportAddOutcome

Property Description Since
sourceID Long ID of the source node that Transport processed 4.1.0
successful Boolean, true if Transport processed the source without an error 4.1.0
added Boolean, true if a new Transport Item was added 4.1.0
skipped Boolean, true if an existing matching item caused the source to be skipped 4.1.0
replaced Boolean, true if an existing matching item was replaced 4.1.0
errorMessage String Transport error for this source, or null when no error was reported 4.1.0

An outcome is added when it is successful and neither skipped nor replaced. errorCount counts the outcomes that are not successful.

flowchart TD
    outcome["CSTransportAddOutcome"]
    successful{"successful?"}
    skipped{"skipped?"}
    replaced{"replaced?"}
    errorState["error: read errorMessage, counted in errorCount"]
    skippedState["skipped, counted in skippedCount"]
    replacedState["replaced, counted in replacedCount"]
    addedState["added, counted in addedCount"]

    outcome --> successful
    successful -->|no| errorState
    successful -->|yes| skipped
    skipped -->|yes| skippedState
    skipped -->|no| replaced
    replaced -->|yes| replacedState
    replaced -->|no| addedState

A result is not an all-or-nothing operation

The add call returns normally even when some source nodes fail. Check errorCount and the errorMessage of each outcome before you package the Workbench.

Redeploying Module Suite objects with transport

When Transport updates an object that already exists on the target system, Module Suite applies the transported data as follows.

Content Script

  • The transported static variable code is applied.
  • The environment-specific data is reset: compiled static variable values (evaluated again on the target), impersonation user, and sandbox exception.
  • The in-memory static variable cache of the script is cleared, so the script does not use the values of the previous deployment.
  • Scheduling is not changed. Content Script schedules are stored in the Distributed Agent tables, not in the script. On an update, the transported schedules are ignored and the existing schedules of the target script keep their enabled or disabled state.

On a first deployment (create), Transport exports only the enabled schedules of the source script and creates them disabled on the target. An administrator must enable them.

Manage schedules on the target

Schedule changes made on the source system are not propagated by a transport update. After a redeploy, review the schedules directly on the target system.

Smart Page

A redeployed Smart Page refreshes its component JavaScript and CSS dependencies, its associated template, its library version, and its advanced mode flag. The template is resolved by path to the local template node, because node IDs differ between systems.

Content Script templates

A redeployed Content Script Template or System Template applies the template configuration that the transport carries (for example the model and the compiled template) together with the new version. Only the keys present in the transported template are applied, because template variants do not all define the same fields.

Examples

The examples are based on the snippets in the Transport folder of CSScriptSnippets in the Content Script Volume. Replace the values in the first lines with IDs from your system.

Example: create a Workbench and add a folder with its contents

def sourceID = 12345L   // the object to transport

def workbench = transport.createWorkbench("Release 4.1.0")
def result = workbench.addItem(sourceID, true, false)

log.info("Root Transport Item {}: {} added, {} skipped, {} replaced, {} errors",
    result.transportItem.ID,
    result.addedCount,
    result.skippedCount,
    result.replacedCount,
    result.errorCount)

result.outcomes.findAll { !it.successful }.each { outcome ->
    log.error("Source {} was not added: {}", outcome.sourceID, outcome.errorMessage)
}

Example: list the Workbench items and their dependencies

def workbenchID = 12345L

def workbench = transport.getWorkbench(workbenchID)
workbench.listItems().each { item ->
    out << "<h3>${item.originalName} (${item.originalPath})</h3>"
    out << "<p>Original ID ${item.originalItemID}, version ${item.originalVersion}, " +
           "available: ${item.originalAvailable}, needs update: ${item.needUpdate}</p>"
    out << "<ul>"
    item.listDependencies().each { dependency ->
        out << "<li>${dependency.type} / ${dependency.field}: ${dependency.originalName}" +
               " → ${dependency.currentName} [${dependency.statusCode}]</li>"
    }
    out << "</ul>"
}

Example: map a Transport Item dependency

def transportItemID = 12345L
def dependencyKey = "..."   // a dependencyKey value from listDependencies()
def targetValue = "67890"

def transportItem = transport.getTransportItem(transportItemID)

// Dependency keys are opaque. Always obtain the key from listDependencies()
// and pass the exact value back to the mapping method.
def dependency = transportItem.listDependencies().find {
    it.dependencyKey == dependencyKey
}
if (dependency == null) {
    throw new IllegalArgumentException(
        "Dependency key '${dependencyKey}' was not found on Transport Item ${transportItem.ID}.")
}
if (!dependency.isEditable) {
    throw new IllegalStateException("Dependency '${dependencyKey}' is not editable.")
}

// mappingValueKind controls the representation accepted by the API.
// mappingTargetKind is only a guide; Content Server validates the actual target.
switch (dependency.mappingValueKind) {
    case "ID":
        transportItem.mapDependencyByID(dependencyKey, Long.valueOf(targetValue))
        break
    case "TEXT":
        transportItem.mapDependencyByText(dependencyKey, targetValue)
        break
    default:
        throw new IllegalStateException(
            "Unsupported mapping value kind '${dependency.mappingValueKind}'.")
}

// Read the dependency again so the output reflects the persisted mapping.
def updatedDependency = transportItem.listDependencies().find {
    it.dependencyKey == dependencyKey
}
log.info("Current value: {}, status: {}",
    updatedDependency?.currentName, updatedDependency?.statusCode)

Example: list global dependency mappings

def globalDependencies = transport.listGlobalTransportDependencyMappings() ?: []

out << "<p>${globalDependencies.size()} global dependency mappings found.</p><ul>"
globalDependencies.each { mapping ->
    out << "<li>${mapping.mappingKey} = ${mapping.mappingValue}</li>"
}
out << "</ul>"

// To remove one mapping, pass its key back unchanged:
// transport.removeGlobalTransportDependencyMapping(globalDependencies[0].mappingKey)

Example: refresh a Workbench and download the package

def workbenchID = 12345L
def packageName = "Release 4.1.0"

def workbench
try {
    workbench = transport.getWorkbench(workbenchID)

    // Only the Transport Items whose original source object changed.
    def itemsToUpdate = workbench.listItems(true) ?: []

    // updateItem() returns the replacement item. If an update fails, the
    // exception stops package creation, so a partially refreshed Workbench
    // is not packaged.
    itemsToUpdate.each { item ->
        item = workbench.updateItem(item, false)
    }

    def packageID = transport.createTransportPackage(workbench, packageName)
    def transportPackage = transport.getTransportPackage(packageID)

    def resource = transportPackage.lastVersion?.content
    if (resource == null) {
        throw new IllegalStateException(
            "Transport Package ${packageID} has no downloadable content.")
    }
    resource.owner = null
    resource.name = packageName.toLowerCase().endsWith(".zip") ?
        packageName : "${packageName}.zip"

    // Returning a CSResource from a Content Script triggers the download.
    return resource
} catch (e) {
    log.error("Unable to refresh and package Workbench {}", workbench, e)
    printError(e)
}

Example: unpack a package into a Workbench

def packageID = 12345L
def workbenchID = 67890L

def transportPackage = transport.getTransportPackage(packageID)
if (transportPackage.unpackToWorkbench(workbenchID, true)) {
    transport.getWorkbench(workbenchID).listItems().each { item ->
        log.info("Unpacked {} ({})", item.originalName, item.originalGUID)
    }
}