Leap Data Context (LDC)

Overview

Leap Data Context, abbreviated as “LDC” is a key component in the Leap Framework. This data structure is integral to all parts of the Leap Lifecycle, from handling a simple HTTP request all the way to applying a custom Taxonomy, Entity, or Projection. In the same way that Apache Camel relies on the Exchange object, Leap relies on LDC.

When it comes to implementing more advanced functionality, Leap requires more information than what should be reasonably maintained in the Camel Exchange Body. Because Camel leverages a Filter-based architecture, each producer and consumer relies on the Exchange Body to inform the behavior of the route. Leap Data Context contains much more data than a plain Camel Exchange Body, in fact, most of the LDC contents are not necessary for regular Camel functions.

While processing a message the Leap framework stores all of the LDC data for the request (currently) in the Camel Exchange Headers instead of the body. This is beneficial as it allows Leap to layer on complex processing without interfering with regular Camel functions. This means that there is a new LDC object created for each request – Event and HTTP alike – that is handled by Leap. See how you can access LDC in this example.

What Does LDC Do?

At its core, LDC contains a Deque (LinkedList<LeapDataElement>) for maintaining any necessary state during the current route, an ArrayList (ArrayList<String>) of tag names that are used to lookup a LeapDataElement from the Deque as needed, and a bunch of other flags and fields that are configured based on the needs of the route. An example of some available fields are:

  • Projections Data
  • Leap Service Context
  • Vendor Taxonomy ID
  • Metadata
  • etc…

The combination of Deque and ArrayList allows us as Leap developers to stash any data we want by pushing it into LDC. We do this by creating a LeapDataElement and pushing it into the Deque while providing a tagName String such as #myNewData.

By default Leap automatically stores the initial request data into LDC by using the #leap_initial tag.

We may choose to store data into LDC that is the result of an external call, lookup, or post-processing step. In a regular Camel application, this would stay in the body, however, since we want this data “on-demand” at a later point in the route, we may choose to store this data in LDC.

It is common in multi-step implementation routes that the current body you can access does not represent the exact data of the initial request. If an implementation route calls an external service, the message body is set as the return data from the service call, this could be an issue if we didn’t have LDC as an optional transitory state manager.

Taxonomy, Projections and LeapDataContext

When Leap tries to apply a specific taxonomy to a set of data, it will check for the taxonomy_id within the LDC, which can exist within Metadata, InitialLeapDataContext, or vendoryTaxonomyID. When a request contains the taxonomy_id header, Leap will automatically cache that value within LDC and override the default taxonomy.

The same is true for Projections; when a projection is required for a set of data LDC is interrogated for a template that can be applied to the current data being supplied. If the data doesn’t already exist, it will load the projection template from a source file and maintain the data within LDC via projectionsArr temporarily while projection data is extracted from the source.

Both Taxonomy and Projections are applied within the applyTemplate() method of the LDC class. Based on the method parameters, any combination of the two are applied and the result is ultimately set back into LDC or set as the Exchange body.

LeapDataContext Fields

Due to the number of fields and methods available, see this link to the class definition in the current Leap Framework Github Repository. Be sure to visit the most recent master branch if this link becomes outdated.

Some of the fields that exist in the LDC class can also be derived from some of the dependent classes, however, they may be easier to access when maintained at the root level.

LDC JSON and Java Examples

