Scripts in IDM/OpenIDM

This book provides information on scripts in IDM/OpenIDM, including troubleshooting them.


Frequently asked questions


FAQ: Scripts in IDM/OpenIDM

The purpose of this FAQ is to provide answers to commonly asked questions regarding scripts in IDM/OpenIDM.

Frequently asked questions

Q. What variables can be used in scripts?

A. The variables that can be used in scripts depend on the triggers that launch the script. The available variables are detailed in the Integrator's Guide › Scripting Reference › Variables Available to Scripts.

Q. How can I use the identityServer variable in scripts?

A. The identityServer variable is a context variable, which can be used for the following locations in scripts:

  • InstallLocation
  • ProjectLocation
  • WorkingLocation

For example:

identityServer.getInstallLocation()

It can also be used to return values of specific properties using getProperty, as used in the crypto.js and policyFilter.js files (located in the /path/to/idm/bin/defaults/script directory).

See Integrator's Guide › Scripting Reference › Variables Available to Scripts for further information on the identityServer variable

Q. When do onUpdate scripts run?

A. onUpdate scripts run before the LiveSync update completes and run regardless of whether the source object has changed or not. The onUpdate script must always execute because it is possible for the onUpdate action to modify the source object itself based on some business logic.

See Integrator's Guide › Data Models and Objects Reference › Managed Object Configuration for further information on when scripts run.

Q. How can I use a postAction script?

A. postAction scripts are used in the sync.json file, where you can associate them with a specific situation and action for a policy. The postAction script is invoked when the corresponding action for the specific policy situation has completed, thereby allowing you to automatically trigger an action upon completion of a related action.

Note

The postAction script is not triggered if the action is set to either IGNORE or ASYNC.

For example, you could use a postAction script on the CREATE action to trigger an update to the LDAP object that you just created. The relevant section in the sync.json file would look similar to this:

{
       "situation" : "ABSENT",
       "action" : "CREATE",
       "postAction" : {
       "type" : "text/javascript",
       "file" : "script/postcreate-update.js"
       }
},

See the Integrator's Guide - Synchronization Situations and Actions and Policy Objects for further information.

Q. How can I troubleshoot scripts?

A. You can add logging to scripts to troubleshoot them as detailed in How do I add logging to JavaScript files in IDM/OpenIDM (All versions)? and How do I add logging to Groovy scripts in IDM/OpenIDM (All versions)?

You can also debug your scripts as detailed in How do I debug server-side JavaScript code in OpenIDM 3.x and 4.x? and How do I debug server-side Groovy code in IDM/OpenIDM (All versions)?

Q. How do I invoke reconciliation (or any other http operation) via REST using a script?

A. You can use the following in JavaScript® to invoke http operations via REST:

openidm.action()

For example, an external REST call via a script would look similar to this:

openidm.action("external/rest", "call", params);

Where the call parameter specifies the action name to be used for invoking the http operation.

See How do I invoke a reconciliation using the openidm.action function in IDM/OpenIDM (All versions)? and Integrator's Guide › Accessing External REST Services for further information.

Q. Why does my external JavaScript script fail to process sample data in the Admin UI?

A. There are known issues in IDM/OpenIDM with evaluating external transformation and conditional scripts against sample data in the Admin UI, which will be fixed in a future release of IDM/OpenIDM. One such issue is: OPENIDM-3127 (Evaluate mapping for source objects as part of reconResults).

External transformation and conditional scripts do not work when you view sample data, but do work if you process the data by performing a reconciliation. As a workaround for viewing sample data, you can include the script inline rather than as an external script.

Q. Why are changes to my JavaScript files being ignored?

A. Once you have made changes to your JavaScript files, you either need to restart IDM/OpenIDM or modify the .json file that invokes the changed JavaScript files. You only need to make a minor modification to the .json file, for example, adding a space at the end of a line and re-saving. Either of these approaches cause the JavaScript files to be reloaded and ensure your changes are observed.

You should also change the value of the following property in the script.json file (located in the /path/to/idm/conf directory) to a much lower value (even 0) to ensure changes are picked up quickly:

"javascript.recompile.minimumInterval" : "60000",

The default is 60000 (60 seconds), which means any changes made to scripts are not picked up for up to 60 seconds. 

Q. Can I use custom Java packages in scripts?

A. Yes, you can use custom Java® packages, but you must prefix the package name with "Packages." in JavaScript. For example:

  • If your package name is:
    custom.package.name
  • You must refer to it as follows in JavaScript files:
    Packages.custom.package.name

JavaScript only recognizes packages beginning with org, com or java, whereas Groovy recognizes all package names.

Q. How do I load external JavaScript functions into other scripts in IDM/OpenIDM?

A. This is described in How do I load JavaScript functions into IDM/OpenIDM (All versions)?

Q. How do I call a Groovy script from another Groovy script?

A. This is described in How do I call one Groovy script from another in IDM/OpenIDM (All versions)?

See Also

How do I invoke a jar file from a Groovy script in IDM/OpenIDM (All versions)?

Scripts in IDM/OpenIDM

Integrator's Guide › Scripting Reference

Related Training

ForgeRock Identity Management Core Concepts (IDM-400)


Groovy


How do I call one Groovy script from another in IDM/OpenIDM (All versions)?

The purpose of this article is to provide assistance in calling a custom Groovy script from another Groovy script in IDM/OpenIDM. Groovy scripts can be used to customize your deployment.

Background information

Groovy scripts can be used to customize your deployment in the following situations:

  • Health Check Scripts
  • Custom Endpoints
  • Groovy Toolkit and Scripted SQL Connectors
  • Synchronization Customizations Logic
  • Audit Data Filters

What follows are two suggestions for keeping your Groovy scripts modularized and organized:

Calling a Groovy script from within the same source location

