Appboard/old/advanced data adapter: Difference between revisions
imported>Jay.barr |
imported>Jay.barr |
||
Line 102: | Line 102: | ||
To verify the refactoring works, preview the <tt>Sine Wave</tt> Data Source. The preview should look similar to: | To verify the refactoring works, preview the <tt>Sine Wave</tt> Data Source. The preview should look similar to: | ||
[[File:ada-fetchRecords.png|center|503px]] | [[File:ada-fetchRecords.png|center|503px]] | ||
Note that the description field shows "Set in fetchRecords()". This will change after the next step. | |||
== Incomplete Records / findById() == | == Incomplete Records / findById() == |
Revision as of 21:59, 1 September 2011
Prerequisites
These examples use the same system configuration as the Creating An AppBoard Data Adapter examples. Review the required environment and verify it using the steps on that page before continuing.
Advanced Data Adapter Concepts
The previous examples demonstrate how to develop the necessary callback methods for the AppBoard system to interrogate an adapter for its Entities and related settings. It also demonstrated the use of fetchRecords() to retrieve all the content for a given Entity. While this approach is sufficient for many adapters, the advanced topics in this walkthrough will illustrate how to be more efficient in memory and network bandwidth usage through the use of other methods to retrieve the data.
These examples will demonstrate the following concepts:
- Partial record retrieval -- Instead of retrieving all the attributes for each record, the adapter can return only some of the attributes, and when needed, the client will request the remainder
- Custom query implementation -- Default query mechanism relies on the server having all records for an entity. If the adapter's data source permits filtered retrieval, the adapter can intercept incoming queries and handle them more efficiently.
- (Future) More discrete cache control
Refactoring fetchRecords()
To make the comparison between the previous fetchRecords() implementation and the new methods that will return the data records, the original adapter class was refactored. The new method is:
[java,N]
public void fetchRecords(ServiceRequestFacade request,
ServiceResponseFacade response) throws Exception
{
logger.debug("fetchRecords('"+request.getEntity().getNamespace().getFQNamespace()+"') starting");
List<GenericRecord> recordList = new ArrayList<GenericRecord>();
int duration = getSettingAsInt(SETTING_DURATION, 600);
int dataInterval = getSettingAsInt(SETTING_DATA_INTERVAL, 10);
long currentMs = System.currentTimeMillis();
long currentSec = currentMs/1000;
long startSec = ((currentSec - duration) / dataInterval) * dataInterval; // align to interval
for (long timeSec = startSec; timeSec <= currentSec; timeSec += dataInterval) {
EntityDef entity = request.getEntity();
fillSineRecords(recordList, timeSec, entity, true, "Set in fetchRecords()");
}
response.addGenericRecords(recordList);
logger.debug("fetchRecords('"+request.getEntity().getNamespace().getFQNamespace()+"') completed");
}
The main change is that the GenericRecords are no longer constructed in fetchRecords(), but instead in a method fillSineRecords(). That method, and those it calls, are:
[java,N]
private void fillSineRecords(List<GenericRecord> recordList, long timeSec, EntityDef entity, boolean sineComplete, String sineDescription)
{
int value = getSineValue(timeSec);
Date timestamp = new Date(timeSec * 1000);
boolean isWaveEntity = entity.getNamespace().getEntityName().equals(ENTITY_SINE_WAVE);
if (isWaveEntity) {
GenericRecord record = getSineRecord(timeSec, entity, value, timestamp, sineComplete, sineDescription);
recordList.add(record);
} else {
int assocOffset = getSettingAsInt(SETTING_ASSOCIATED_DATA_POINTS, 3);
for (int i = -assocOffset; i <= assocOffset; i++) {
GenericRecord record = new GenericRecord(entity);
record.setAttributeValue(ATTR_TIMESTAMP_OFFSET, Long.toString(timeSec) + ':' + Integer.toString(i));
record.setAttributeValue(ATTR_WAVE_KEY, timeSec);
record.setAttributeValue(ATTR_TIMESTAMP, timestamp);
record.setAttributeValue(ATTR_OFFSET, i);
record.setAttributeValue(ATTR_DESCRIPTION, "Offset " + i + " from value " + value + " is " + (value + i));
recordList.add(record);
}
}
}
private int getSineValue(long timeSec) {
int wavePeriod = getSettingAsInt(SETTING_WAVE_PERIOD, 60);
int waveAmp = getSettingAsInt(SETTING_WAVE_AMPLITUDE, 50);
int waveOffset = getSettingAsInt(SETTING_WAVE_OFFSET, 50);
double phase = ((double) (timeSec % wavePeriod)) / ((double) wavePeriod) * 2.0d * Math.PI;
int value = (int) (Math.sin(phase) * waveAmp + waveOffset);
return value;
}
private GenericRecord getSineRecord(long timeSec, EntityDef entity,
int value, Date timestamp, boolean setComplete, String description)
{
GenericRecord record = new GenericRecord(entity);
record.setAttributeValue(ATTR_KEY, timeSec);
record.setAttributeValue(ATTR_TIMESTAMP, timestamp);
record.setAttributeValue(ATTR_VALUE, value);
if (setComplete) {
record.setAttributeValue(ATTR_DESCRIPTION, description);
}
record.setComplete(setComplete);
return record;
}
Note the only new method exposed, which is GenericRecord.setComplete(boolean isComplete). This method marks the record as either complete or not, with the default being complete. If the record is complete, the client will not request any missing attribute values, which is the behavior seen so far. The next step will explore using incomplete records.
To verify the refactoring works, preview the Sine Wave Data Source. The preview should look similar to:
Note that the description field shows "Set in fetchRecords()". This will change after the next step.