If you have worked with the Automation Engine for a while, you will no doubt have seen references to “XREQ” objects in error messages, logs, or traces.
Background
For a long time, XREQ objects were known only to the product developers. The UC_OTYP table refers to these objects as “Xrequests”, but the DB schema documentation misleadingly states that the XREQ table is for storing “Memory distribution”. The table actually contains information about XREQ objects. XREQs are used internally by the Automation Engine, mostly for functions that require user interaction.
The XREQ icon, from the ucdj.jar file in the Java User Interface. |
The XREQ icon, from the Automic Web Interface. |
The description of the XRequest
API class, new in v12.1, explains the role XREQ objects play:
XREQ Object : Each X-request has an according X-request object XREQ available in the the AE database that specifies how a request is processed. The processing instructions are written in a specific script (Automic script and reserved internal functions of DSFUN). These objects are of type XREQ and exclusively stored in client 0. They are not visible for the customer an hidden in folder 0000\XREQ\. XREQ objects are part of the AE initial data (OH_Idnr lower than 100.000) and can be imported/exported via DB.LOAD/UNLOAD (uc_ini.txt).
AE initial data include many , including XREQ, JOBI, PRPT, and VARA objects. You can view these objects (or fragements therof at least) by looking at the uc_ini.txt file. The AE DB file parsing script parse_ad_db_file.sh can also display information about initial data files.
XREQ table structure
The XREQ table has no XREQ_OH_Idnr column. However, bytes 6 through 15 of XREQ_Name correspond to OH_Idnr entries in OH. (And all of these OH entries have the OH_OType XREQ.) The following query can be used to list all XREQ objects by name.
WITH XREQ_OBJECTS
AS ( SELECT OH_IDNR,OH_NAME,OH_OTYPE,XREQ_NAME,
SUBSTR (XREQ_NAME, 1, 4) AS XREQ_SUBTYPE,
CASE SUBSTR (XREQ_NAME, 1, 4)
WHEN 'ODOC' THEN SUBSTR (XREQ_NAME, 17, 4)
ELSE ' '
END AS ODOC_SUBTYPE, XREQ_CONTENTLEN,XREQ_CONTENT
FROM UC4.XREQ LEFT JOIN UC4.OH ON SUBSTR (XREQ_NAME, 6, 10) = OH_IDNR
ORDER BY OH_Name, SUBSTR (XREQ_NAME, 19, 4))
SELECT OH_IDNR, OH_OTYPE,OH_NAME,XREQ_NAME,XREQ_SUBTYPE,ODOC_SUBTYPE,
XREQ_CONTENTLEN,XREQ_CONTENT,
FROM XREQ_OBJECTS
WHERE ROWNUM < 200
ORDER BY OH_NAME, XREQ_NAME
The relationships between the various elements are shown below.
XREQ_Name contains three parts separated by underscores (_). Each part is important. As with many other object types, most of the content of XREQ objects is stored in other tables. XREQ objects can contains several tabs, similar to DOCU objects. The first part of XREQ_Name indicates in which table a particular tab is stored. The second part is the OH_Idnr of the XREQ object. For a given OH_Idnr, one row appears in the XREQ table for each tab in the XREQ object. The last part identifies the row uniquely in either the ODOC or OT table. XREQ content is stored in the OT and ODOC tables, specifically in ODOC_Content and OT_Content.
First 4 characters of XREQ_Name | Description | Last 4 characters of XREQ_Name corresponds to |
---|
ODOC | Documentation tab content stored in ODOC table | ODOC.ODOC_Name |
XREQ | Scripting tab content stored in OT table | OT.OT_Type |
Listing XREQ content
The following Oracle query will list the contents of both ODOC and OT tabs of XREQ objects. (Long content will be truncated.)
CREATE OR REPLACE FUNCTION blob2clob (p_blob BLOB)
RETURN CLOB
IS
l_clob CLOB;
l_dest_offsset INTEGER := 1;
l_src_offsset INTEGER := 1;
l_lang_context INTEGER := DBMS_LOB.default_lang_ctx;
l_warning INTEGER;
BEGIN
IF p_blob IS NULL
THEN
RETURN NULL;
END IF;
DBMS_LOB.createTemporary (lob_loc => l_clob, cache => FALSE);
DBMS_LOB.converttoclob (dest_lob => l_clob,
src_blob => p_blob,
amount => DBMS_LOB.lobmaxsize,
dest_offset => l_dest_offsset,
src_offset => l_src_offsset,
blob_csid => DBMS_LOB.default_csid,
lang_context => l_lang_context,
warning => l_warning);
RETURN l_clob;
END;
WITH BIND_PARMS
AS (SELECT 0 AS CLIENT_NUMBER, '%' AS OBJECT_NAME FROM DUAL),
XREQ_OBJECTS
AS ( SELECT OH_Idnr,
OH_Client,
OH_Name,
OH_OType,
XREQ_Name,
CASE SUBSTR (XREQ_Name, 1, 4)
WHEN 'XREQ' THEN 'OT'
WHEN 'ODOC' THEN 'ODOC'
END
AS XREQ_SubType,
CASE SUBSTR (XREQ_Name, 1, 4)
WHEN 'ODOC' THEN SUBSTR (XREQ_Name, 17, 4)
ELSE ' '
END
AS XREQ_ODOC_SubType,
XREQ_CONTENTLEN,
CASE SUBSTR (XREQ_Name, 1, 4)
WHEN 'XREQ' THEN TO_NUMBER (SUBSTR (XREQ_Name, 19, 4))
ELSE NULL
END
AS XREQ_OT_SubType,
XREQ_CONTENT
FROM XREQ LEFT JOIN OH ON SUBSTR (XREQ_Name, 6, 10) = OH_IDNR
WHERE OH_Client = (SELECT CLIENT_NUMBER FROM BIND_PARMS)
ORDER BY OH_Client,
OH_Name,
SUBSTR (XREQ_Name, 1, 4),
SUBSTR (XREQ_Name, LENGTH (XREQ_Name) - 3, 4)),
ODOC_TABS
AS ( SELECT OH_Client,
OH_Idnr,
ODOC_Name,
ODOC_Type,
RTRIM (
XMLCAST (
XMLAGG (
XMLELEMENT (e, blob2clob (ODOC_Content) || CHR (10))
ORDER BY ODOC_Lnr) AS CLOB))
AS ODOC_CONTENT_CLOB
FROM ODOC LEFT OUTER JOIN OH ON ODOC_OH_Idnr = OH_Idnr
WHERE OH_Client = (SELECT CLIENT_NUMBER FROM BIND_PARMS)
GROUP BY OH_Client,
OH_Idnr,
ODOC_Name,
ODOC_Type
ORDER BY OH_Client,
OH_Idnr,
ODOC_Name,
ODOC_Type),
OT_TABS
AS ( SELECT OH_Client,
OH_Idnr,
OT_TYPE,
RTRIM (
XMLCAST (
XMLAGG (XMLELEMENT (e, OT_Content || CHR (10))
ORDER BY OT_Lnr) AS CLOB))
AS OT_CONTENT_CLOB
FROM OT LEFT OUTER JOIN OH ON OT_OH_Idnr = OH_Idnr
WHERE OH_Client = (SELECT CLIENT_NUMBER FROM BIND_PARMS)
GROUP BY OH_Client, OH_Idnr, OT_Type
ORDER BY OH_Client, OH_Idnr, OT_Type),
MAIN_QUERY
AS ( SELECT OH_Client,
OH_IDnr,
OH_OType,
OH_Name,
XREQ_Name,
XREQ_SubType,
XREQ_ODOC_SubType,
XREQ_ContentLen,
XREQ_OT_SubType,
XREQ_Content,
CASE XREQ_SubType
WHEN 'ODOC'
THEN
(SELECT ODOC_CONTENT_CLOB
FROM ODOC_TABS
WHERE OH_Client = ODOC_TABS.OH_Client
AND OH_Idnr = ODOC_TABS.OH_Idnr
AND XREQ_ODOC_SubType = ODOC_TABS.ODOC_Name
AND ROWNUM = 1)
WHEN 'OT'
THEN
(SELECT OT_CONTENT_CLOB
FROM OT_TABS
WHERE OH_Client = OT_TABS.OH_Client
AND OH_Idnr = OT_TABS.OH_Idnr
AND XREQ_OT_SubType = OT_TABS.OT_Type
AND ROWNUM = 1)
END
AS TAB_CONTENT
FROM XREQ_OBJECTS
ORDER BY OH_Name, XREQ_Name)
SELECT *
FROM MAIN_QUERY
WHERE OH_Client = (SELECT CLIENT_NUMBER FROM BIND_PARMS)
AND OH_NAME like (SELECT OBJECT_NAME FROM BIND_PARMS)
All XREQ tabs contain either XML or AE scripting. For example, the XREQ tab of the XABOUT object contains this XML:
<uc-env request = "?Request_id#">
<MESSAGEBOX system = "?system#" client = "?client#">
<message nr = "4006267" insert = "?xreq_name#'">
<OK default="1">
<command owneraction="close"/>
</OK>
<HELP/>
</message>
</MESSAGEBOX >
</uc-env>
XRequest API class
The XRequest
API class can be used, but it’s not very useful.
...
log.writeLine(String.format("Opening %s in read-only mode.", objectName));
XMLRequest xmlReq = new OpenObject(new UC4ObjectName(objectName), true, true);
localUC4Object.sendRequestAndWait(xmlReq);
Common.getMsgBox(xmlReq);
OpenObject openObj = (OpenObject) xmlReq;
UC4Object uc4Object = openObj.getUC4Object();
log.writeLine(String.format("Reading basic information about object %s.", objectName));
String objName = uc4Object.getName();
String objType = uc4Object.getType();
String objIdnr = uc4Object.getIdnr();
boolean objIsExec = uc4Object.isExecutable();
log.writeLine(String.format("Object name : %s", objName));
log.writeLine(String.format("Object type : %s", objType));
log.writeLine(String.format("Object ID : %s", objIdnr));
if ("XREQ".equals(objType)) {
XRequest xRequest = (XRequest) uc4Object;
}
...
It’s not useful because an authorization mechanism prevents reading many hidden objects, including XREQ objects. Any attempt to read an XREQ object using the XRequest class results in error U00000009:
U000000009 'XDESKTOP': Access denied
This authorization mechanism prevents inadvertent changes to system-critical objects. CA developers have a way to bypass this check so that they can edit these objects.
This document is derived from the discussion XREQ objects.
Acknowledgements
The blob2clob function comes from StackOverflow.
Thanks to PhilippElmer, Christian_Boeck_57, and petwir.