The source location for your custom Groovy script is configured in the conf/script.json file (see project-script location). The benefit of calling a Groovy script this way is that you will not need to restart IDM/OpenIDM if you make changes to the referenced script. Any changes should automatically get recompiled by the Groovy interpreter.

The following example shows how you can create a custom Groovy script and then call it from within the same source location:

  1. Copy the endpoint-echo.json file from the following sample location to the /path/to/idm/conf directory depending on your version:
    • IDM 5.5: copy the file from the /path/to/idm/samples/example-configurations/custom-endpoint/conf/ directory.
    • Pre-IDM 5.5: copy the file from the /path/to/idm/samples/customendpoint/conf/ directory.
  2. Copy the echo.groovy file from the following sample location to the /path/to/idm/script directory depending on your version:
    • IDM 5.5: copy the file from the /path/to/idm/samples/example-configurations/custom-endpoint/script/ directory.
    • Pre-IDM 5.5: copy the file from the /path/to/idm/samples/customendpoint/script/ directory
  3. Create your Groovy script in the /path/to/idm/script directory. This example creates a GroovyTest.groovy file with the following content:
    import org.slf4j.*;
    
    class GroovyTest {                                    
    	def logger = LoggerFactory.getLogger('logger');
    
        void printTest(String msg) {          
            logger.info(msg);
        }
    }
    
  4. Edit the echo.groovy file and add the following lines immediately after the import statements at the top of the file to call your Groovy script:
    File sourceFile = new File("script/GroovyTest.groovy");
    Class groovyClass = new GroovyClassLoader(getClass().getClassLoader()).parseClass(sourceFile);
    GroovyObject myObject = (GroovyObject) groovyClass.newInstance();
    myObject.printTest("this is from my project script directory");
    
  5. Test your Groovy script by invoking it using a curl command such as:
    $ curl -u openidm-admin:openidm-admin "http://localhost:8080/openidm/endpoint/echo?_prettyPrint=true"
  6. Check the log file to verify that the script has run as expected; you should see the following entry for this example:
    INFO: this is from my project script directory

Calling a Groovy script from the Library location

Calling another Groovy script from the Library location (located in the conf/script.json file as groovy.classpath) is a simple matter of adding the import statement at the top of your calling script:

import MyGroovyTest

You can call a Groovy script from the Library location as follows:

  1. Create your Groovy script in the /path/to/idm/script directory. This example creates a GroovyTest.groovy file with the following content:
    import org.slf4j.*;
    
    class MyGroovyTest {                                    
    	def logger = LoggerFactory.getLogger('logger');
    
        void printTest(String msg) {          
            logger.info(msg);
        }
    }
    
  2. Add the following to your calling script to invoke your Groovy script:
    import MyGroovyTest
    MyGroovyTest mgt = new MyGroovyTest();
    mgt.printTest("This is my library location");
    
    
  3. Restart the IDM/OpenIDM instance to pick up the changes you made to your calling script.

The downside to doing it this way is that any changes made to your library will not get picked up by the calling script until you restart IDM/OpenIDM. You should only place libraries in the groovy.classpath location if you do not expect them to change often.

See Integrator's Guide › Configuring the Server › Setting the Script Configuration for further information about the groovy.classpath.

See Also

How do I add logging to Groovy scripts in IDM/OpenIDM (All versions)?

Integrator's Guide › Starting and Stopping the Server › Reconciliation Health Check

Integrator's Guide › Configuring the Server › Setting the Script Configuration

Connectors Reference › Groovy Connector Toolkit

Connector Developer's Guide › Writing Scripted Connectors With the Groovy Connector Toolkit

Integrator's Guide › Extending Functionality By Using Scripts › Creating a Custom Endpoint Configuration File

How do I invoke a jar file from a Groovy script in IDM/OpenIDM (All versions)?

Related Training

N/A

Related Issue Tracker IDs

OPENIDM-4376 (Resource exception when change an imported groovy script)


How do I invoke a jar file from a Groovy script in IDM/OpenIDM (All versions)?

The purpose of this article is to provide information on class loading from a Groovy script in IDM/OpenIDM.

Overview

The GroovyScriptLoader is used to load classes within a Groovy script. You can use the information in this article to specify the classpath for classes loaded directly as well as those loaded indirectly.

If you experience issues with classes not being found, for example, you see errors such as NoClassDefFoundError or ClassNotFoundException:

SEVERE: Script invocation error 
java.lang.NoClassDefFoundError: full/class/name

...
Caused by: java.lang.ClassNotFoundException: full.class.name
        at java.net.URLClassLoader$1.run(URLClassLoader.java:359)
        at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:648)

You can resolve it by specifying the classpath for the implementing jar files.

Finding classes

