Connecting to MongoDB from Corticon Using Service Callouts

July 18, 2018 Digital Experience, Corticon

Learn how to create a working service callout that can read from and store to a MongoDB instance using Corticon's flexible datasource management API.

Progress Corticon 5.7 includes a unified datasource configuration file allowing users to configure connections to external sources of data. While this file is mostly used for connecting to external databases (either using ADC or EDC), it’s exposed in the service callout framework so users can connect Corticon Server to a customized datasource and handle the data appropriately even if the database isn’t natively supported. This guide explains how to connect to a MongoDB instance using a REST API.

Before starting, make sure you have all the prerequisite software:

  • Corticon Studio 5.7
  • Corticon Server 5.7 (for deployment)
  • MongoDB 3.6
  • MongoDB Compass (for setting up the database and catalog)
  • RESTHeart 3.2.2

Set Up MongoDB

Follow the instructions on the MongoDB website to install and start MongoDB. The website offers installation guides for all major desktop operating systems.

Open MongoDB Compass

The default connection settings should allow you to connect.

Create a Database and a Collection

Use “Corticon” as the name for both. (You don’t need to create a document; you’ll be doing that with the REST API).

Start RESTHeart

Step 1

Unzip the archive, open the containing directory and edit the etc/restheart.yml file with your favorite text editor. Make sure to configure the mongo-uri property to include the correct address and port of your MongoDB server. For example, if you’re running MongoDB locally on port 27017, change the following line from this:
mongo-uri: MongoDB://127.0.0.1

 

to this:

mongo-uri: MongoDB://127.0.0.1:27017

 

Step 2

Start RESTHeart, and you should see output like the following: 

09:59:43.461 [main] INFO  org.restheart.Bootstrapper - ANSI colored console: true                                  
09:59:43.481 [main] INFO  org.restheart.Bootstrapper - Starting RESTHeart instance default                         
09:59:43.483 [main] INFO  org.restheart.Bootstrapper - version 3.2.2                                               
09:59:43.486 [main] INFO  org.restheart.Bootstrapper - Logging to file C:\Users\bblais\AppData\Local\Temp\\restheart.
log with level INFO                                                                                                
09:59:43.486 [main] INFO  org.restheart.Bootstrapper - Logging to console with level INFO                          
09:59:44.229 [main] INFO  org.restheart.Bootstrapper - MongoDB connection pool initialized                         
09:59:44.230 [main] INFO  org.restheart.Bootstrapper - MongoDB version 3.6.2                                       
09:59:44.230 [main] WARN  org.restheart.Bootstrapper - ***** No Identity Manager specified. Authentication disabled.
09:59:44.231 [main] WARN  org.restheart.Bootstrapper - ***** No access manager specified. users can do anything.   
09:59:44.232 [main] INFO  org.restheart.Bootstrapper - Authentication Mechanism io.undertow.security.impl.BasicAuthen
ticationMechanism enabled                                                                                          
09:59:44.232 [main] INFO  org.restheart.Bootstrapper - Token based authentication enabled with token TTL 15 minutes
09:59:44.242 [main] INFO  org.restheart.Bootstrapper - HTTPS listener bound at 0.0.0.0:4443                        
09:59:44.243 [main] INFO  org.restheart.Bootstrapper - HTTP listener bound at 0.0.0.0:8080                         
09:59:44.244 [main] INFO  org.restheart.Bootstrapper - Local cache for db and collection properties enabled with TTL
1000 msecs                                                                                                         
09:59:44.245 [main] INFO  org.restheart.Bootstrapper - Local cache for schema stores not enabled                   
09:59:44.423 [main] INFO  org.restheart.Bootstrapper - URL / bound to MongoDB resource *                           
09:59:44.558 [main] INFO  org.restheart.Bootstrapper - Embedded static resources browser extracted in C:\Users\bblais
\AppData\Local\Temp\restheart-
  
6229838428094629763                                                                  
09:59:44.596 [main] INFO  org.restheart.Bootstrapper - URL /browser bound to static resources C:\Users\bblais\AppData
\Local\Temp\restheart-6229838428094629763. Access Manager: false                                                   
09:59:44.790 [main] INFO  org.restheart.Bootstrapper - RESTHeart started

Step 3

Test the endpoint using your favorite browser or HTTP client.

Now the REST API connected to MongoDB is up and running.

Create the Decision Service

Step 1

Open Corticon Studio and make a simple Vocabulary. You can follow this example of a mileage calculator. It will take in information about a car and calculate how many more miles it can drive before needing fuel.

 

 

Step 2

Create a Rulesheet to do the calculation.

 

Step 3

Make a Ruletest and add some sample data to ensure the Decision Service is working.

 

Step 4

Run the test and check your results.

 

Connect the Decision Service to Your MongoDB Instance Using a Service Callout

Step 1

Add an ADC configuration to your Vocabulary and choose any database type. (You won’t be using that for the service callout.)

 

Step 2

