appendix-scripting.xml revision 3f86d4e2ad2128cae27b60d8584d6befb05505d8
<?xml version="1.0" encoding="UTF-8"?>
<!--
! CCPL HEADER START
!
! This work is licensed under the Creative Commons
! Attribution-NonCommercial-NoDerivs 3.0 Unported License.
! To view a copy of this license, visit
! or send a letter to Creative Commons, 444 Castro Street,
! Suite 900, Mountain View, California, 94041, USA.
!
! You can also obtain a copy of the license at
! See the License for the specific language governing permissions
! and limitations under the License.
!
! If applicable, add the following below this CCPL HEADER, with the fields
! enclosed by brackets "[]" replaced with your own identifying information:
! Portions Copyright [yyyy] [name of copyright owner]
!
! CCPL HEADER END
!
! Copyright 2011-2014 ForgeRock AS
!
-->
<appendix xml:id="appendix-scripting"
version="5.0"
xml:lang="en"
xmlns="http://docbook.org/ns/docbook"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://docbook.org/ns/docbook http://docbook.org/xml/5.0/xsd/docbook.xsd"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Scripting Reference</title>
<indexterm>
<primary>Scripting</primary>
</indexterm>
<para>
Scripting allows you to customize various aspects of OpenIDM functionality,
for example, by providing custom logic between source and target mappings,
defining correlation rules, filters, and triggers, and so on.
</para>
<section xml:id="scripting-configuration">
<title>Scripting Configuration</title>
<para>
You define scripts using script objects, which can either include the code
directly in the configuration, or call an external file that contains the
script.
</para>
<para>
Custom scripts should be placed in the <literal>script/</literal> folder for
modify or remove the script files located in
contains the default scripts that are required to run specific services.
Scripts in this folder are not guaranteed to remain constant between product
releases.
</para>
<programlisting language="javascript">
{
"type" : "text/javascript",
"source": <replaceable>string</replaceable>
} </programlisting>
<para>or</para>
<programlisting language="javascript">
{
"type" : "text/javascript",
"file" : <replaceable>file location</replaceable>
} </programlisting>
<variablelist>
<varlistentry>
<term>type</term>
<listitem>
<para>string, required</para>
<para>
Specifies the type of script to be executed. Supported types include
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>source</term>
<listitem>
<para>string, required if file is not specified</para>
<para>
Specifies the source code of the script to be executed.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>file</term>
<listitem>
<para>string, required if source is not specified</para>
<para>
Specifies the file containing the source code of the script to execute.
</para>
</listitem>
</varlistentry>
</variablelist>
</section>
<section xml:id="scripting-example">
<title>Examples</title>
<para>
returns <literal>true</literal> if the <literal>employeeType</literal> is
equal to <literal>external</literal>, otherwise returns
<literal>false</literal>. This script can be useful during reconciliation to
establish whether the source object should be a part of the reconciliation,
or ignored.
</para>
<programlisting language="javascript">
"validTarget": {
"type" : "text/javascript",
"source": "target.employeeType == 'external'"
} </programlisting>
<para>
sets the <literal>__PASSWORD__</literal> attribute to
<literal>defaultpwd</literal> when OpenIDM creates a target object.
</para>
<programlisting language="javascript">
"onCreate" : {
"type" : "text/javascript",
"source": "target.__PASSWORD__ = 'defaultpwd'"
} </programlisting>
<para>
file) shows a trigger to create Solaris home directories using a script. The
script is located in a file,
</para>
<programlisting language="javascript">
{
"filters" : [ {
"methods" : [ "create" ],
"onResponse" : {
"type" : "text/javascript",
"file" : "script/createUnixHomeDir.js"
}
} ]
} </programlisting>
</section>
<section xml:id="function-ref">
<title>Function Reference</title>
<indexterm>
<primary>Objects</primary>
<secondary>Script access</secondary>
</indexterm>
<indexterm>
<primary>Scripting</primary>
<secondary>Functions</secondary>
</indexterm>
<para>
Functions (access to managed objects, system objects, and configuration
objects) within OpenIDM are accessible to scripts via the
<literal>openidm</literal> object, which is included in the top-level scope
provided to each script.
</para>
<para>
OpenIDM also provides a <literal>logger</literal> object to access SLF4J
facilities. The following code shows an example:
</para>
<programlisting language="javascript">
logger.info("Parameters passed in: {} {} {}", param1, param2, param3);
</programlisting>
<para>To set the log level, use
</para>
<section xml:id="function-create">
<para>This function creates a new resource object.</para>
<variablelist>
<title>Parameters</title>
<varlistentry>
<term>container</term>
<listitem>
<para>string</para>
<para>
The resource container in which the object will be
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>id</term>
<listitem>
<para>string</para>
<para>
The identifier of the object to be created, if the
client is supplying the ID. If the server should
generate an ID, pass null here.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>value</term>
<listitem>
<para>object</para>
<para>The value of the object to be created.</para>
</listitem>
</varlistentry>
</variablelist>
<itemizedlist>
<title>Returns</title>
<listitem>
<para>The created OpenIDM resource object.
</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>Throws</title>
<listitem>
<para>An exception is thrown if the object could not be created.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="function-patch">
<para>This function performs a partial modification of a managed
object. Unlike the <literal>update</literal> function, only the
modified attributes are provided, not the entire object.</para>
<variablelist>
<title>Parameters</title>
<varlistentry>
<term>id</term>
<listitem>
<para>string</para>
<para>The identifier of the object to be updated.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>rev</term>
<listitem>
<para>string</para>
<para>The revision of the object to be updated, or
<literal>null</literal> if the object is not subject
to revision control.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>value</term>
<listitem>
<para>object</para>
<para>The value of the modifications to be applied to
the object.</para>
</listitem>
</varlistentry>
</variablelist>
<itemizedlist>
<title>Returns</title>
<listitem>
<para>The modified OpenIDM resource object.
</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>Throws</title>
<listitem>
<para>An exception is thrown if the object could not be updated.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="function-read">
<para>This function reads and returns an OpenIDM resource object.</para>
<variablelist>
<title>Parameters</title>
<varlistentry>
<term>id</term>
<listitem>
<para>string</para>
<para>The identifier of the object to be read.</para>
</listitem>
</varlistentry>
</variablelist>
<itemizedlist>
<title>Returns</title>
<listitem>
<para>The read OpenIDM resource object, or <literal>null</literal>
if not found.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="function-update">
<para>This function updates a resource object.</para>
<variablelist>
<title>Parameters</title>
<varlistentry>
<term>id</term>
<listitem>
<para>string</para>
<para>The identifier of the resource object to be updated.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>rev</term>
<listitem>
<para>string</para>
<para>The revision of the object to be updated, or
<literal>null</literal> if the object is not subject to revision control.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>value</term>
<listitem>
<para>object</para>
<para>The value of the object to be updated.</para>
</listitem>
</varlistentry>
</variablelist>
<itemizedlist>
<title>Returns</title>
<listitem>
<para>The modified OpenIDM resource object.
</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>Throws</title>
<listitem>
<para>An exception is thrown if the object could not be updated.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="function-delete">
<para>This function deletes a resource object.</para>
<variablelist>
<title>Parameters</title>
<varlistentry>
<term>id</term>
<listitem>
<para>string</para>
<para>The identifier of the object to be deleted.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>rev</term>
<listitem>
<para>string</para>
<para>The revision of the object to be deleted, or
<literal>null</literal> if the object is not subject to revision control.
</para>
</listitem>
</varlistentry>
</variablelist>
<itemizedlist>
<title>Returns</title>
<listitem>
<para>A <literal>null</literal> value if successful.
</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>Throws</title>
<listitem>
<para>An exception is thrown if the object could not be deleted.
</para>
</listitem>
</itemizedlist>
<para>Note that <literal>delete</literal> is a reserved word in JavaScript
and this function can therefore not be called in the usual manner. To call
delete from a JavaScript, you must specify the call as shown in the
following example:
</para>
<programlisting language="javascript">
</programlisting>
results in an error similar to the following:
</para>
<screen>
</screen>
</section>
<section xml:id="function-query">
<para>This function performs a query on the specified OpenIDM resource object.
</para>
<variablelist>
<title>Parameters</title>
<varlistentry>
<term>id</term>
<listitem>
<para>string</para>
<para>The identifier of the object to perform the query on.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>params</term>
<listitem>
<para>object</para>
<para>An object containing the query ID and its parameters.</para>
</listitem>
</varlistentry>
</variablelist>
<itemizedlist>
<title>Returns</title>
<listitem>
<para>The result of the query. A query result includes the following
parameters:
</para>
<variablelist>
<varlistentry>
<term>"query-time-ms"</term>
<listitem>
<para>The time, in milliseconds, that OpenIDM took to process
the query.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>"conversion-time-ms"</term>
<listitem>
<para>(For an OrientDB repository only) the time, in
milliseconds, taken to convert the data to a JSON object.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>"result"</term>
<listitem>
<para>The list of entries retrieved by the query. The result
includes the revision (<literal>"_rev"</literal>) of the
entry and any other properties that were requested in the
query.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>The following example shows the result of a custom query that
requests the ID, user name, and email address of managed users in the
repository. For an OrientDB repository, the query would be something like
<literal>select _openidm_id, userName, email from managed_user,</literal>.
</para>
<programlisting language="javascript">
{
"conversion-time-ms": 0,
"result": [
{
"email": "bjensen@example.com",
"userName": "bjensen",
"_rev": "0",
"_id": "36bbb745-517f-4695-93d0-998e1e7065cf"
},
{
"email": "scarter@example.com",
"userName": "scarter",
"_rev": "0",
"_id": "cc3bf6f0-949e-4699-9b8e-8c78ce04a287"
}
],
"query-time-ms": 1
}
</programlisting>
</listitem>
</itemizedlist>
<itemizedlist>
<title>Throws</title>
<listitem>
<para>An exception is thrown if the given query could not be processed.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="function-action">
<para>This function performs an action on the specified OpenIDM resource
object.
</para>
<variablelist>
<title>Parameters</title>
<varlistentry>
<term>id</term>
<listitem>
<para>string</para>
<para>The identifier of the object on which the action should be
performed.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>params</term>
<listitem>
<para>object</para>
<para>An object containing the parameters to pass to the action.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>value</term>
<listitem>
<para>object</para>
<para>A value that can be provided to the action for processing.</para>
</listitem>
</varlistentry>
</variablelist>
<itemizedlist>
<title>Returns</title>
<listitem>
<para>The result of the action. May be <literal>null</literal> if
no result is provided.
</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>Throws</title>
<listitem>
<para>An exception is thrown if the given action could not be executed
for any reason.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="function-encrypt">
<para>This function encrypts a value.</para>
<variablelist>
<title>Parameters</title>
<varlistentry>
<term>value</term>
<listitem>
<para>any</para>
<para>The value to be encrypted.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>cipher</term>
<listitem>
<para>string</para>
<para>The cipher with which to encrypt the value, using the form
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>alias</term>
<listitem>
<para>string</para>
<para>The key alias in the key store with which to encrypt the node.</para>
</listitem>
</varlistentry>
</variablelist>
<itemizedlist>
<title>Returns</title>
<listitem>
<para>The value, encrypted with the specified cipher and key.</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>Throws</title>
<listitem>
<para>An exception is thrown if the object could not be encrypted for any
reason.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="function-decrypt">
<para>This function decrypts a value.</para>
<variablelist>
<title>Parameters</title>
<varlistentry>
<term>value</term>
<listitem>
<para>any</para>
<para>The value to be decrypted.</para>
</listitem>
</varlistentry>
</variablelist>
<itemizedlist>
<title>Returns</title>
<listitem>
<para>A deep copy of the value, with any encrypted value decrypted.</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>Throws</title>
<listitem>
<para>An exception is thrown if the object could not be decrypted for any
reason.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="function-logger-debug">
<para>Logs a message at DEBUG level.</para>
<variablelist>
<title>Parameters</title>
<varlistentry>
<term>message</term>
<listitem>
<para>string</para>
<para>The message format to log. Params replace <literal>{}</literal>
in your message.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>params</term>
<listitem>
<para>object</para>
<para>Arguments to include in the message.</para>
</listitem>
</varlistentry>
</variablelist>
<itemizedlist>
<title>Returns</title>
<listitem>
<para>A <literal>null</literal> value if successful.
</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>Throws</title>
<listitem>
<para>An exception is thrown if the message could not be logged.</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="function-logger-error">
<para>Logs a message at ERROR level.</para>
<variablelist>
<title>Parameters</title>
<varlistentry>
<term>message</term>
<listitem>
<para>string</para>
<para>The message format to log. Params replace <literal>{}</literal>
in your message.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>params</term>
<listitem>
<para>object</para>
<para>Arguments to include in the message.</para>
</listitem>
</varlistentry>
</variablelist>
<itemizedlist>
<title>Returns</title>
<listitem>
<para>A <literal>null</literal> value if successful.
</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>Throws</title>
<listitem>
<para>An exception is thrown if the message could not be logged.</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="function-logger-info">
<para>Logs a message at INFO level.</para>
<variablelist>
<title>Parameters</title>
<varlistentry>
<term>message</term>
<listitem>
<para>string</para>
<para>The message format to log. Params replace
<literal>{}</literal>
in your message.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>params</term>
<listitem>
<para>object</para>
<para>Arguments to include in the message.</para>
</listitem>
</varlistentry>
</variablelist>
<itemizedlist>
<title>Returns</title>
<listitem>
<para>A
<literal>null</literal>
value if successful.
</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>Throws</title>
<listitem>
<para>An exception is thrown if the message could not be logged.</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="function-logger-trace">
<para>Logs a message at TRACE level.</para>
<variablelist>
<title>Parameters</title>
<varlistentry>
<term>message</term>
<listitem>
<para>string</para>
<para>The message format to log. Params replace
<literal>{}</literal>
in your message.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>params</term>
<listitem>
<para>object</para>
<para>Arguments to include in the message.</para>
</listitem>
</varlistentry>
</variablelist>
<itemizedlist>
<title>Returns</title>
<listitem>
<para>A
<literal>null</literal>
value if successful.
</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>Throws</title>
<listitem>
<para>An exception is thrown if the message could not be logged.</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="function-logger-warn">
<para>Logs a message at WARN level.</para>
<variablelist>
<title>Parameters</title>
<varlistentry>
<term>message</term>
<listitem>
<para>string</para>
<para>The message format to log. Params replace
<literal>{}</literal>
in your message.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>params</term>
<listitem>
<para>object</para>
<para>Arguments to include in the message.</para>
</listitem>
</varlistentry>
</variablelist>
<itemizedlist>
<title>Returns</title>
<listitem>
<para>A <literal>null</literal> value if successful.
</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>Throws</title>
<listitem>
<para>An exception is thrown if the message could not be logged.</para>
</listitem>
</itemizedlist>
</section>
</section>
<section xml:id="script-places">
<title>Places to Trigger Scripts</title>
<para>Scripts can be triggered at different places, by different events.
</para>
<variablelist>
<varlistentry>
</term>
<listitem>
<variablelist>
<varlistentry>
<term>Triggered by situation</term>
<listitem>
<para>onCreate, onUpdate, onDelete, onLink, onUnlink</para>
</listitem>
</varlistentry>
<!-- what of postCreate, postUpdate, postDelete? -->
<varlistentry>
<term>Object filter</term>
<listitem>
<para>vaildSource, validTarget</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Correlating objects</term>
<listitem>
<para>correlationQuery</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Triggered on any reconciliation</term>
<listitem>
<para>result</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Scripts inside properties</term>
<listitem>
<para>condition, transform</para>
per hook. If multiple scripts are defined for the same hook, only the last
one is kept.
</para>
</listitem>
</varlistentry>
</variablelist>
</listitem>
</varlistentry>
<varlistentry>
</term>
<listitem>
<para>onCreate, onRead, onUpdate, onDelete, onValidate,
onRetrieve, onStore, postCreate, postUpdate, and postDelete
</para>
per hook. If multiple scripts are defined for the same hook, only the last
one is kept.
</para>
</listitem>
</varlistentry>
<varlistentry>
</term>
<listitem>
<para>onRequest, onResponse, onFailure</para>
</para>
</listitem>
</varlistentry>
</variablelist>
</section>
<section xml:id="script-variables">
<title>Variables Available in Scripts</title>
<para>The variables that are available to scripts depend on the triggers
that launch the script. The following section outlines the available
variables, per trigger.
</para>
<variablelist>
<varlistentry>
<term>condition</term>
<listitem>
<para>object</para>
</listitem>
</varlistentry>
<varlistentry>
<term>correlationQuery</term>
<listitem>
<para>source</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Custom endpoint scripts</term>
<listitem>
<para>request</para>
</listitem>
</varlistentry>
<varlistentry>
<term>onCreate, postCreate</term>
<listitem>
<para>object, source, target</para>
</listitem>
</varlistentry>
<varlistentry>
<term>onDelete, postDelete</term>
<listitem>
<para>object</para>
</listitem>
</varlistentry>
<varlistentry>
<term>onLink</term>
<listitem>
<para>source, target</para>
</listitem>
</varlistentry>
<varlistentry>
<term>onRead</term>
<listitem>
<para>object</para>
</listitem>
</varlistentry>
<varlistentry>
<term>onRetrieve</term>
<listitem>
<para>object (when called from either an object or a
property storage trigger); property (only when
called from a property storage trigger)</para>
</listitem>
</varlistentry>
<varlistentry>
<term>onStore</term>
<listitem>
<para>object, property</para>
</listitem>
</varlistentry>
<varlistentry>
<term>onUnlink</term>
<listitem>
<para>source, target</para>
</listitem>
</varlistentry>
<varlistentry>
<term>onUpdate, postUpdate</term>
<listitem>
<para>oldObject, newObject</para>
</listitem>
</varlistentry>
<varlistentry>
<term>onValidate</term>
<listitem>
<para>object, property</para>
</listitem>
</varlistentry>
<varlistentry>
<term>result</term>
<listitem>
<para>source, target</para>
</listitem>
</varlistentry>
<varlistentry>
<term>synchronization situation scripts</term>
<listitem>
the synchronization operation in progress. This variable can
be used for asynchronous callbacks to execute the action at
a later stage.</para>
<para><literal>sourceAction</literal> - a boolean that
indicates whether the situation was assessed during the source phase</para>
<para><literal>source</literal> (if found)</para>
<para><literal>target</literal> (if found)</para>
<para>The properties from the configured script object.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>taskScanner</term>
<listitem>
<para>input, objectID</para>
</listitem>
</varlistentry>
<varlistentry>
<term>transform</term>
<listitem>
<para>source</para>
</listitem>
</varlistentry>
<varlistentry>
<term>validSource</term>
<listitem>
<para>source</para>
</listitem>
</varlistentry>
<varlistentry>
<term>validTarget</term>
<listitem>
<para>target</para>
</listitem>
</varlistentry>
</variablelist>
</section>
<section xml:id="debugging-scripts">
<title>Debugging OpenIDM JavaScripts</title>
<para>
OpenIDM includes Eclipse JSDT libraries so you can use Eclipse to debug
your OpenIDM JavaScripts during development.
</para>
<procedure xml:id="enable-debugging">
<title>To Enable Debugging</title>
<para>
Follow these steps to enable debugging using Eclipse.
</para>
<step>
<para>
Install the environment to support JavaScript development in either of
the following ways.
</para>
<stepalternatives>
<step>
<para>
Download and install Eclipse IDE for JavaScript Web Developers from
xlink:show="new">Eclipse download page</link>.
</para>
</step>
<step>
<para>
xlink:show="new">JavaScript Development Tools</link> to your existing
Eclipse installation.
</para>
</step>
</stepalternatives>
</step>
<step>
<para>
Create an empty JavaScript project called <literal>External JavaScript
Source</literal> in Eclipse.
</para>
<para>
Eclipse then uses the <filename>External JavaScript Source</filename>
directory in the default workspace location to store sources that it
downloads from OpenIDM.
</para>
</step>
<step>
<para>
Stop OpenIDM.
</para>
</step>
<step>
<para>
debugging.
</para>
<substeps>
<step>
<para>
Uncomment and edit the following line.
</para>
<programlisting>
#openidm.script.javascript.debug=transport=socket,suspend=y,address=9888,trace=true
</programlisting>
<para>
Here <literal>suspend=y</literal> prevents OpenIDM from starting until
the remote JavaScript debugger has connected. You might therefore
choose to set this to <literal>suspend=n</literal>.
</para>
</step>
<step>
<para>
Uncomment and edit the following line.
</para>
<programlisting>
</programlisting>
<para>
to match the absolute path to this folder including the trailing
<literal>/</literal> character. On Windows, also use forward slashes,
</para>
<para>
Each time OpenIDM loads a new script, it then creates or overwrites
the file in the <filename>External JavaScript Source</filename>
directory. Before toggling breakpoints, be sure to refresh the source
manually in Eclipse so you have the latest version.
</para>
</step>
</substeps>
</step>
<!-- Fix for OPENIDM-566: JavaScript debugger configuration description refinement -->
<step>
<para>Prepare the Eclipse debugger to allow you to set breakpoints.</para>
<para>In the Eclipse Debug perspective, select the Breakpoints tab, and
then click the Add Script Load Breakpoint icon to open the list of
scripts.
</para>
<para>In the Add Script Load Breakpoint window, select your scripts, and
then click OK.
</para>
</step>
<step>
<para>Start OpenIDM, and connect the debugger.</para>
<para>To create a new debug, configuration click Run > Debug
Configurations... > Remote JavaScript > New button, and then set
the port to 9888 as shown above.
</para>
</step>
</procedure>
</section>
</appendix>