Skip to content
Data

3forge REST Server

3forge provides its own REST server API for retrieving statistics about the 3forge session.

Note

If you are looking for documentation on the REST datasource adapter, see here.

Overview

The 3forge REST server is used for providing information about the 3forge session and the VM it's running in.

Generally, the REST endpoints are in two categories:

  1. Retrieving information about the 3forge session
    • This includes information on JVM memory usage, user statistics, and more.
  2. Querying the Center
    • This involves sending commands into the Center and requires authentication.

The default endpoint of the REST server is attributed to the web server's own ports followed by the REST query. For example: "localhost:33332/3forge_rest/stats".

To configure your 3forge session's REST Server API, see the following full list of properties. Otherwise, for a minimal configuration see the setup below.

Setup

The default configuration for the 3forge REST Server API as shipped in default.properties looks similar to this:

1
2
3
4
5
6
7
8
ami.rest.uses.web.port=true
ami.rest.plugin.classes=com.f1.ami.amicommon.rest.AmiRestPlugin_Version,\
                        com.f1.ami.amicommon.rest.AmiRestPlugin_Whoami,\
                        com.f1.ami.amicommon.rest.AmiRestPlugin_Whatsmyip,\
                        com.f1.ami.amicommon.rest.AmiRestPlugin_Stats,\
                        com.f1.ami.center.AmiRestPlugin_Query
ami.rest.auth.plugin.class=${ami.auth.plugin.class}
ami.rest.auth.plugin.cache.duration=10 seconds

The plugin classes that ship natively with 3forge are the default endpoints, which are listed below.

To use a different address to the web port, you will need to refer to this configuration guide to set the appropriate properties.

Properties

A full list of configurable properties for REST can be found here, but the minimum required properties are the following:

# a comma-delimited list of the common endpoints
ami.rest.plugin.classes=com.f1.ami.amicommon.rest.AmiRestPlugin_Version,\
                        com.f1.ami.amicommon.rest.AmiRestPlugin_Whoami,\
                        com.f1.ami.amicommon.rest.AmiRestPlugin_Whatsmyip,\
                        com.f1.ami.amicommon.rest.AmiRestPlugin_Stats

# the authorization plugin class that REST uses. 3forge provides one by default
ami.rest.auth.plugin.class=${ami.auth.plugin.class} 

# the cache duration time of authorization plugin
ami.rest.auth.plugin.cache.duration=10 seconds

Default Endpoints

The default endpoints supplied by 3forge are primarily aimed at developers to debug and retrieve information about the JVM state of the 3forge session.

While some endpoints are publicly available, others require admin or dev access. See the below table for a list of the default endpoints and their access requirements.

Endpoint Description Authentication Required Admin Only Params
/3forge_rest/version Returns detailed version information about the 3forge session display=json or display=text
/3forge_rest/whoami Returns the username and properties display=json or display=text
/3forge_rest/whatsmyip Returns the IP address of the client accessing the endpoint display=json or display=text
/3forge_rest/stats Public endpoint that returns statistics about the VM display=json or display=text
/3forge_rest/query? Executes a command against the Center and returns the result sets. See here for more information on query commands and parameters full list here

Note

All default endpoints are GET requests.

Authentication

Some of the default endpoints require authentication, admin permissions, or both.

Depending on how you access the REST Server, the method to do so will differ.

Basic Authentication

For the simplest authentication setup that uses a combination of a 3forge user's username and password, see the instructions below.

When accessing an endpoint that requires authorization via browser links, i.e: navigating to http://localhost:33332/3forge_rest/version, you will be prompted to login.

Enter your 3forge username and password details in the pop-up.

To send a curl request via a terminal, supply the username and password token using the Basic Authentication flag:

curl -u username:password -X GET "http://localhost:33332/3forge_rest/version?display=json"

This will output the full JSON of the query endpoint, in this case version, in the terminal.

To send requests via the command-line or PowerShell, you will need to authorize your request with an Authorization header.

WebRequests must be encoded. Input the following into PowerShell, replacing username:password with your details:

1
2
3
4
5
$auth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("username:password"))
$headers = @{ Authorization = "Basic $auth" }
$url = "http://localhost:33332/3forge_rest/version?display=json"
$response = Invoke-WebRequest -Uri $url -Method GET -Headers $headers
$response.Content

This will output the full JSON of the query endpoint, in this case version, in the terminal.

Examples

These are examples of queries that can be passed to the REST API.

Examples assume the following that the 3forge session is being hosted on your local machine with default ports (localhost:33332). Please change these details according to your setup.

GET /3forge_rest/version

3forge session version information

Query Commands

Params Description Options
display What format to display the response json or text

Default:

http://localhost:33332/3forge_rest/version

With display params:

http://localhost:33332/3forge_rest/version?display=json

Default:

curl -u username:password -X GET "http://localhost:33332/3forge_rest/version

With display params:

curl -u username:password -X GET "http://localhost:33332/3forge_rest/version?display=json" 

Once authenticated, default:

Invoke-WebRequest -Uri "http://localhost:33332/3forge_rest/version" -Method GET -Headers $headers

With display params:

Invoke-WebRequest -Uri "http://localhost:33332/3forge_rest/version?display=json" -Method GET -Headers $headers

Query Outputs

These are example outputs as if called from within the browser. Calling via terminal will retrieve slightly different formatting.

Display