Replace the default URL with the path to the RESTHeart API and append the name of the document to the URL. In this example, the document name is “cars.”

 

Step 3

Create a service callout. As a shortcut, import the Corticon 5.7/Samples/Weather Callout file into your project; it already contains all the necessary libraries.

 

Step 4

After importing, delete the Java files and, optionally, rename the project.

 

Step 5

Write the code to create a class for your callout. Start by storing the data. (In this practice example, don’t worry too much about error handling.)

public static void storeData(ICcDataObjectManager dataObjectManager) throws Exception {
    //
    // Convert our DataObjects to JSON
    //
      
    //Since we are talking to a REST API convert the 'cars' to JSON
    Set<ICcDataObject> cars = dataObjectManager.getEntitiesByName("Car");
      
    //This will be the JSONObject we send
    JSONObject carsJson = new JSONObject();
      
    //And this will be the array we put all the 'cars' into
    JSONArray carsArray = new JSONArray();
      
    //Convert the data objects, each one will be a json object in the array
    //Each attribute will be a property
    for(ICcDataObject currentCar : cars) {
        JSONObject currentCarJSON = new JSONObject();
          
        //Get the attribute names
        Set<String> attributeNames = currentCar.getAttributeNames();
          
        //Loop through the attributes
        for(String currentAttributeName : attributeNames) {
            //Place the attribute value
            currentCarJSON.put(currentAttributeName, currentCar.getAttributeValue(currentAttributeName));
        }
          
        //Add the new car JSON object to the array
        carsArray.put(currentCarJSON);
    }
      
    //Place the array in the JSON object
    carsJson.put("Cars", (Object)carsArray);
      
    //
    // Connect to MongoDB
    //
      
    //Get the datasource Manager
    IDatasourceManager datasourceManager = dataObjectManager.getDatasourceManager();
      
    //Get our MongoDB datasource
    IDatasource mongoDBDatasource = datasourceManager.getDatasource("MongoDB");
      
    //Open an http connection to the database
    URL mongoDBConnectionURL = new URL(mongoDBDatasource.getDatasourceConnectionURL());
    HttpURLConnection mongoDBConnection = (HttpURLConnection) mongoDBConnectionURL.openConnection();
      
    //Since we are storing, we want to use the 'PUT' HTTP method
    mongoDBConnection.setRequestMethod("PUT");
      
    //Set the content type
    mongoDBConnection.setRequestProperty("Content-Type", "application/json");
      
    //Make sure we can write to the connection
    mongoDBConnection.setDoOutput(true);
      
    //We are going to write the data to the connection output stream
    BufferedWriter jsonContentWriter = null;
    try {
        jsonContentWriter = new BufferedWriter(new OutputStreamWriter(mongoDBConnection.getOutputStream()));
          
        //Convert our JSON to a string to send to the REST API
        jsonContentWriter.write(carsJson.toString());
          
        //Flush the stream
        jsonContentWriter.flush();
          
        //Open the connection
        mongoDBConnection.connect();
          
        //Print out a rule message on success or failure
        int mongoDBResponseCode = mongoDBConnection.getResponseCode();
          
        if(mongoDBResponseCode >= 200 && mongoDBResponseCode < 300) {
            dataObjectManager.postMessage(ICcDataObjectManager.INFO, "MongoDB PUT call SUCCEEDED with a status of " + Integer.toString(mongoDBResponseCode), null);
        } else {
 
            dataObjectManager.postMessage(ICcDataObjectManager.INFO, "MongoDB PUT call FAILED with a status of " + Integer.toString(mongoDBResponseCode), null);
        }
    } finally {
        //Close the stream and connection once we are done
        if(jsonContentWriter != null) {
            jsonContentWriter.close();
        }
        mongoDBConnection.disconnect();
    }
      
}


Export the JAR File and Test the New Service Callout

Step 1

To export the JAR file, right-click on the project, select Export in the menu and select Java/JAR File from the wizard options.

 

Step 2

Add the exported JAR to the project.

 

Step 3

Create a Ruleflow to test the new service callout.

 

Step 4

Make a new Testsheet in the Ruletest and select the Ruleflow as the test subject.

 

Step 5

Run the test.

If you successfully hit the REST API, you should see the new document populated with data.

Now, you have stored data in MongoDB.                                                                                            

 

Retrieve Data from MongoDB

Step 1

Go back to the service callout and update it to read the information from the REST API instead of storing it.

Step 2

Add the following method to your service callout.

