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.