LeapDataContext[
  dequeContextElement = [
	LeapDataContextElement[
	  tagName = request, 
	  dataElementObject = LeapDataElement[apiVersion = apiVersion, context = context, lang = lang, selfLink = null, id = null, error = null, data = LeapData[kind = request, metadata = [MetaData[type = Object, byteLenth = 7, effectiveColumnName = request, actualColumnName = request]], items = LeapJSONResultSet[type = JSON, data = {"request": {}}], taxonomyId = LeapDefault]]], LeapDataContextElement[tagName = #leap_initial, dataElementObject = LeapDataElement[apiVersion = apiVersion, context = context, lang = lang, selfLink = null, id = null, error = null, data = LeapData[kind = #context, metadata = [], items = LeapJSONResultSet[type = JSON, data = {}], taxonomyId = LeapDefault]]], LeapDataContextElement[tagName = #header, dataElementObject = InitialLeapDataContextElement[requestHeaderElement = {
	 serviceType = delete,
	implName = wherewerks,
	feature = forwarder,
	vendor = wherewerks,
	featureGroup = communicator,
	requestMethod = POST,
	tenantId = all,
	siteId = all,
	version = 1.0
}, ]], 
	LeapDataContextElement[tagName = #leap_private, dataElementObject = InitialLeapDataContextElement[privateHeaderElement = {accountId = ALL,	siteId = all
}, ]]], allElementTagsList = [#leap_private, #header, #leap_initial, request], 
  projectionsArr = null, 
  flattenedrojectionArr = null, 
  jsonSchema = null, 
  taxonomyJson = null, 
  vendorTaxonomyId = null, 
  configNodeId = null, 
  taxonomyRequired = true, 
  initialContextConfig = InitialLeapDataContextElement[], 
  taxonomyHelper = com.attunedlabs.featuremetainfo.taxonomy.FeatureTaxonomyHelper @47bf7138, 
  featureDeploymentService = com.attunedlabs.featuredeployment.impl.FeatureDeploymentService @2dfb579c, 
  featureDeployment = FeatureDeployment[id = 0, 
  featureMasterId = 0, featureName = null,
  implementationName = null, vendorName = null, 
  featureVersion = null, isActive = false, isPrimary = false, isCustomized = false, 
  provider = null, vendorTaxonomyId = null], parentKinds = [], contextMap = {}, 
  parentKindIdentifiers = [], 
  finalJsonStructured = [], mapToGetActualColumnName = {}, 
  mapToGetEffectiveColumnName = {}, cachedJsonSchema = {}, 
  mapOfAllUIDData = {}, mapOfParentUIDData = {}, childOfCacheMap = {}, 
  leapServiceContext = LeapServiceContext[tenant = all, site = all, endpointType = HTTP - JSON, 
										  featureGroup = communicator, featureName = forwarder,
										  requestUUID = QD7EeC9I, 
										  requestContext = RequestContext[tenantId = all, siteId = all, 
																		  featureGroup = communicator,
																		  featureName = forwarder, 
																		  implementationName = wherewerks, 
																		  vendor = wherewerks, version = 1.0, 
																		  requestId = QD7EeC9I, provider = null, 
																		  vendorTaxonomyId = null]], metaDataMap = {}, 
  displayMetaDataList = null]
{
	"normalizedContext": [],
	"allContextElementWithMappedTag": [],
	"allContextElement": [],
	"serviceDataContext": {
		"serviceHandlerFromServiceContext": {},
		"requestUUID": "BPyvN0L9",
		"serviceCompletionEventHolderFromServiceContext": {},
		"pipelineContextDataFromServiceContext": {},
		"resourceHolder": {},
		"leapServiceRuntimeContextList": [{
			"implementationName": "communicatorimpl",
			"serviceHandlers": {},
			"genricdata": {},
			"vendor": "attunedlabs",
			"permadata": {},
			"serviceRequestData": {},
			"pipeContextData": {},
			"version": "1.0",
			"subscriberData": {
				"subscriptionQuartzTrigger": false,
				"EventSubscriptionTracker": {
					"partition": "0",
					"eventData": "{\"EventParam\":{\"apiVersion\":\"apiVersion\",\"data\":{\"wh_id\":\"dfdf\",\"devCode\":\"sdf\",\"kind\":\"confirmCurrentLocation\",\"location\":\"CCF20843DO1\",\"userId\":\"admin\",\"vehicleType\":\"Fork Lift\"},\"context\":\"context\",\"lang\":\"lang\"},\"EventId\":\"validateCurrentLocation\",\"EventHeader\":{\"tenantId\":\"all\",\"siteId\":\"all\"}}",
					"offset": "otzmf1MW",
					"retryCount": 0,
					"isRetryable": false,
					"tenantId": "all",
					"siteId": "all",
					"topic": "requestQueue_all_all_queue",
					"subscriptionId": "elasticCommunicator-communicatorService-communicatorimpl-attunedlabs-1.0-RequestQueue_Subscriber"
				},
				"RetryStrategy": {
					"allStrategyConfigurationKeys": ["retryCount", "retryInterval", "retryIntervalMultiplier", "timeIntervalUnit", "maximumRetryInterval", "retryTopRecords", "retryQueueName", "retryConsumers"],
					"retryConfiguration": {
						"maximumRetryInterval": "30",
						"retryIntervalMultiplier": "1",
						"retryTopRecords": 5,
						"retryCount": "4",
						"timeIntervalUnit": "MINUTES",
						"retryQueueName": "retry_all_all",
						"retryConsumers": 1,
						"retryInterval": "1"
					}
				}
			},
			"policydata": {},
			"entityData": {},
			"integrationpipelineData": {}
		}, {
			"implementationName": "communicatorimpl",
			"serviceHandlers": {},
			"genricdata": {},
			"vendor": "attunedlabs",
			"permadata": {},
			"serviceRequestData": {},
			"pipeContextData": {},
			"serviceName": "addItem",
			"version": "1.0",
			"policydata": {},
			"entityData": {},
			"integrationpipelineData": {}
		}],
		"genericDataFromServiceContext": {},
		"featureGroup": "elasticCommunicator",
		"vendor": "attunedlabs",
		"tenant": "all",
		"entitesConfigFromServiceContext": {},
		"endpointType": "HTTP-JSON",
		"implementationName": "communicatorimpl",
		"featureName": "communicatorService",
		"serviceCompletionEventHolder": {},
		"currentLeapServiceRuntimeContext": {
			"implementationName": "communicatorimpl",
			"serviceHandlers": {},
			"genricdata": {},
			"vendor": "attunedlabs",
			"permadata": {},
			"serviceRequestData": {},
			"pipeContextData": {},
			"serviceName": "addItem",
			"version": "1.0",
			"policydata": {},
			"entityData": {},
			"integrationpipelineData": {}
		},
		"serviceRequestDataFromServiceContext": {},
		"version": "1.0",
		"eventHolderFromServiceContext": {},
		"policyFromServiceContext": {},
		"integrationPipelineFromServiceContext": {},
		"site": "all",
		"runningContextServiceName": "addItem",
		"eventHolder": {},
		"requestContext": {
			"valid": true,
			"implementationName": "communicatorimpl",
			"featureName": "communicatorService",
			"featureGroup": "elasticCommunicator",
			"vendor": "attunedlabs",
			"requestId": "BPyvN0L9",
			"tenantId": "all",
			"siteId": "all",
			"configurationContext": {
				"implementationName": "communicatorimpl",
				"featureName": "communicatorService",
				"featureGroup": "elasticCommunicator",
				"tenantId": "all",
				"siteId": "all",
				"vendorName": "attunedlabs",
				"version": "1.0"
			},
			"version": "1.0"
		},
		"resourceHolderFromServiceContext": {},
		"subscriberDataFromServiceContext": {
			"subscriptionQuartzTrigger": false,
			"EventSubscriptionTracker": {
				"partition": "0",
				"eventData": "{\"EventParam\":{\"apiVersion\":\"apiVersion\",\"data\":{\"wh_id\":\"dfdf\",\"devCode\":\"sdf\",\"kind\":\"confirmCurrentLocation\",\"location\":\"CCF20843DO1\",\"userId\":\"admin\",\"vehicleType\":\"Fork Lift\"},\"context\":\"context\",\"lang\":\"lang\"},\"EventId\":\"validateCurrentLocation\",\"EventHeader\":{\"tenantId\":\"all\",\"siteId\":\"all\"}}",
				"offset": "otzmf1MW",
				"retryCount": 0,
				"isRetryable": false,
				"tenantId": "all",
				"siteId": "all",
				"topic": "requestQueue_all_all_queue",
				"subscriptionId": "elasticCommunicator-communicatorService-communicatorimpl-attunedlabs-1.0-RequestQueue_Subscriber"
			},
			"RetryStrategy": {
				"allStrategyConfigurationKeys": ["retryCount", "retryInterval", "retryIntervalMultiplier", "timeIntervalUnit", "maximumRetryInterval", "retryTopRecords", "retryQueueName", "retryConsumers"],
				"retryConfiguration": {
					"maximumRetryInterval": "30",
					"retryIntervalMultiplier": "1",
					"retryTopRecords": 5,
					"retryCount": "4",
					"timeIntervalUnit": "MINUTES",
					"retryQueueName": "retry_all_all",
					"retryConsumers": 1,
					"retryInterval": "1"
				}
			}
		},
		"permastoreFromServiceContext": {},
		"onlyRuntimeServiceContext": false
	}
}
public static void myCustomBeanMethod(Exchange exchange) {
    LeapDataContext leapDataCtx = (LeapDataContext) exchange.getIn()
            .getHeader(LeapDataContextConstant.LEAP_DATA_CONTEXT);
    String tagName = leapDataCtx.getServiceDataContext().getCurrentLeapServiceRuntimeContext().serviceName;
    String kindOfData = "CustomBeanData";

    Object responseData = exchange.getIn().getBody();

    try {
        if (responseData instanceof JSONObject || responseData instanceof JSONArray) {
            // responseData = JSON
            // kindOfData = Kind field to use on Leap JSON Object that is generated
            // serviceName = using serviceName as the tag "#myServiceName"
            // null = no taxonomy_id was provided, could fetch from LDC if necessary
            leapDataCtx.addContextElement(responseData, kindOfData, serviceName, null);
        } else if(responseData instanceof String) {
            //Add as String Instead
            leapDataCtx.addContextElement(responseData.toString(), kindOfData, serviceName, null);
        } else {
            throw LeapDataContextInitialzerException("Cannot add data to LDC");
        }
    } catch (LeapDataContextInitialzerException e) {
        e.printStackTrace();
    }
}
public void myCustomProcessor(Exchange exchange) throws Exception {
    LeapDataContext leapDataContext = exchange.getIn().getHeader("leapDataContext", LeapDataContext.class);
    
    // Get as JSON
    JSONObject serviceResponse = leapDataContext.getContextElementForTag("#myServiceName")
            .getJSONObject("#leap_service_response").getJSONObject("data").getJSONObject("items")
            .get("element");
    
    //set jSon into camel body
    exchange.getIn().setBody(serviceResponse.toString());
}

So What?

Leap Data Context contains a huge amount of data and metadata that each route and bean/processor within the route can access. For this reason, it’s a very valuable class that enables many framework-level functions but empowers custom beans by exposing them to low-level contextual elements which allow developers to make highly informed decisions about data manipulation, caching, and feature comingling.

For more details about where LDC is used throughout the Leap Framework see documents related to:

  • Applying Taxonomy
  • Leap Entities
  • Applying Data Projections
  • Detecting other deployed Features
  • Leap Data Transformations
Updated on February 3, 2022

Was this article helpful?

Related Articles