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)
}
}