public static void readData(ICcDataObjectManager dataObjectManager) throws Exception {
         
    //This will be the object that holds the JSON response from the rest API
    JSONObject mongoDBCarDocument = null;
         
    //
    // Connect to MongoDB
    //
         
    //Get the datasource Manager
    IDatasourceManager datasourceManager = dataObjectManager.getDatasourceManager();
         
    //Get our MongoDB datasource
    IDatasource mongoDBDatasource = datasourceManager.getDatasource("MongoDB");
         
    //Open an http connection to the database
    URL mongoDBConnectionURL = new URL(mongoDBDatasource.getDatasourceConnectionURL());
    HttpURLConnection mongoDBConnection = (HttpURLConnection) mongoDBConnectionURL.openConnection();
         
    //Since we are reading, we want to use the 'GET' HTTP method
    mongoDBConnection.setRequestMethod("GET");
         
    //Set the content type
    mongoDBConnection.setRequestProperty("Content-Type", "application/json");
         
    //Make the connection
    mongoDBConnection.connect();
         
    //Print out a rule message on success or failure
    int mongoDBResponseCode = mongoDBConnection.getResponseCode();
         
    if(mongoDBResponseCode >= 200 && mongoDBResponseCode < 300) {
        dataObjectManager.postMessage(ICcDataObjectManager.INFO, "MongoDB GET call SUCCEEDED with a status of " + Integer.toString(mongoDBResponseCode), null);
    } else {
        dataObjectManager.postMessage(ICcDataObjectManager.INFO, "MongoDB GET call FAILED with a status of " + Integer.toString(mongoDBResponseCode), null);
    }
         
    //Read response data
    BufferedReader jsonContentReader = null;
    try {
        jsonContentReader = new BufferedReader(new InputStreamReader(mongoDBConnection.getInputStream()));
             
        //Get all the data from the stream
        StringBuilder jsonResultStringBuilder = new StringBuilder();
             
        //Read each line of the input to the end
        String lineOfJson = null;
        while( (lineOfJson = jsonContentReader.readLine()) != null) {
            jsonResultStringBuilder.append(lineOfJson);
        }
             
        //Convert the entire message payload into a JSONObject
        mongoDBCarDocument = new JSONObject(jsonResultStringBuilder.toString());
             
    } finally {
        if(jsonContentReader != null) {
            jsonContentReader.close();
        }
        mongoDBConnection.disconnect();
    }
         
    //
    // Convert our JSON to DataObjects
    //
         
    //Get the 'cars' array out of the Document
    JSONArray carsJsonArray = mongoDBCarDocument.getJSONArray("Cars");
         
    //Create a data object based off of each car in the array.
    for(int carsIndex = 0; carsIndex < carsJsonArray.length(); carsIndex++) {
        //Get the JSONObject out of the array
        JSONObject currentCar = carsJsonArray.getJSONObject(carsIndex);
             
        //Create the ICcDataObject
        ICcDataObject carDataObject = dataObjectManager.createEntity("Car");
             
        Set<String> attributeNames = carDataObject.getAttributeNames();
             
        //Loop through the data object attributes and, if they are properties in the JSONObject, set them
        for(String currentAttributeName : attributeNames) {
            if(currentCar.has(currentAttributeName)) {
                     
                //Get the attribute's type
                String currentAttributeType = carDataObject.getAttributeDataType(currentAttributeName);
                     
                //Create a variable for storing the attribute value in
                Object currentAttributeValue = null;
                     
                //Long
                if(currentAttributeType.contains("Long")) {
                    currentAttributeValue = new Long(currentCar.getLong(currentAttributeName));
                }
                //BigDecimal
                else if(currentAttributeType.contains("BigDecimal")) {
                    currentAttributeValue = new BigDecimal(currentCar.getLong(currentAttributeName));
                }
                //Integer
                else if(currentAttributeType.contains("Integer")) {
                    currentAttributeValue = new Integer(currentCar.getInt(currentAttributeName));
                }
                //String
                else {
                    currentAttributeValue = currentCar.getString(currentAttributeName);
                }
                     
                carDataObject.setAttributeValue(currentAttributeName, currentAttributeValue);
            }
        }
    }
}

Step 3

Export the JAR file again and restart Corticon Studio.

Step 4

Create a new Ruleflow to handle retrieval from the database. Put the calculator Rulesheet in the Ruleflow to use the data retrieved from the database.

Step 5

Add a new Testsheet with the new Ruleflow as test subject to try out the new service. Run the test.

Step 6

If the retrieval succeeds, bring everything together by adding the first service callout, which saves information to the database, to the Ruleflow.

Step 7

Test again.

Step 8

Hit the API and see the updated MilesTillEmpty property.

That’s It!

With that, we're done with this example. If you’re feeling adventurous, you can also export the datasource configuration file and build a Decision Service for deploying to the server. Now, you know how to create a working service callout that can read from and store to a MongoDB instance using Corticon. And you can use this approach to create interesting and useful callouts with Corticon’s flexible datasource management API.

This is just one of many new improvements we’ve delivered with Corticon 5.7. Check out everything that’s new in this release, then contact us to schedule a demo and let us know what you think.

Ben Blais

Ben is a software engineer working on Corticon. Along with the other members of the team, Ben designs, implements, and maintains various aspects of the studio, server, and web console modules. In his discretionary time Ben likes to dabble with new web technologies, and his GitHub account can be found here.

Read next File Governance and Versioning in Progress Corticon