You can find classes in an IDM/OpenIDM installation as follows:

  1. Run the following command from the root directory of the installation:
    for x in */*.jar; do echo ">>> $x"; jar tvf $x; echo "<<< $x"; echo; done > jars.log
    
    The resulting file will list all classes found in any jar files.
  2. Search the list of classes for the one referenced in the error.
  3. Scroll up or down from the class to locate the jar file that contains it.
  4. Invoke the required jar file per the steps in this article.

Invoking a jar file from a Groovy script (IDM 6 and later)

You can invoke a jar file from a Groovy script as follows:

  1. Add the external jar file to the /path/to/idm/lib directory.
  2. Update the script.json file (located in the /path/to/idm/conf directory) to append the complete path to the jar file in the groovy.classpath, per your operating system:
    • Unix® and Linux® systems:
      "groovy.classpath" : "&{idm.install.dir}/lib:&{idm.install.dir}/lib/custom-lib-test-1.0.0.jar",
      
    • Microsoft® Windows® systems: (The groovy.classpath requires a semicolon ';' separator on Windows instead of a ':')
      "groovy.classpath" : "&{idm.install.dir}/lib;&{idm.install.dir}/lib/custom-lib-test-1.0.0.jar",
      
  3. Update the Groovy script to import the class(es) and invoke them, for example:
    import org.sample.Sample
    
    samp = new Sample(); java.lang.System.out.println('It is working: ' + samp.reverse("testing"));
    
Note

You can add multiple jars to the class path as detailed in Integrator's Guide › Setting the Script Configuration.

Invoking a jar file from a Groovy script (pre-IDM 6)

You can invoke a jar file from a Groovy script as follows:

  1. Add the external jar file to the /path/to/idm/lib directory.
  2. Update the script.json file (located in the /path/to/idm/conf directory) to append the complete path to the jar file in the groovy.classpath, per your operating system:
    • Unix® and Linux® systems:
      "groovy.classpath" : "&{launcher.install.location}/lib:&{launcher.install.location}/lib/custom-lib-test-1.0.0.jar",
      
    • Microsoft® Windows® systems: (The groovy.classpath requires a semicolon ';' separator on Windows instead of a ':')
      "groovy.classpath" : "&{launcher.install.location}/lib;&{launcher.install.location}/lib/custom-lib-test-1.0.0.jar",
      
  3. Update the Groovy script to import the class(es) and invoke them, for example:
    import org.sample.Sample
    
    samp = new Sample(); java.lang.System.out.println('It is working: ' + samp.reverse("testing"));
    
Note

You can add multiple jars to the class path as detailed in Integrator's Guide › Setting the Script Configuration.

Troubleshooting

If the jar file is not loaded, you should try the following process to remove any outdated generated classes:

  1. Shutdown the IDM/OpenIDM instance.
  2. Delete the install-dir/classes directory.
  3. Delete the contents of the /path/to/idm/felix-cache directory.
  4. Start the IDM/OpenIDM server.

See Also

How do I call one Groovy script from another in IDM/OpenIDM (All versions)?

How do I add logging to Groovy scripts in IDM/OpenIDM (All versions)?

Scripts in IDM/OpenIDM

Integrator's Guide › Setting the Script Configuration

Related Training

N/A

Related Issue Tracker IDs

N/A


How do I add logging to Groovy scripts in IDM/OpenIDM (All versions)?

The purpose of this article is to provide information on adding logging to Groovy scripts in IDM/OpenIDM. You may want to add logging to troubleshoot a script that is not behaving as expected or desired.

Background Information

Groovy in IDM/OpenIDM can either use the SLF4J logging facility or java.util logging depending on which Logger class you import into your script. By default, the logger outputs logging to the standard IDM/OpenIDM log.

SLF4J and Groovy log levels

There are five different log levels available for Groovy files when you use SLF4J logging, where ERROR is the least verbose as it only produces logs in the most serious circumstances through to TRACE, which is the most verbose as it provides detailed information about program execution. These logs take the following format:

logger.error(string message, optional params)
logger.warn(string message, optional params)
logger.info(string message, optional params)
logger.debug(string message, optional params)
logger.trace(string message, optional params)

And there are six different log levels available for Groovy files when you use java.util logging, where SEVERE is the least verbose as it only produces logs in the most serious circumstances through to FINEST, which is the most verbose as it provides detailed information about program execution. These logs take the following format:

logger.severe(string message, optional params)
logger.warning(string message, optional params)
logger.info(string message, optional params)
logger.fine(string message, optional params)
logger.finer(string message, optional params)
logger.finest(string message, optional params)

Logging Level

The following table indicates how these log levels correspond to each other and also to the log levels in the logging.properties file (located in the /path/to/idm/conf directory):

log levels for SLF4J logging log levels for java.util logging logging.properties
ERROR SEVERE SEVERE
WARN WARNING WARNING
INFO INFO INFO
DEBUG FINER and FINE FINER and FINE
TRACE FINEST FINEST

The Global logging level in the logging.properties file applies to everything, including Groovy files and defaults to INFO:

.level=INFO

You can change this if you require a different log level globally. Alternatively, you can use the SLF4J logging within a Groovy script to set logging levels that are specific to one or more Groovy scripts as demonstrated below.

SLF4J logging in Groovy scripts (called directly)

When Groovy scripts are called directly, for example from an endpoint, trigger or workflow, you can either use the SLF4J logging facility or java.util logging. This section details the use of the SLF4J logging facility. With SLF4J logging, you can use logger names (either explicitly or by passing a class) to configure different log levels that only apply to specific Groovy scripts.

Explicit logger name

This example demonstrates defining a custom logger name (myGroovyLogger); you can add the code from steps 1 and 2 to multiple Groovy scripts if you want to define the same log level in multiple scripts:

  1. Include the following code at the top of your Groovy script to import the SLF4J logger and define your custom logger name:
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    final Logger logger = LoggerFactory.getLogger("myGroovyLogger");
    
  2. Include the appropriate logger code within your Groovy script where logging output is required. For example, you could add the following to output a simple message:
    logger.trace("Test log output message");
    
  3. Add your logger name and required log level to the logging.properties file, for example to output at the finest log level:
    myGroovyLogger.level=FINEST
    This example log will output to your IDM/OpenIDM log as:
    Oct 24, 2017 11:24:12 AM org.slf4j.Logger$trace call
    FINEST: Test log output message

Logger name by passing a class

This example demonstrates using a logger name by passing a class, where the class file name is the base name of the *.groovy script when compiled. For example, if you wanted to debug the echo.groovy script (from the echo endpoint sample), you would use echo.class and echo.level in your Groovy script as follows:

  1. Include the following code at the top of your Groovy script to import the SLF4J logger and define your logger name:
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    final Logger logger = LoggerFactory.getLogger(echo.class);
    
  2. Include the appropriate logger code within your Groovy script where logging output is required. For example, you could add the following to output a simple message:
    logger.trace("Test log output message for echo endpoint sample");
    
  3. Add the logger name (in the format scriptName.level) and the required log level to the logging.properties file, for example to output at the finest log level:
    echo.level=FINEST
    This example log will output to your IDM/OpenIDM log as:
    Oct 24, 2017 11:24:12 AM org.slf4j.Logger$trace call
    FINEST: Test log output message for echo endpoint sample

java.util logging in Groovy scripts (called directly)

When Groovy scripts are called directly, for example from an endpoint, trigger or workflow, you can either use the SLF4J logging facility or java.util logging. This section details the use of java.util logging.

To configure java.util logging:

  1. Change the Global logging level in in the logging.properties file if you want a level other than INFO.
  2. Include the following code at the top of your Groovy script to import the java.util logger and define the logger:
    java.util.logging.Logger logger = java.util.logging.Logger.getLogger "" 
    
  3. Include the appropriate logger code within your Groovy script where logging output is required. For example, you could add the following to output a simple message:
    logger.info("Test log output message")
    
    This example log will output to your IDM/OpenIDM log as:
    Oct 24, 2017 11:24:12 AM org.forgerock.openidm.script.javascript.LoggerPropertyFactory$1$3 call
    INFO: Test log output message

Logging in Groovy scripts (called from Groovy connector)

When Groovy scripts are called from the Groovy connector, you can only use the SLF4J logging facility:

  1. Change the Global logging level in in the logging.properties file if you want a level other than INFO.
  2. Include the following code at the top of your Groovy script to import the SLF4J logger:
    import org.identityconnectors.common.logging.Log
    
    
  3. Include the following code to define the logger:
    logger = LoggerFactory.getLogger('logger');
    
  4. Include the appropriate logger code within your Groovy script where logging output is required. For example, you could add the following to output a simple message:
    logger.info("Test log output message");
    
    This example log will output to your IDM/OpenIDM log as:
    [2017-10-12 11:27:26.506] [INFO   ] [org.forgerock.openicf.misc.scriptedcommon.ScriptedConfiguration evaluate]: Test log output message

You can alternatively use the log.info format as demonstrated in the scripted-rest-with-dj sample (scriptedrest2dj) (located in the /path/to/idm/samples directory). The only difference between the sample and the process above is the code used to define the logger and the format of the logger code:

  • Step 3 - use the following code to define the logger:
    def log = log as Log
    
  • Step 4 - use the following logger code format, that is log. instead of logger.:
    log.info("Test log output message");
    

See Also

How do I call one Groovy script from another in IDM/OpenIDM (All versions)?

FAQ: Scripts in IDM/OpenIDM

How do I add logging to JavaScript files in IDM/OpenIDM (All versions)?

Integrator's Guide › Scripting Reference › Logging Functions

Samples Guide › Connecting to DS With ScriptedREST

Connector Developer's Guide › Writing Scripted Connectors With the Groovy Connector Toolkit

SLF4J Logger

Related Training

N/A

Related Issue Tracker IDs

OPENIDM-1246 (Add openidm.logger.*() to activiti for easier workflow logging.)

OPENIDM-4546 (Please make the API for groovy and javascript consistent in openidm


How do I debug server-side Groovy code in IDM/OpenIDM (All versions)?

The purpose of this article is to provide information on debugging server-side Groovy code in IDM/OpenIDM. Debugging Groovy code can help you troubleshoot issues with your code that can be difficult to by only adding logging to your scripts.

Starting IDM/OpenIDM in Debug Mode

Debugging of server-side Groovy scripts within IDM/OpenIDM requires the server instance to be started in debug mode.

You can start an IDM/OpenIDM instance in debug mode as follows:

  1. Shutdown the instance if it is already running.
  2. Use the command line to start IDM/OpenIDM interactively and specify the jpda option as a parameter for the startup.sh script:
    ./startup.sh jpda
    
    Executing ./startup.sh...
    Using OPENIDM_HOME:   /opt/server/openidm
    Using PROJECT_HOME:   /opt/server/openidm
    Using OPENIDM_OPTS:   -Xmx1024m -Xms1024m -Djava.compiler=NONE -Xnoagent -Xdebug -Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n
    Using LOGGING_CONFIG: -Djava.util.logging.config.file=/opt/server/openidm/conf/logging.properties
    Listening for transport dt_socket at address: 5005
    Using boot properties at /opt/server/openidm/resolver/boot.properties
    -> OpenIDM ready
Note

When started in debug mode, the IDM/OpenIDM JVM will listen on port 5005 for connections from a remote debugger. The defaults for suspend and address used by IDM/OpenIDM when running debug mode can be modified within the startup.sh script.

Debugging a Groovy script using JDB

Although there are many IDEs available that allow you to debug Groovy scripts within IDM/OpenIDM, one of the simplest methods available is to use the JDB bundled with the Oracle® JDK. In essence, you attach the JDB to the running IDM/OpenIDM instance and set a breakpoint within the script you want to debug after having set the sourcepath to the location of your Groovy script(s).

Example

In the following example, we are debugging the echo.groovy script included with the IDM/OpenIDM Custom Endpoint sample. The echo.groovy script has been modified to add the following at line 34.

def guid = java.util.UUID.randomUUID()

You can debug the echo.groovy script using JDB as follows:

  1. Execute the JDB utility and attach it to port 5005 on which IDM/OpenIDM is listening:
    $ jdb -attach 5005
    Set uncaught java.lang.Throwable
    Set deferred uncaught java.lang.Throwable
    Initializing jdb ...
  2. Use the sourcepath command to set the location of the Groovy script(s) you intend to debug:
    > sourcepath /Users/jdoe/server/openidm/script
  3. Use the stop in command to set a breakpoint within the echo.groovy script for the Groovy Script.run() method:
    > stop in echo.run
    Set breakpoint echo.run
  4. Execute a REST call against the echo endpoint to invoke it. You can use a curl command such as:
    $ curl -u openidm-admin:openidm-admin http://localhost:8080/openidm/endpoint/echo
    Once you have executed the curl command to invoke the custom echo endpoint, the JDB debugger will stop when the Groovy script engine executes the echo.groovy script. You should see output similar to the following within your JDB session:
    >
    Breakpoint hit: "thread=qtp80929581-100", echo.run(), line=34 bci=4
    34    def guid = java.util.UUID.randomUUID()
  5. Use the next command with the thread identified in the previous step to execute the call to UUID.randomUUID() on line 34:
    qtp80929581-100[1] next
    >
    Step completed: "thread=qtp80929581-100", echo.run(), line=36 bci=18
    36    if (request instanceof CreateRequest) {
    You can now inspect the contents of the guid variable along within the bindings passed to the Groovy script by IDM/OpenIDM and perform other actions. For example:
    • Use the locals command with the identified thread to list local variables that have been defined:
      qtp80929581-100[1] locals
       Method arguments:
       Local variables:
       guid = instance of java.util.UUID(id=7776)
    • Print the contents of the guid variable:
      qtp80929581-100[1] print guid
       guid = "b862a202-cb20-4cf3-b060-3a434e901b79"
    • Print the value of the request property bound to the script execution context by IDM/OpenIDM:
      qtp80929581-100[1] print binding.getProperty("request")
       binding.getProperty("request") = "{ "resourcePath": "", "method": "read", "fields": [  ] }"
    • Continue execution of the Groovy script:
      qtp80929581-100[1] run

See Also

How do I add logging to Groovy scripts in IDM/OpenIDM (All versions)?

FAQ: Scripts in IDM/OpenIDM

Related Training

N/A

Related Issue Tracker IDs

N/A


JavaScript


How do I load JavaScript functions into IDM/OpenIDM (All versions)?

The purpose of this article is to provide assistance if you want to load JavaScript® functions into other scripts in IDM/OpenIDM. You might want to do this to reuse JavaScript functions across a number of JavaScript files.

Loading JavaScript functions

IDM/OpenIDM uses Mozilla® Rhino v1.7R4, which includes a fully compliant CommonJS module implementation.

You can load JavaScript functions as follows:

  1. Consolidate your JavaScript functions into a single JavaScript file.
  2. Add your JavaScript file (containing the functions) to the /lib directory below the current directory.
  3. Load the JavaScript functions using the require() call, for example:
    var _ = require('lib/function.js');
    _.functionname();
    

See Also

Error when loading JavaScript functions into other scripts in OpenIDM 3.x and 4.x

Related Training

ForgeRock Identity Management Core Concepts (IDM-400)

Related Issue Tracker IDs

OPENIDM-1352 (Replace javascript load with modularity)


How do I write to a file using JavaScript on a custom endpoint in IDM/OpenIDM (All versions)?

The purpose of this article is to provide examples of writing to a local file using JavaScript® on a custom endpoint in IDM/OpenIDM. This article assumes you have already created your custom endpoint.

Overview

IDM/OpenIDM uses Mozilla® Rhino v1.7R4, which is an embedded Java® implementation of a JavaScript engine. This implementation allows you to execute JavaScript used by endpoints, inline code etc in IDM/OpenIDM.

Writing to a file using JavaScript examples

Here are two examples of how you can write to a file using JavaScript by calling Java from Rhino:

// Verify that the file exists and if not, create it
var fileObj = new java.io.File("/path/to/myfile/file.txt");
if (!fileObj.exists()) {
fileObj.createNewFile();
}

// Create a filewriter object, then append a line to the file and close the file
var fileWriter = new java.io.FileWriter("/path/to/myfile/file.txt", true); // true = append
var logData = "Test\n";
fileWriter.write(logData);
fileWriter.close();

Or a more efficient version:

var content = new java.lang.String("some string" +
java.lang.System.lineSeparator());
java.nio.file.Files.write(
  java.nio.file.Paths.get("logs/mylog.log"),
  content.getBytes(),
  java.nio.file.StandardOpenOption.CREATE,
  java.nio.file.StandardOpenOption.APPEND
);

See Also

How do I load JavaScript functions into IDM/OpenIDM (All versions)?

How do I add logging to JavaScript files in IDM/OpenIDM (All versions)?

How do I debug server-side JavaScript code in OpenIDM 3.x and 4.x?

FAQ: Scripts in IDM/OpenIDM

Integrator's Guide › Extending Functionality By Using Scripts › Creating Custom Endpoints to Launch Scripts

Samples Guide › Creating a Custom Endpoint

Connector Developer's Guide

Rhino documentation

Related Issue Tracker IDs

N/A


How do I update attributes stored in arrays in IDM/OpenIDM (All versions) using JavaScript?

The purpose of this article is to provide information on updating attributes stored in arrays in IDM/OpenIDM using JavaScript® for the transform script. Attributes are stored in arrays when they need to be multi-valued.

Updating attributes stored in arrays

When an attribute is stored in an array, the transform script needs to iterate over the array and perform the replace on each array element.

For example, the following transform script will update an attribute of type string (single valued):

{
      "source" : "manager",
      "target" : "manager",
                    "transform" : {
                        "type" : "text/javascript",
                        "source" : "source.replace('ou=People,o=forgerock.com', 'ou=people,dc=forgerock,dc=com');"
},

If the attribute is of type array (multi-valued), the transform script needs to iterate over the array and update each array element. The equivalent of the above script for a multi-valued attribute is as follows:

{
      "source" : "manager",
      "target" : "manager",
                    "transform" : {
                        "type" : "text/javascript",
                        "source" : "(function() { var numberOfMembers =source.length; for (var i = 0; i < numberOfMembers; i++) {source[i]=source[i].replace(/o=forgerock.com/i, \"dc=forgerock,dc=com\");}return source;}())"
},

This transform script has been included in the sync.json file (located in the /path/to/idm/conf directory); alternatively, you could include this transform script in a separate file and call the file from sync.json instead, for example:

{
      "source" : "manager",
      "target" : "manager",
                    "transform" : {
                        "type" : "text/javascript",
                        "file" : "script/updateManager.js"
},

See Also

How do I search managed user objects for attributes stored in arrays in IDM/OpenIDM (All versions)?

Integrator's Guide › Synchronizing Data Between Resources › Editing Connector Configuration Files

Related Training

N/A

Related Issue Tracker IDs

N/A


Error when loading JavaScript functions into other scripts in OpenIDM 3.x and 4.x

The purpose of this article is to provide assistance if you encounter an error when loading a JavaScript® file (containing JavaScript functions) into other scripts in OpenIDM using a load function such as: load(identityServer.getProjectLocation() + "/script/function.js");

Symptoms

The JavaScript file fails to load and you may see an error similar to the following when the JavaScript file is called from an endpoint:

Failure in executing script for endpoint/[script name]: ReferenceError: \"openidm\" is not defined

Recent Changes

N/A

Causes

The load function does not pass the objects in the calling scope to the loaded script.

Solution

This issue can be resolved by using the load.call(this, [script location]) function instead, for example:

load.call(this, identityServer.getProjectLocation() + "/script/function.js");

See Also

How do I load JavaScript functions into IDM/OpenIDM (All versions)?

Related Training

N/A

Related Issue Tracker IDs

N/A


How do I add logging to JavaScript files in IDM/OpenIDM (All versions)?

The purpose of this article is to provide information on adding logging to JavaScript® files in IDM/OpenIDM. You may want to add logging to troubleshoot a script that is not behaving as expected or desired.

Background information

JavaScript in IDM/OpenIDM uses the SLF4J logging facility. By default, the logger outputs logging to the standard IDM/OpenIDM log.

There are five different logger levels available for JavaScript files, where ERROR is the least verbose as it only produces logs in the most serious circumstances through to TRACE, which is the most verbose as it provides detailed information about program execution.

These loggers take the following format:

logger.error(string message, optional params)
logger.warn(string message, optional params)
logger.info(string message, optional params)
logger.debug(string message, optional params)
logger.trace(string message, optional params)

These five logger levels correspond to the log levels in the logging.properties file (located in the /path/to/idm/conf directory) as follows:

loggers logging.properties
ERROR SEVERE
WARN WARNING
INFO INFO
DEBUG FINER and FINE
TRACE FINEST

Logging in JavaScript scripts

Logging Level

Within logging.properties, you can set different logging levels, which affect how logging is output in JavaScript files:

  1. Global logging level - this logging level applies to everything, including JavaScript files and defaults to INFO:
    .level
  2. All JavaScript files logging level - this logging level applies to all JavaScript files and overrides the global logging level when set. The property used for setting the log level varies by version:
    • IDM 5.5:
      org.forgerock.openidm.script.javascript.JavaScript.level
    • Pre-IDM 5.5:
      org.forgerock.script.javascript.JavaScript.level
  3. Individual JavaScript files logging level - this logging level applies to specific JavaScript files and overrides the All JavaScript logging level when set. The property used for setting the log level varies by version:
    • IDM 5.5:
      org.forgerock.openidm.script.javascript.JavaScript.script-name.level
    • Pre-IDM 5.5:
      org.forgerock.script.javascript.JavaScript.script-name.level

For example, in IDM 5.5, you could leave the global logging level set to the default INFO. You could then set the following logging levels in logging.properties to generate only WARN logging for all JavaScript files but output DEBUG information for a specific file (myScript):

.level=INFO
org.forgerock.openidm.script.javascript.JavaScript.level=WARNING
org.forgerock.openidm.script.javascript.JavaScript.myScript.level=FINE

By setting logging levels in this way, you are not outputting DEBUG and INFO messages for all JavaScript files, only the one you want to debug.

JavaScript files

Within your JavaScript files, you can then add the appropriate logger code to log to the IDM/OpenIDM log.

For example, in the JavaScript file that you want to debug (myScript in the above example), you could add the following:

logger.debug("Test error message logged for user {}", userName);

This example logger will output to your IDM/OpenIDM log as:

May 24, 2014 11:27:28 AM org.forgerock.openidm.script.javascript.LoggerPropertyFactory$1$3 call
FINE: Test error message logged for user jdoe

See Also

FAQ: Scripts in IDM/OpenIDM

Customizing Java Log Format to use SimpleFormatter fails in IDM/OpenIDM (All versions)

Integrator's Guide › Configuring Server Logs › Logging Levels

Integrator's Guide › Scripting Reference › Logging Functions

Integrator's Guide › Using Scripts to Generate Log Messages

SLF4J Logger

Related Training

N/A

Related Issue Tracker IDs

OPENIDM-1246 (Add openidm.logger.*() to activiti for easier workflow logging.)


How do I debug server-side JavaScript code in OpenIDM 3.x and 4.x?

The purpose of this article is to provide information on debugging server-side JavaScript® code in OpenIDM using Eclipse® IDE for Java EE developers. Debugging JavaScript code can help you troubleshoot issues with your code that cannot be resolved by adding logging to your scripts.

Debugging JavaScript code

Note

JavaScript debugging is only supported with Eclipse Luna; the more recent releases are not supported.

To configure JavaScript debugging via Eclipse:

  1. Download the Java EE version of Eclipse Luna from the Eclipse Downloads page.
  2. Back up and delete the following files from the /path/to/openidm/bundle directory:
    • rhino.debugger-1.0.300.v201109150503.jar
    • transport-1.0.100.v201109150330.jar
  3. Copy the following files from the Eclipse plugins directory to the /path/to/openidm/bundle directory:
    • org.eclipse.wst.jsdt.debug.rhino.debugger_1.0.400.v201402051800.jar
    • org.eclipse.wst.jsdt.debug.transport_1.0.200.v201402051800.jar
  4. Delete the contents of the /path/to/openidm/felix-cache directory.
  5. Edit the script.json file (located in the /path/to/openidm/project/conf directory) and uncomment the following line:
    "javascript.debug" : "transport=socket,suspend=y,address=9888,trace=true",
    
  6. Start Eclipse and create a new JavaScript Project.
  7. Select JavaScript > Include Path > Source within the Project Properties and add a link to the folder(s) containing the JavaScript files you want to debug via the Link External Folder... button.
  8. Start the OpenIDM instance.
  9. Attach the debugger to OpenIDM via the Run > Debug Configurations... menu item in Eclipse.

See Also

How do I add logging to JavaScript files in IDM/OpenIDM (All versions)?

Customizing Java Log Format to use SimpleFormatter fails in IDM/OpenIDM (All versions)

FAQ: Scripts in IDM/OpenIDM

Related Training

N/A

Related Issue Tracker IDs

OPENIDM-3351 (Unable to debug JavaScript code within OpenIDM 3.x)


How do I load OSGI bundles and JAR files into IDM/OpenIDM (All versions)?

The purpose of this article is to provide information on loading Open Service Gateway Initiative (OSGI) bundles and JAR files into IDM/OpenIDM from separate locations.

Loading OSGI bundles and JAR files

You can load OSGI bundles and JAR files from separate locations using the launcher.json​ configuration script (located in the /path/to/idm/bin directory by default). The default locations defined within this file are relative to the OPENIDM_HOME directory unless an absolute path is specified. If required, you can locate the launcher.json file in a different location providing you start IDM/OpenIDM with the -c option and specify the path. See Integrator's Guide › Advanced Startup Configuration for further information about the launcher.json file.

To load OSGI bundles and JAR files:

  1. Add location, includes and excludes properties to launcher.json to specify the absolute path to where the files are located and to define the jars you want to include or exclude from loading. For example:
    {
        "location":"/path/to/separate/location",
        "includes":["*.jar"]
    }, 
    
  2. Restart the IDM/OpenIDM instance to apply these changes.

See Also

How do I invoke a jar file from a Groovy script in IDM/OpenIDM (All versions)?

How do I load JavaScript functions into IDM/OpenIDM (All versions)?

Integrator's Guide › Specifying the Startup Configuration

Related Training

N/A

Related Issue Tracker IDs

N/A


Workflow


How do I use workflow scripts to make calls back to IDM/OpenIDM (All versions)?

The purpose of this article is to provide assistance with using workflow scripts to make calls back to IDM/OpenIDM. This article gives some examples around managed users to help you create your own workflows.

Using workflow scripts to make calls back to IDM/OpenIDM

Example scripts are included in this article for the following use cases:

  • How do I query IDM/OpenIDM for a list of all managed users? - this example calls openidm.query from a workflow.
  • How do I query IDM/OpenIDM for a list of all managed provisioning roles? - this example calls openidm.query from a workflow.
  • How do I patch a managed user to add additional roles? - this example calls openidm.patch from a workflow.
  • How do I retrieve a managed user's attributes? -  this example calls openidm.query from a workflow.

Querying IDM/OpenIDM for a list of all managed users

To achieve this in a workflow, you could add the following scriptTask code to the workflow XML file:

    <scriptTask id="scripttask1" name="Read Users" scriptFormat="groovy"> 
      <script>
                params = [_queryId:'query-all-ids']
                out:println "script task using resolver: " + openidm.query('managed/user', params)
                execution.setVariable('params', params)
      </script> 
    </scriptTask> 

Querying IDM/OpenIDM for a list of all managed provisioning roles

To achieve this in a workflow, you could add the following scriptTask code to the workflow XML file:

   <scriptTask id="scripttask1" name="Read Roles" scriptFormat="groovy"> 
      <script>
                params = [_queryId:'query-all-ids']
                out:println "script task using resolver: " + openidm.query('managed/role', params)
                execution.setVariable('params', params)
      </script> 
    </scriptTask> 

Patching a managed user to add additional roles

To achieve this in a workflow, you could add the following scriptTask code to the workflow XML file:

    <scriptTask id="scripttask1" name="Patch User" scriptFormat="groovy"> 
      <script>
                patchValue = [[operation: 'replace', field:'description',value:'testing']]
                out:println "script task using resolver: " + openidm.patch('managed/user/f03d01e8-b3e3-41b0-b2df-50776e9df744', null, patchValue)
                execution.setVariable('patchValue', patchValue)
      </script> 
    </scriptTask> 

Retrieving a managed user's attributes

To achieve this in a workflow, you could add the following scriptTask code to the workflow XML file:

    <scriptTask id="scripttask1" name="Retrieve Attributes" scriptFormat="groovy">
      <script>
                readStartUserFromRepoParams = [_queryId:'for-userName',uid:startUserId]
                startUserFromRepo = openidm.query('managed/user', readStartUserFromRepoParams)
                execution.setVariable("startUserFromRepo", startUserFromRepo)
      </script>
    </scriptTask>

See Also

How do I use RequireJS to load dependencies inside a workflow in IDM 5.x, 6 and OpenIDM 4.x?

How do I migrate my existing BPMN workflows after upgrading to IDM 5.5 or applying Security Advisory #201705?

Scripts in IDM/OpenIDM

Integrator's Guide › Integrating Business Processes and Workflows › Setting Up Activiti Integration

Samples Guide › Using a Workflow to Provision User Accounts

Samples Guide › Asynchronous Reconciliation Using a Workflow

Related Training

N/A

Related Issue Tracker IDs

OPENIDM-1245 (Align openidm and activiti contract on scripting(openidm.action() and openidm.patch() failed in a workflow on managed object.))


How do I use RequireJS to load dependencies inside a workflow in IDM 5.x, 6 and OpenIDM 4.x?

The purpose of this article is to provide information on loading dependencies (external libraries) inside a workflow using RequireJS. You might want to load dependencies to either reuse JavaScript® methods, or call standard jQuery or jQuery UI functions. An example is given in this article to embed the jQuery UI Datepicker function in a workflow. This information does not apply to IDM 6.5 and later because that uses the Vue JS framework.

Background information

Key things to understand about loading dependencies inside a workflow:

  • Workflow definitions are manged by Activiti; the JavaScript engines used by Activiti do not support CommonJS, which includes RequireJS. This means you need to load the RequireJS library (r.js) in order to load dependencies:
    • JDK 8 uses the Nashorn JavaScript engine, which does not support CommonJS.
    • JDK 7 uses the Rhino JavaScript engine bundled within the JVM; this version of Rhino is pre-CommonJS support.
    • Activiti uses the JVM's ScriptEngineManager to locate a supported JavaScript engine, which means the JDK's embedded version of Rhino is always used instead of the version bundled with IDM/OpenIDM (which does support CommonJS). 
  • Workflow XHTML forms utilize the UI library loading mechanism; the library loading mechanism in the IDM/OpenIDM UI is based on RequireJS:
    • Workflow forms are loaded as follows:
      1. The UI interrogates the workflow engine via a REST call.
      2. It extracts the form definition from the process definition.
      3. It dynamically renders it using different mechanisms based on the form type.
    • The entire form file is loaded with RequireJS provided you use the recommended activiti:formKey attribute to refer to the HTML template in the workflow definition. See Integrator's Guide › Using Custom Templates for Activiti Workflows for further information. 
    • Module Loader is available from within the form since Module Loader has jQuery in its scope (as '$').
    • jQuery is included in the UI dependencies.
    • jQuery-ui is not included in the UI dependencies, which means jQuery UI functions are not available by default in the UI. To use jQuery UI functions, you therefore need to load them from the form using RequireJS.

Loading dependencies using RequireJS

You can load external libraries within a workflow using RequireJS as follows, depending on where the call is coming from:

  • Workflow definition - scriptTask:
    <scriptTask id="test" name="Test Task" scriptFormat="javascript" activiti:autoStoreVariables="false">
       <script>
         load("path/to/r.js"); 
         require(["path/to/module"], function(module) { //some code});
       </script>
    </scriptTask>
    
  • Workflow XHTML form:
    <script>
       require(["path/to/module"], function(module) { //some code});
    </script>
    
    

Datepicker example

You can load jQuery UI functions using RequireJS. The following example demonstrates this by loading the jQuery UI Datepicker function into a workflow XHTML form:

  1. Include the following in your workflow XHTML form to provide the Datepicker input, load the jQuery-ui library and dynamically load the CSS: 
    <div class="form-group">
     <p>Enter Date: <input type = "text" id = "datepicker"></p>
    </div>
    
    ...
    
    <script>
    
    function loadCss(url) {
        var link = document.createElement("link");
        link.type = "text/css";
        link.rel = "stylesheet";
        link.href = url;
        document.getElementsByTagName("head")[0].appendChild(link);
    }
    
    $(document).ready(function() {
      loadCss("https://code.jquery.com/ui/1.10.4/themes/ui-lightness/jquery-ui.css")
      require(["https://code.jquery.com/ui/1.10.4/jquery-ui.js"], function() {
        $( "#datepicker" ).datepicker();
     })
    });
    </script>
    

The above example code renders a Datepicker in the workflow form as follows:

See Also

How do I use workflow scripts to make calls back to IDM/OpenIDM (All versions)?

How do I migrate my existing BPMN workflows after upgrading to IDM 5.5 or applying Security Advisory #201705?

Integrator's Guide › Integrating Business Processes and Workflows

Related Training

N/A

Related Issue Tracker IDs

N/A


How do I customize exceptions thrown for custom endpoints in IDM/OpenIDM (All versions)?

The purpose of this article is to provide information on customizing exceptions thrown for custom endpoints in IDM/OpenIDM. This allows you to output a custom message for standard HTTP error codes.

Customizing exceptions

You can add exceptions to endpoints as detailed in the documentation: Integrator's Guide › Setting Up Exceptions in Scripts.

If you want to customize the error response further, you can add information to the detail property (JavaScript®) or setDetail (Groovy); the code and reason properties are reserved for the standard HTTP error codes and messages.

Example

You want to include the following additional details for a 404: Not Found error code:

  • custom error code
  • custom message
  • description
  • severity rating

Add the following code to your script depending on your script type:

  • JavaScript:
    throw {
     code : 404,
     message : "Error",
     detail: {
             code:  "ERROR1",
             description : "Custom error description", 
             severity : "Fatal"
      }
    }
    
    
    
  • Groovy:
    import org.forgerock.json.resource.ResourceException
    import org.forgerock.json.JsonValue
    
    throw new ResourceException(404, "Error").setDetail(new JsonValue([
        "code": "ERROR1",
        "description": "Custom error description", 
        "severity": "Fatal"
    ])) 
    

These code changes will output the following response when a 404 error is encountered:

{
    "code": 404,
    "reason": "Not Found",
    "message": "Error",
    "detail": {
        "code": "ERROR1",
        "severity": "Fatal",
        "description": "Custom error description"
    }
}

See Also

How do I write to a file using JavaScript on a custom endpoint in IDM/OpenIDM (All versions)?

Custom endpoints that worked in previous versions of OpenIDM fail in OpenIDM 4.x

How do I customize authorization rules for http requests in IDM/OpenIDM (All versions)?

How do I add logging to JavaScript files in IDM/OpenIDM (All versions)?

How do I debug server-side JavaScript code in OpenIDM 3.x and 4.x?

FAQ: Scripts in IDM/OpenIDM

Scripts in IDM/OpenIDM

Related Training

N/A

Related Issue Tracker IDs

N/A


Copyright and TrademarksCopyright © 2018 - 2019 ForgeRock, all rights reserved.

This content has been optimized for printing.

Loading...