Output from hitting the endpoint with no additional parameters (http://localhost:33332/3forge_rest/version), or with display=text (http://localhost:33332/3forge_rest/version?display=text)

This will return a webpage with information similar to the following:

3forge Version: version
+----------------------------------
| Visit http://3forge.com
| 3forge.com
+----------------------------------
|           Startup Time: Thu May 01 10:10:18 BST 2025
|            Working Dir: C:/Program Files/ami_dev/amione
|             Local Host: PC_NAME
|                    Pid: 6164
|          F1 ProcessUid: F1-c2kVn5PI9DUgPZ0GE8lY0i
|              CPU Count: 14
|           Avail.Memory: 3.78 GB (4,054,319,104 B)
|         Line Seperator: \r\n
|              Java Home: c:\program files\ami_dev\jre
|           Java Version: 1.8.0_242
|            Java Vendor: AdoptOpenJDK
|          Java Compiler: null
|           RC5 Strength: unlimited
|           OS - Version: Windows 10 - 10.0
|               OS Arch.: amd64
|           Current Time: Thu May 01 10:21:34 BST 2025
|       Default TimeZone: Europe/London
|        Default Charset: UTF-8
|        Default Country: GB
|       Default Language: en
|                   User: User
|              User Home: C:\Users\User
+----------------------------------
|            License App: amione
|       License Instance: id=717021
|           License Host: latitude5350-1
|     License Start Date: 20250120
|       License End Date: 20260101
|           License Text: 3FKEY|amione|id=717021|PC_NAME|start_date|end_date|license_key
+----------------------------------
|         Max Code Cache: 240.00 MB (251,658,240 B)
|          Max Metaspace: -1 (-1 B)
| Max Compressed Class Space: 1,024.00 MB (1,073,741,824 B)
|     Max Par Eden Space: 732.19 MB (767,754,240 B)
| Max Par Survivor Space: 91.50 MB (95,944,704 B)
|        Max CMS Old Gen: 2.97 GB (3,190,620,160 B)
+----------------------------------
|        Java Ext. Dirs.: c:\program files\ami_dev\jre\lib\ext
|                         C:\WINDOWS\Sun\Java\lib\ext
+----------------------------------
|         Java Classpath: C:\Program Files\ami_dev\.install4j\i4jruntime.jar                                                 (cksum  1291145369 1946822)
|                         C:\Program Files\ami_dev\amione\..\amione\lib\autocode.jar                                         (cksum  3575987261 4746753)
|                         C:\Program Files\ami_dev\amione\..\amione\lib\ch.ethz.ganymed-ganymed-ssh2-262.jar                 (cksum  1554228428 307216)
....

Output from hitting the endpoint with the additional display=json parameter (http://localhost:33332/3forge_rest/version?display=json).

This will return a webpage similar to the following:

Navigate between the different headers. To view/download the raw JSON, select the "Raw Data" tab. Below is a sample of the Pretty Print formatted JSON output from this endpoint:

{
"Avail.Memory": "3.78 GB (4,054,319,104 B)",
"Boot Classpath": [
    "c:\\program files\\ami_dev\\jre\\lib\\resources.jar   (cksum  926139156 3495695)",
    "c:\\program files\\ami_dev\\jre\\lib\\rt.jar          (cksum  2997456213 62558874)",
    "c:\\program files\\ami_dev\\jre\\lib\\sunrsasign.jar  (missing)",
    "c:\\program files\\ami_dev\\jre\\lib\\jsse.jar        (cksum  2395934130 682252)",
    "c:\\program files\\ami_dev\\jre\\lib\\jce.jar         (cksum  1407907570 94879)",
    "c:\\program files\\ami_dev\\jre\\lib\\charsets.jar    (cksum  3896236762 3090350)",
    "c:\\program files\\ami_dev\\jre\\lib\\jfr.jar         (missing)",
    "c:\\program files\\ami_dev\\jre\\classes             (missing)"
],
"CPU Count": "14",
"Current Time": "Thu May 01 10:24:13 BST 2025",
"Default Charset": "UTF-8",
"Default Country": "GB",
"Default Language": "en",
"Default Properties": {
    "ami.amilog.stats.period": "15 SECONDS",
    "ami.amiscript.default.limit": "10000",
    "ami.amiscript.default.timeout": "60 seconds",
    "ami.auth.timeout.ms": "5000",
    "ami.autosave.count": "100",
    "ami.autosave.dir": "data/autosave",

    ...

    "web.logged.out.url": "/",
    "web.title": "3forge Website"
},
"Default TimeZone": "Europe/London",
"Env. Vars": {
    "=::": "::\\",
    "ALLUSERSPROFILE": "C:\\ProgramData",
    "APPDATA": "C:\\Users\\User\\AppData\\Roaming",
    "CLASS_PATH": "c:\\Program Files\\Java\\jdk1.8.0_144\\lib;c:\\Program Files\\Java\\jdk1.8.0_144\\jre\\lib",
    "COMPUTERNAME": "PC_NAME",
    "ComSpec": "C:\\WINDOWS\\system32\\cmd.exe",
    "CommonProgramFiles": "C:\\Program Files\\Common Files",
    "CommonProgramFiles(x86)": "C:\\Program Files (x86)\\Common Files",
    "CommonProgramW6432": "C:\\Program Files\\Common Files",
    "DriverData": "C:\\Windows\\System32\\Drivers\\DriverData",
    "EFC_3404": "1",
    "HOMEDRIVE": "C:",
    "HOMEPATH": "\\Users\\User",
    "JAVA_HOME": "c:\\Program Files\\Java\\jdk1.8.0_144",
    "JAVA_VERSION": "1.8",

    ...

    "USERDOMAIN": "AzureAD",
    "USERDOMAIN_ROAMINGPROFILE": "AzureAD",
    "USERNAME": "User",
    "USERPROFILE": "C:\\Users\\User",
    "ZES_ENABLE_SYSMAN": "1",
    "windir": "C:\\WINDOWS"
}
...
}     

GET /3forge_rest/whoami

Username and properties details of user accessing the 3forge session.

Query Commands

Params Description Options
display What format to display the response json or text

Default:

http://localhost:33332/3forge_rest/whoami

With display params:

http://localhost:33332/3forge_rest/whoami?display=json

Default:

curl -u username:password -X GET "http://localhost:33332/3forge_rest/whoami

With display params:

curl -u username:password -X GET "http://localhost:33332/3forge_rest/whoami?display=json" 

Once authenticated, default:

Invoke-WebRequest -Uri "http://localhost:33332/3forge_rest/whoami" -Method GET -Headers $headers

With display params:

Invoke-WebRequest -Uri "http://localhost:33332/3forge_rest/whoami?display=json" -Method GET -Headers $headers

Query Outputs

These are example outputs as if called from within the browser. Calling via terminal will retrieve slightly different formatting.

Display

Output from hitting the endpoint with no additional parameters (http://localhost:33332/3forge_rest/whoami), or with display=text (http://localhost:33332/3forge_rest/whoami?display=text)

This will return a webpage with information similar to the following:

1
2
3
4
5
Username: demo
|
+--> ISDEV=true
+--> ISADMIN=true
+--> timeZone=ACT

Output from hitting the endpoint with the additional display=json parameter (http://localhost:33332/3forge_rest/whoami?display=json).

This will return a webpage similar to the following:

Navigate between the different headers. To view/download the raw JSON, select the "Raw Data" tab. Below is a sample of the Pretty Print formatted JSON output from this endpoint:

1
2
3
4
5
6
7
8
{
"attributes": {
    "ISADMIN": "true",
    "ISDEV": "true",
    "timeZone": "ACT"
},
"username": "demo"
}

GET /3forge_rest/whatsmyip

IP address information of client accessing the endpoint.

Query Commands

Params Description Options
display What format to display the response json or text

Default:

http://localhost:33332/3forge_rest/whatsmyip

With display params:

http://localhost:33332/3forge_rest/whatsmyip?display=json

Default:

curl -u username:password -X GET "http://localhost:33332/3forge_rest/whatsmyip

With display params:

curl -u username:password -X GET "http://localhost:33332/3forge_rest/whatsmyip?display=json" 

Once authenticated, default:

Invoke-WebRequest -Uri "http://localhost:33332/3forge_rest/whatsmyip" -Method GET -Headers $headers

With display params:

Invoke-WebRequest -Uri "http://localhost:33332/3forge_rest/whatsmyip?display=json" -Method GET -Headers $headers

Query Outputs

These are example outputs as if called from within the browser. Calling via terminal will retrieve slightly different formatting.

Display

Output from hitting the endpoint with no additional parameters (http://localhost:33332/3forge_rest/whatsmyip), or with display=text (http://localhost:33332/3forge_rest/whatsmyip?display=text)

This will return a webpage with information similar to the following:

IP Address: 127.0.0.1
    Port: 52184

Http Request Headers:
    Accept-Encoding=gzip, deflate, br, zstd
    User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0
    Connection=keep-alive
    Sec-Fetch-Site=none
    Priority=u=0, i
    Sec-Fetch-Dest=document
    Sec-Fetch-User=?1
    Authorization=Basic ZGVtbzpkZW1vMTIz
    Cookie=F1SESSION=79YKS2SqCGCbxRqELzQKM1sMRe
    Accept-Language=en-GB,en;q=0.5
    Upgrade-Insecure-Requests=1
    Host=localhost:33332
    Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Sec-Fetch-Mode=navigate

Http Request Headers:
    Authorization

Output from hitting the endpoint with the additional display=json parameter (http://localhost:33332/3forge_rest/whatsmyip?display=json).

This will return a webpage similar to the following:

Navigate between the different headers. To view/download the raw JSON, select the "Raw Data" tab. Below is a sample of the Pretty Print formatted JSON output from this endpoint:

{
"headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate, br, zstd",
    "Accept-Language": "en-GB,en;q=0.5",
    "Connection": "keep-alive",
    "Cookie": "F1SESSION=79YKS2SqCGCbxRqELzQKM1sMRe",
    "Host": "localhost:33332",
    "Priority": "u=0, i",
    "Sec-Fetch-Dest": "document",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "none",
    "Sec-Fetch-User": "?1",
    "Upgrade-Insecure-Requests": "1",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0"
},
"ip": "127.0.0.1",
"port": 33332,
"redactedHeaders": [
    "Authorization"
]
}

GET /3forge_rest/stats

3forge JVM statistics

Query Commands

Params Description Options
display What format to display the response json or text

Default:

http://localhost:33332/3forge_rest/stats

With display params:

http://localhost:33332/3forge_rest/stats?display=json

Default:

curl -u username:password -X GET "http://localhost:33332/3forge_rest/stats

With display params:

curl -u username:password -X GET "http://localhost:33332/3forge_rest/stats?display=json" 

Once authenticated, default:

Invoke-WebRequest -Uri "http://localhost:33332/3forge_rest/stats" -Method GET -Headers $headers

With display params:

Invoke-WebRequest -Uri "http://localhost:33332/3forge_rest/stats?display=json" -Method GET -Headers $headers

Query Outputs

These are example outputs as if called from within the browser. Calling via terminal will retrieve slightly different formatting.

Display

Output from hitting the endpoint with no additional parameters (http://localhost:33332/3forge_rest/stats), or with display=text (http://localhost:33332/3forge_rest/stats?display=text)

This will return a webpage with information similar to the following:

========= JAVA VIRTUAL MACHINE =========

        Start Time: 2025-05-01 09:10:18.094 GMT
                Now: 2025-05-01 10:22:14.547 GMT
            CPU Used: 8.12%
        Threads Used: 32.46%
Estimated Memory Used: 2.88%
        Used Memory: 111.57 MB
Used Memory After GC: 84.59 MB
        Max Memory: 3.78 GB
        Total Memory: 239.75 MB
Garbage Collections: 81
        Total Threads: 77
    Running Threads: 25

================= WEB ==================

            logins: 1
            sessions: 1

Output from hitting the endpoint with the additional display=json parameter (http://localhost:33332/3forge_rest/stats?display=json).

This will return a webpage similar to the following:

Navigate between the different headers. To view/download the raw JSON, select the "Raw Data" tab. Below is a sample of the Pretty Print formatted JSON output from this endpoint:

{
"vm": {
    "cpuPct": 0.06,
    "estMemUsedPct": 2.99,
    "now": 1746095166380,
    "startTime": 1746090618094,
    "threadPct": 31.16
},
"vmraw": {
    "gc": 83,
    "memMax": 4054319104,
    "memTot": 251396096,
    "memUsed": 121409304,
    "memUsedAfterGc": 88829656,
    "now": 1746095166380,
    "startTime": 1746090618094,
    "threadsRunnable": 24,
    "threadsTotal": 77
},
"web": {
    "logins": 1,
    "sessions": 1
}
}        

GET /3forge_rest/query

Use this endpoint to send Center commands via the REST API.

Query Commands

Param Description Example
cmd (required)
  • Command parameter
  • Takes a center command to be executed
  • localhost:33332/3forge_rest/query?cmd=SHOW+TABLES
    display
  • Response display type
  • Valid options are: text, json, jsonRows, jsonMaps, pipe
  • localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&display=jsonRows
    timeout
  • Time in milliseconds before the command should be considered timed out
  • localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&timeout=1000
    limit
  • Limit how many rows are returned by a query
  • localhost:33332/3forge_rest/query?cmd=SELECT+*+FROM+__TABLE&limit=2
    ds
  • Which datasource to use
  • localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&ds=AMI
    show_plan
  • Whether to show query plan
  • Valid options are: on or off
  • localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&show_plan=on
    string_template
  • Whether to interpret string commands encased in ${...}, or to interpret them as literals
  • Valid options are: on or off
  • localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&string_template=off

    Basic SHOW TABLES Center command:

    http://localhost:33332/3forge_rest/query?cmd=SHOW+TABLES
    

    With display params:

    http://localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&display=jsonRows
    

    Default:

    curl -u username:password -X GET "http://localhost:33332/3forge_rest/query?cmd=SHOW+TABLES
    

    With display params:

    curl -u username:password -X GET "http://localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&display=jsonRows" 
    

    Once authenticated, default:

    Invoke-WebRequest -Uri "http://localhost:33332/3forge_rest/query?cmd=SHOW+TABLES" -Method GET -Headers $headers
    

    With display params:

    Invoke-WebRequest -Uri "http://localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&display=jsonRows" -Method GET -Headers $headers
    

    Query Outputs

    These are example outputs as if called from within the browser. Calling via terminal will retrieve slightly different formatting.

    Display

    Output from hitting the endpoint with no additional parameters (http://localhost:33332/3forge_rest/query?cmd=SHOW+TABLES), or with display=text (http://localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&display=text)

    This will return a webpage with information similar to the following:

    +--------------------------------------------------------------------------------------------------------------+
    |                                                    TABLES                                                    |
    +-----------------+---------+---------------+-------------+-------------+---------+------+--------+------------+
    |TableName        |Broadcast|RefreshPeriodMs|PersistEngine|OnUndefColumn|DefinedBy|Scope |RowCount|ColumnsCount|
    |String           |Boolean  |Long           |String       |String       |String   |String|Long    |Integer     |
    +-----------------+---------+---------------+-------------+-------------+---------+------+--------+------------+
    |__CENTER         |true     |250            |TEXT         |REJECT       |SYSTEM   |PUBLIC|1       |4           |
    |__COLUMN         |true     |250            !null         |REJECT       |SYSTEM   |PUBLIC|155     |7           |
    |__COMMAND        |true     |250            !null         |REJECT       |AMI      |PUBLIC|0       |22          |
    |__CONNECTION     |true     |250            !null         |REJECT       |AMI      |PUBLIC|0       |11          |
    |__DATASOURCE     |true     |250            |TEXT         |REJECT       |AMI      |PUBLIC|2       |9           |
    |__DATASOURCE_TYPE|true     |250            !null         |REJECT       |AMI      |PUBLIC|18      |5           |
    |__DBO            |true     |250            !null         |REJECT       |SYSTEM   |PUBLIC|1       |6           |
    |__INDEX          |true     |250            !null         |REJECT       |SYSTEM   |PUBLIC|19      |8           |
    |__PLUGIN         |true     |250            !null         |REJECT       |SYSTEM   |PUBLIC|31      |4           |
    |__PROCEDURE      |true     |250            !null         |REJECT       |SYSTEM   |PUBLIC|15      |6           |
    |__PROPERTY       |true     |250            !null         |REJECT       |AMI      |PUBLIC|0       |2           |
    |__RELAY          |true     |250            !null         |REJECT       |SYSTEM   |PUBLIC|1       |7           |
    |__REPLICATION    |true     |250            |TEXT         |REJECT       |SYSTEM   |PUBLIC|1       |6           |
    |__RESOURCE       |true     |250            !null         |REJECT       |AMI      |PUBLIC|0       |6           |
    |__STATS          |true     |250            !null         |REJECT       |SYSTEM   |PUBLIC|1153    |11          |
    |__TABLE          |true     |250            !null         |REJECT       |SYSTEM   |PUBLIC|24      |7           |
    |__TIMER          |true     |250            !null         |REJECT       |SYSTEM   |PUBLIC|0       |9           |
    |__TRIGGER        |true     |250            !null         |REJECT       |SYSTEM   |PUBLIC|0       |7           |
    |accounts         |true     |100            !null         |REJECT       |USER     |PUBLIC|0       |4           |
    |alert            |true     |100            !null         |REJECT       |USER     |PUBLIC|0       |2           |
    |hdb_test         !null     !null           |HISTORICAL   !null         |USER     |PUBLIC|0       |2           |
    |sample_fix       |true     |100            !null         |ADD          |USER     |PUBLIC|0       |7           |
    |test_table       |true     |100            !null         |REJECT       |USER     |PUBLIC|0       |1           |
    |times            |true     |100            !null         |ADD          |USER     |PUBLIC|0       |2           |
    +-----------------+---------+---------------+-------------+-------------+---------+------+--------+------------+
    
    (RETURNED 24 ROWS, 1 TABLE, AFFECTED 0 ROWS, EXECUTED IN 0.72 MILLISECONDS)
    

    display=pipe

    Returns a pipe-delineated text output of the table information:

    TABLES|24|String|TableName|Boolean|Broadcast|Long|RefreshPeriodMs|String|PersistEngine|String|OnUndefColumn|String|DefinedBy|String|Scope|Long|RowCount|Integer|ColumnsCount
    __CENTER|true|250|TEXT|REJECT|SYSTEM|PUBLIC|1|4
    __COLUMN|true|250|null|REJECT|SYSTEM|PUBLIC|155|7
    __COMMAND|true|250|null|REJECT|AMI|PUBLIC|0|22
    __CONNECTION|true|250|null|REJECT|AMI|PUBLIC|0|11
    __DATASOURCE|true|250|TEXT|REJECT|AMI|PUBLIC|2|9
    __DATASOURCE_TYPE|true|250|null|REJECT|AMI|PUBLIC|18|5
    __DBO|true|250|null|REJECT|SYSTEM|PUBLIC|1|6
    __INDEX|true|250|null|REJECT|SYSTEM|PUBLIC|19|8
    __PLUGIN|true|250|null|REJECT|SYSTEM|PUBLIC|31|4
    __PROCEDURE|true|250|null|REJECT|SYSTEM|PUBLIC|15|6
    __PROPERTY|true|250|null|REJECT|AMI|PUBLIC|0|2
    __RELAY|true|250|null|REJECT|SYSTEM|PUBLIC|1|7
    __REPLICATION|true|250|TEXT|REJECT|SYSTEM|PUBLIC|1|6
    __RESOURCE|true|250|null|REJECT|AMI|PUBLIC|0|6
    __STATS|true|250|null|REJECT|SYSTEM|PUBLIC|1220|11
    __TABLE|true|250|null|REJECT|SYSTEM|PUBLIC|24|7
    __TIMER|true|250|null|REJECT|SYSTEM|PUBLIC|0|9
    __TRIGGER|true|250|null|REJECT|SYSTEM|PUBLIC|0|7
    accounts|true|100|null|REJECT|USER|PUBLIC|0|4
    alert|true|100|null|REJECT|USER|PUBLIC|0|2
    hdb_test|null|null|HISTORICAL|null|USER|PUBLIC|0|2
    sample_fix|true|100|null|ADD|USER|PUBLIC|0|7
    test_table|true|100|null|REJECT|USER|PUBLIC|0|1
    times|true|100|null|ADD|USER|PUBLIC|0|2
    

    Output from hitting the endpoint with the additional display=json parameters, of which there are several. Please refer to the table above for the different options.

    This will return a webpage similar to the following:

    Navigate between the different headers. To view/download the raw JSON, select the "Raw Data" tab.

    Listed below are samples of Pretty Print formatted JSON from the different display options:

    display=json

    {
    "durationNanos": 679200,
    "returnValue": null,
    "rowsAffected": 0,
    "status": "ok",
    "tables": [
        {
        "columns": [
            {
            "name": "TableName",
            "type": "String",
            "values": [
                "__CENTER",
                "__COLUMN",
                "__COMMAND",
                "__CONNECTION",
                "__DATASOURCE",
                "__DATASOURCE_TYPE",
                "__DBO",
                "__INDEX",
                "__PLUGIN",
                "__PROCEDURE",
                "__PROPERTY",
                "__RELAY",
                "__REPLICATION",
                "__RESOURCE",
                "__STATS",
                "__TABLE",
                "__TIMER",
                "__TRIGGER",
                "accounts",
                "alert",
                "hdb_test",
                "sample_fix",
                "test_table",
                "times"
            ]
            },
        ...
        }
    }
    

    display=jsonRows

    {
    "durationNanos": 521800,
    "returnValue": null,
    "rowsAffected": 0,
    "status": "ok",
    "tables": [
        {
        "columns": [
            {
            "name": "TableName",
            "type": "String"
            },
            {
            "name": "Broadcast",
            "type": "Boolean"
            },
            {
            "name": "RefreshPeriodMs",
            "type": "Long"
            },
            {
            "name": "PersistEngine",
            "type": "String"
            },
            {
            "name": "OnUndefColumn",
            "type": "String"
            },
            {
            "name": "DefinedBy",
            "type": "String"
            },
            {
            "name": "Scope",
            "type": "String"
            },
            {
            "name": "RowCount",
            "type": "Long"
            },
            {
            "name": "ColumnsCount",
            "type": "Integer"
            }
        ],
        "rows": [
            [
            "__CENTER",
            true,
            250,
            "TEXT",
            "REJECT",
            "SYSTEM",
            "PUBLIC",
            1,
            4
            ],
            [
            "__COLUMN",
            true,
            250,
            null,
            "REJECT",
            "SYSTEM",
            "PUBLIC",
            155,
            7
            ],
            [
            "__COMMAND",
            true,
            250,
            null,
            "REJECT",
            "AMI",
            "PUBLIC",
            0,
            22
            ],
    
            ...
        ]
        ...
        }
    }
    

    display=jsonMaps

    {
    "durationNanos": 802200,
    "returnValue": null,
    "rowsAffected": 0,
    "status": "ok",
    "tables": {
        "TABLES": [
        {
            "Broadcast": true,
            "ColumnsCount": 4,
            "DefinedBy": "SYSTEM",
            "OnUndefColumn": "REJECT",
            "PersistEngine": "TEXT",
            "RefreshPeriodMs": 250,
            "RowCount": 1,
            "Scope": "PUBLIC",
            "TableName": "__CENTER"
        },
        {
            "Broadcast": true,
            "ColumnsCount": 7,
            "DefinedBy": "SYSTEM",
            "OnUndefColumn": "REJECT",
            "PersistEngine": null,
            "RefreshPeriodMs": 250,
            "RowCount": 155,
            "Scope": "PUBLIC",
            "TableName": "__COLUMN"
        },
        {
            "Broadcast": true,
            "ColumnsCount": 22,
            "DefinedBy": "AMI",
            "OnUndefColumn": "REJECT",
            "PersistEngine": null,
            "RefreshPeriodMs": 250,
            "RowCount": 0,
            "Scope": "PUBLIC",
            "TableName": "__COMMAND"
        },
        {
            "Broadcast": true,
            "ColumnsCount": 11,
            "DefinedBy": "AMI",
            "OnUndefColumn": "REJECT",
            "PersistEngine": null,
            "RefreshPeriodMs": 250,
            "RowCount": 0,
            "Scope": "PUBLIC",
            "TableName": "__CONNECTION"
        },
        ...
        ]
    ...
        }
    }
    

    Custom Endpoints

    You can create your own custom endpoints using Java by implementing an instance of the AmiRestPlugin or AmiRestCenterPlugin interface. This is similar to how any custom plugin is implemented in 3forge.

    Setup

    To create a custom endpoint requires the following:

    1. Create a Java package for your new endpoint. Include autocode.jar and out.jar in the build classpath.

    2. Create a Java file that implements either the com.f1.ami.amicommon.rest.AmiRestPlugin or com.f1.ami.center.AmiRestCenterPlugin interface depending on the type of REST endpoint you require.

    3. Save the package as a .jar file to then be added to your 3forge installation's amione/lib directory.

    4. Add the new REST endpoint to your local.properties:

      ami.rest.plugin.classes=$${ami.rest.plugin.classes},fully_qualified_java_class_name
      

      Note

      $${ami.rest.plugin.classes} references the existing REST API endpoints. Do not remove it, and ensure to add any additional endpoints are added after in a comma-delimited list with no spaces.

    Common Endpoint: Basic Example

    This example demonstrates how to use the AmiRestPlugin interface to access a basic endpoint that displays "Hello World" for an admin user.

    To do so, follow the steps below.

    1. In an IDE of your choosing, create a Java package called test_endpoint and add the following to your local.properties:

      ami.rest.plugin.classes=$${ami.rest.plugin.classes},test_endpoint.AmiRestPlugin_HelloWorld
      
    2. Create and define the endpoint HelloWorld by implementing the AmiRestPlugin interface in your Java IDE:

      AmiRestPlugin_HelloWorld.java

      package test_endpoint;
      
      import com.f1.ami.amicommon.rest.AmiRestPlugin;
      import com.f1.ami.amicommon.rest.AmiRestRequest;
      import com.f1.ami.web.auth.AmiAuthUser;
      import com.f1.container.ContainerTools;
      import com.f1.utils.CH;
      import com.f1.utils.PropertyController;
      import com.f1.utils.casters.Caster_Boolean;
      
      public class AmiRestPlugin_HelloWorld implements AmiRestPlugin {
      
          private ContainerTools tools;
      
          @Override
          public String getPluginId() {
              // Give your plugin a unique ID
              return "REST_HelloWorld";
          }
      
          @Override
          public void init(ContainerTools tools, PropertyController props) {
              this.tools = tools;
      
          }
      
          @Override
          public String getEndpoint() {
              // The URL endpoint to hit
              return "HelloWorld";
          }
      
          @Override
          public void handler(AmiRestRequest rr, AmiAuthUser user) {
              // Implement the logic required. In this case, we just want our request to display "Hello World" if the user is an admin
      
              boolean b = CH.getOr(Caster_Boolean.INSTANCE, user.getAuthAttributes(), AmiAuthUser.PROPERTY_ISADMIN, Boolean.FALSE);
      
              if (!b) {
      
                  rr.error("permission denied - must be admin");
                  return;
              }
      
              rr.println("Hello World");
      
          }
      
          @Override
          public boolean requiresAuth() {
      
              // Whether authorization is needed for this endpoint
              return true;
          }
      
      }
      
    3. Save the project and export it as a .jar file into the amione/lib directory of the 3forge installation.

    4. Restart 3forge and go to the REST API page. By default, this will be hosted on the same port as the web:

      localhost:33332/3forge_rest
      

      You should now see your custom endpoint in the available list:

    5. Click on the endpoint, or paste the command directly into your browser. If you are logged in as an admin user, you will see the following:

      Otherwise:

    Center Endpoint: Example

    REST endpoints can be used to query information in the Center, such as specific tables. The existing endpoint /3forge_rest/query is an example of a Center endpoint.

    Implementing a custom Center endpoint requires the AmiRestCenterPlugin interface which allows a developer to query the Center within some custom Java code and display the output in a REST endpoint.

    This example demonstrates how to use the AmiRestCenterPlugin interface to create the endpoint order. This endpoint can query and pull row(s) from a Center table named "orders" with the following user-supplied query parameters:

    1. id

      • User supplied id parameter corresponding to some row(s) in the Center table "orders"
    2. multRows

      • Whether the supplied ID can be found in multiple rows or not. Default is true.

    To implement, follow the steps below.

    1. You will need a Center table called "orders" populated with some data. For the purposes of this example, we are using the following schema:

      CREATE PUBLIC TABLE orders (id int, symbol string, amount double);
      
    2. In an IDE of your choosing, create a Java package called center_endpoint and add the following to your local.properties:

      ami.rest.plugin.classes=$${ami.rest.plugin.classes},center_endpoint.AmiRestPlugin_Order
      
    3. Create and define the endpoint Order by implementing the AmiRestCenterPlugin interface in your Java IDE:

      AmiRestPlugin_Order.java

      package center_endpoint;
      
      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      import java.util.Objects;
      
      import com.f1.ami.amicommon.AmiUtils;
      import com.f1.ami.amicommon.msg.AmiCenterQueryDsRequest;
      import com.f1.ami.amicommon.msg.AmiCenterQueryDsResponse;
      import com.f1.ami.amicommon.msg.AmiCenterRequest;
      import com.f1.ami.amicommon.msg.AmiCenterResponse;
      import com.f1.ami.amicommon.rest.AmiRestRequest;
      import com.f1.ami.center.AmiCenterUtils;
      import com.f1.ami.center.AmiRestCenterPlugin;
      import com.f1.ami.web.auth.AmiAuthUser;
      import com.f1.base.Row;
      import com.f1.base.Table;
      import com.f1.container.ContainerTools;
      import com.f1.container.RequestOutputPort;
      import com.f1.container.ResultActionFuture;
      import com.f1.container.ResultMessage;
      import com.f1.utils.CH;
      import com.f1.utils.PropertyController;
      import com.f1.utils.SH;
      import com.f1.utils.string.ExpressionParserException;
      import com.f1.utils.structs.Tuple2;
      
      public class AmiRestPlugin_Order implements AmiRestCenterPlugin {
      
          private ContainerTools tools;
          private RequestOutputPort<AmiCenterRequest, AmiCenterResponse> port;
      
          @Override
          public String getPluginId() {
              // Give your plugin a unique ID
              return "Order";
          }
      
          @Override
          public void init(ContainerTools tools, PropertyController props) {
              this.tools = tools;
          }
      
          @Override
          public String getEndpoint() { 
              // The URL endpoint to hit
              return "order";
          }
      
          @SuppressWarnings("rawtypes")
          @Override
          public void handler(AmiRestRequest rr, AmiAuthUser user) { 
              /* Implements the logic of the endpoint. We want to achieve a few things:
                  - Send a specific query to the Center: Retrieve an order from "orders" with a user-supplied ID. 
                  - Our "orders" table can have multiple rows linked to an ID, allow for this to be passed.
                  - Handle errors like an incorrect ID parameter.
                  - Store the output values in a table and display them as a JSON.
              */
      
              // Create a HashMap to store error messages 
              HashMap<String, Object> err = new HashMap<>();
      
              // Create a request object to hold the Center query. This is the Center request, NOT the REST request 
              AmiCenterQueryDsRequest request = tools.nw(AmiCenterQueryDsRequest.class);
      
              // Check the user accessing the endpoint has permission to access the Center table 
              byte permissions = AmiCenterUtils.getPermissions(this.tools, user);
              request.setPermissions(permissions);
              request.setInvokedBy(user.getUserName());
      
              // Set the Center and table information for the query request
              request.setType(AmiCenterQueryDsRequest.TYPE_QUERY);
              request.setOriginType(AmiCenterQueryDsRequest.ORIGIN_REST);
              int timeout = 60000;
              int limit = 10000;
              String ds = "AMI";
              String queryidParam = rr.getParam("id"); // user-supplied in the REST request
      
              if (SH.isnt(queryidParam)) {
                  // No ID supplied error handling 
                  err.put("error", "id parameter required e.g. http://<your-ami-host>:<port>"
                          + rr.getInnerRequest().getRequestUri() + "?id=3");
                  rr.printJson(err);
                  return;
              } 
      
              /* The user-supplied REST endpoint query parameters: 
                  - "id": id of a row (can be multiple)
                  - "multRows": optional, whether the supplied id can correspond to multiple rows. If not supplied, default is "true"
              */
      
              // AmiScript command for querying Center table with a specified id
              String cmd = "select * from orders where id == " + queryidParam;
              // Set whether or not the id can correspond to multiple rows
              String MultipleRows = rr.getParam("multRows"); 
      
              // Center request handling
              request.setDatasourceName(ds); // Set datasource to AMI
              request.setQuery(cmd); // Center query
              request.setLimit(limit);
              request.setQuerySessionKeepAlive(true);
              request.setTimeoutMs(timeout);
              request.setIsTest(false);
              request.setAllowSqlInjection(false);
              request.setQuerySessionKeepAlive(false);
      
              ResultActionFuture<AmiCenterResponse> future = port.requestWithFuture(request, null);
              ResultMessage<AmiCenterResponse> result = future.getResult(timeout);
      
              if (result.getError() != null)
                  throw new RuntimeException(result.getError());
      
              AmiCenterQueryDsResponse action = (AmiCenterQueryDsResponse) result.getActionNoThrowable();
      
              // values hash map stores the Center response including errors etc
              Map values = new HashMap();
      
              if (!action.getOk()) {
                  Map<String, Object> error = new HashMap<String, Object>();
                  Exception exception = action.getException();
                  if (exception == null) {
                      error.put("message", "unknown error");
                  } else {
                      error.put("message", exception.getMessage());
                      if (exception instanceof ExpressionParserException) {
                          ExpressionParserException ee = (ExpressionParserException) exception;
                          Tuple2<Integer, Integer> pos = SH.getLinePosition(ee.getExpression(), ee.getPosition());
                          error.put("atLine", pos.getA());
                          error.put("atChar", pos.getB());
                      }
      
                  }
                  values.put("error", error);
              }
      
              int numberOfRows = 0;
      
              // If the query does not return any values
              if (CH.isEmpty(action.getTables())) {
                  err.put("error", "the table \'orders\' does not exist");
                  rr.printJson(err);
                  return;
              }
      
              // Store the query response in values
              List<Table> tables = action.getTables();
              Map l = new HashMap();
              for (Table t : tables) {
                  List<Map> rows = new ArrayList<Map>(t.getSize());
                  numberOfRows = t.getSize();
                  for (Row row : t.getRows())
                      rows.add(new HashMap(row));
                  l.put(t.getTitle(), rows);
              }
              values.put("tables", l);
      
              // Store the resulting table as a JSON
              Map<String, Object> tablesJson = (Map<String, Object>) values.get("tables");
              Object orders = tablesJson.get("orders");
      
              if ("{orders=[]}".equals(Objects.toString(values.get("tables")))) {
                  err.put("error", "The 'id' value does not exist in the table.");
                  rr.printJson(err);
                  return;
              }
      
              // multRows handling
              List<Map<String, Object>> ordersList = (List<Map<String, Object>>) tablesJson.get("orders");
              Map<String, Object> firstOrder = ordersList.get(0);
      
              if (!"false".equals(MultipleRows) && !"true".equals(MultipleRows) && MultipleRows != null) {
                  err.put("error",
                          "Invalid 'multRows' parameter. Acceptable values are 'multRows=true' or 'multRows=false' (default: 'multRows=true').");
                  rr.printJson(err);
                  return;
              }
              if ("false".equals(MultipleRows)) {
                  if (numberOfRows == 1) {
                      rr.printJson(firstOrder);
                      return;
                  }
                  err.put("error", "Multiple rows match the specified 'id'. Set 'multRows=true' if this is intentional.");
                  rr.printJson(err);
              }
              if ("true".equals(MultipleRows) || MultipleRows == null) {
                  rr.printJson(orders);
                  return;
              }
      
          }
      
          @Override
          public boolean requiresAuth() {
      
              // Whether authorization is needed. Note that authorization is always needed for Center endpoints
              return true;
          }
      
          public void setItineraryPort(RequestOutputPort<AmiCenterRequest, AmiCenterResponse> itineraryPort) { 
      
              // Allows the Center request to be sent. Do not change this
              this.port = itineraryPort;
              this.tools = port.getTools();
          }
      
      }
      

      Note

      Refer to the comments within the code snippet for details on the methods and implementation. If you require additional information, please reach out to us at support@3forge.com

    4. Save the project and export it as a .jar file into the amione/lib directory of the 3forge installation.

    5. Restart 3forge and insert some data into the table "orders." For example:

      INSERT INTO orders VALUES 
      (1, "COMP", 250.50),
      (2, "MSFT", 399.99),
      (3, "ORCL", 120.00),
      (4, "IBM", 89.95),
      (5, "TSLA", 560.75),
      (6, "3FG", 42.10),
      (7, "NVDA", 780.00),
      (8, "AMZN", 150.25),
      (9, "GOOG", 310.40),
      (10, "JPM", 99.99),
      (12, "AAPL", 150.00),
      (12, "AAPL", 175.50),
      (12, "AAPL", 199.99);
      
    6. Go to the REST API page. By default, this will be hosted on the same port as the web:

      localhost:33332/3forge_rest
      

      You should now see your custom endpoint in the available list:

    7. Click on the endpoint (you may be prompted to login). You will see the following error message:

      This is the error message for not passing in a query. Supply a query with an id to the endpoint, e.g: localhost:33332/3forge_rest/order?id=3

    8. This endpoint also takes the optional multRows param. Passing in localhost:33332/3forge_rest/order?id=12&multRows=true will display the following: