Appboard/old/custom data adapter

Revision as of 15:16, 13 April 2011 by imported>Jay.barr (→‎Extending Base CachingRecordAdapter Class)

Environment

  • Java SDK (ver 1.6 or greater) - Recommend adding the SDK's bin directory to your PATH.
  • Eclipse (ver 3.6+ recommended) - Project works best with M2Eclipse plugin for Maven building, but it is not required.
  • AppBoard release with license (ver 2.0.5 for this walkthrough)

Exercise Objective and Setup

This exercise walks though the creation of a Data Adapter for AppBoard and its installation and testing. The AppBoard Java SDK includes a packaged version of the DemoDataAdapter the exercise describes. This can be loaded into AppBoard to walk through the exercise with a running adapter.

  1. Copy demo-adapter.jar into the <AppBoard-Home>/server/webapps/enportal/WEB-INF/lib/ directory.
  2. Edit <AppBoard-Home>/server/webapps/enportal/WEB-INF/config/appboard-custom.properties and add the following line:
    appboard.registry.DemoDataAdapter=com.demo.DemoDataAdapter
  3. Start Tomcat with <AppBoard-Home>/server/startup.bat

Verify Environment

Extract the contents of the AppBoard release into desired directory, referenced as <AB-Home> from here on. Run <AB-Home>/server/bin/startup.bat to start Tomcat, and then browse to http://localhost:8080/. You should see the following:

Da-login.png

Login with the default administrator credentials:

  • User: administrator
  • Password: administrator
  • Domain: System

Once you have logged in, click on the AppBoard tab and you will be taken to the Visual Builder's main screen:

Da-initial-ab.png

To verify the demo adapter was loaded, select Data Sources and then Add from Visual Builder. Enter demo.demoadapter for Name and select Third Party and Demo Adapter for adapter type.

Da-newsource.png

Configuring Java Project

Data Adapters are implemented in Java and require the edge-apb-<version>.jar to be compiled, as well as any other JARs containing additional classes used in the adapter (e.g. log4j for logging). The AppBoard Java SDK includes a pre-configured Eclipse project that builds using the default Eclipse Java compiler and can have Maven support from the M2Eclipse plugin enabled via the project's content menu (Maven > Enable Dependency Management).

Extending Base CachingRecordAdapter Class

AppBoard Data Adapters extend the abstract class com.edgetech.services.DataAdapter (Note: package will probably be renamed to com.edgetech.appboard in a future release). For ease of implementation, a base class com.edgetech.services.impl.CachingRecordAdapter is used for development of Data Adapters that do not have large memory footprints from high numbers of records (approximately 10,000 records is probably the cutoff where caching the full data set is too high a use of memory and network bandwidth).

Data Adapters must provide the following services, which will be described in detail in the subsequent sections:

  1. List the Settings that can modify the behavior of the adapter
  2. List the Entities served by the adapter
  3. List the Attributes for each Entity
  4. List the Associations between Entities
  5. Retrieve the data that comprise the Entities

List Settings for the adapter

The adapter must inform the AppBoard server of the settings that can modify the behavior of the adapter. This is done through the method: public List<SettingDef> getSettingDefs(). SettingDef stores information about the setting such as name, description, data type, and default value. The implementation for DemoDataAdapter follows: [java,N]

public List<SettingDef> getSettingDefs() { List<SettingDef> settings = new ArrayList<SettingDef>(10); SettingDef wavePeriod = new SettingDef(SETTING_WAVE_PERIOD, "Time in seconds for one period of sine wave.", AttributeType.NUMBER, "60"); wavePeriod.setRequired(true); settings.add(wavePeriod);

SettingDef waveAmplitude = new SettingDef(SETTING_WAVE_AMPLITUDE, "Amplitude of the sine wave.", AttributeType.NUMBER, "50"); waveAmplitude.setRequired(true); settings.add(waveAmplitude);

SettingDef waveOffset = new SettingDef(SETTING_WAVE_OFFSET, "Zero offset for sine wave.", AttributeType.NUMBER, "50"); waveOffset.setRequired(true); settings.add(waveOffset);

SettingDef dataDuration = new SettingDef(SETTING_DURATION, "Length of time to display data, in seconds.", AttributeType.NUMBER, "600"); waveOffset.setRequired(true); settings.add(dataDuration);

SettingDef dataInterval = new SettingDef(SETTING_DATA_INTERVAL, "Time between samples, in seconds.", AttributeType.NUMBER, "10"); dataInterval.setRequired(true); settings.add(dataInterval);

SettingDef assocationDataPoints = new SettingDef( SETTING_ASSOCIATED_DATA_POINTS, "Number of data points each side of value. Zero disables association.", AttributeType.NUMBER, "3"); assocationDataPoints.setRequired(true); settings.add(assocationDataPoints);

SettingDef cacheTimeout = new SettingDef( CachingRecordAdapter.CACHE_TIMEOUT, "Number of seconds to cache data..", AttributeType.NUMBER, "30"); cacheTimeout.setRequired(true); settings.add(cacheTimeout);

return settings; }

The settings can be seen in the Builder by continuing with the data source addition by selecting Add Data Source, which will display the Connect screen in the Data Source Wizard, shown below:

Da-settings.png

Note the settings defined in getSettingDefs() appear in order in the wizard.

List Entities for the adapter

[java,N] public Collection<Entity> getEntities() throws AdapterLoadException { List<Entity> entities = new ArrayList<Entity>();

Entity thisEntity = new Entity(ENTITY_SINE_WAVE, getDefinition().getNamespace()); entities.add(thisEntity); logger.info("Defining entity \"" + thisEntity.getEntityName() + "\" in namespace \"" + thisEntity.getNamespace() + "\"");

if (getSettingAsInt(SETTING_ASSOCIATED_DATA_POINTS, 0) != 0) { thisEntity = new Entity(ENTITY_ASSOCIATED_DATA, getDefinition() .getNamespace()); entities.add(thisEntity); logger.info("Defining entity \"" + thisEntity.getEntityName() + "\" in namespace \"" + thisEntity.getNamespace() + "\"");

}

return entities; }

Da-entities.png

List Attributes for the adapter

[java,N] public Collection<AttributeDef> findAttributeDefinitions(Entity entityDef) throws AdapterLoadException {

List<AttributeDef> attributes = new ArrayList<AttributeDef>(5); AttributeDef thisAttribute;

String entityName = entityDef.getEntityName(); logger.info("Getting attribues for " + entityName); if (ENTITY_SINE_WAVE.equals(entityName)) { thisAttribute = new AttributeDef(ATTR_KEY, true, "STRING"); thisAttribute.setIdentity(true); attributes.add(thisAttribute);

thisAttribute = new AttributeDef(ATTR_TIMESTAMP, false, "DATE"); attributes.add(thisAttribute);

thisAttribute = new AttributeDef(ATTR_VALUE, false, "NUMBER"); attributes.add(thisAttribute); } else if (ENTITY_ASSOCIATED_DATA.equals(entityName)) { thisAttribute = new AttributeDef(ATTR_TIMESTAMP_OFFSET, true, "STRING"); thisAttribute.setIdentity(true); attributes.add(thisAttribute);

thisAttribute = new AttributeDef(ATTR_WAVE_KEY, false, "STRING"); attributes.add(thisAttribute);

thisAttribute = new AttributeDef(ATTR_TIMESTAMP, false, "DATE"); attributes.add(thisAttribute);

thisAttribute = new AttributeDef(ATTR_OFFSET, false, "NUMBER"); attributes.add(thisAttribute);

thisAttribute = new AttributeDef(ATTR_DESCRIPTION, false, "STRING"); attributes.add(thisAttribute); } else { logger.equals("Entity not defined: " + entityName); throw new AdapterLoadException(); }

return attributes; }

Da-ents attrs.png

[java,N]

public boolean allowAttrTypeOverride() { return false; }

List Associations for the adapter

[java,N] public Collection<AssociationDef> findAssociationDefinitions( Entity entityDef, Collection<String> entityNames) throws AdapterLoadException {

List<AssociationDef> associations = new ArrayList<AssociationDef>();

if (entityDef.getEntityName().equals(ENTITY_SINE_WAVE)) { if (entityNames.contains(ENTITY_ASSOCIATED_DATA)) { AssociationDef thisAssociation = new AssociationDef(); thisAssociation.setName(ENTITY_ASSOCIATED_DATA);

thisAssociation.setKeyFrom(ATTR_KEY); thisAssociation.setLinkedNamespace(getDefinition().getNamespace() + Namespace.SEPARATOR + ENTITY_ASSOCIATED_DATA); thisAssociation.setKeyTo(ATTR_WAVE_KEY);

thisAssociation.setRelationshipAsOneToMany(true); associations.add(thisAssociation);

}

} else {

}

return associations; }

Da-associations.png

Retrieve data for the adapter

[java,N] protected Map<ObjectReference, GenericRecord> refreshRecords ( ServiceRequestFacade request) throws Exception {

String entityName = request.getEntity().getEntityName(); ServiceResponseFacade response = new ServiceResponseFacade(request); List<GenericRecord> recordList = new ArrayList<GenericRecord>();

int duration = getSettingAsInt(SETTING_DURATION, 600); int wavePeriod = getSettingAsInt(SETTING_WAVE_PERIOD, 60); int waveAmp = getSettingAsInt(SETTING_WAVE_AMPLITUDE, 50); int waveOffset = getSettingAsInt(SETTING_WAVE_OFFSET, 50); int dataInterval = getSettingAsInt(SETTING_DATA_INTERVAL, 10);

int assocOffset = getSettingAsInt(SETTING_ASSOCIATED_DATA_POINTS, 3);


long currentMs = System.currentTimeMillis(); long currentSec = currentMs/1000;

long startSec = ((currentSec - duration) / dataInterval) * dataInterval; // align to interval

boolean isWaveEntity = entityName.equals(ENTITY_SINE_WAVE); for (long timeSec = startSec; timeSec <= currentSec; timeSec += dataInterval) { double phase = ((double) (timeSec % wavePeriod)) / ((double) wavePeriod) * 2.0d * Math.PI; int value = (int) (Math.sin(phase) * waveAmp + waveOffset); Date timestamp = new Date(timeSec * 1000);

if (isWaveEntity) { Map<String,Object> attributes = new HashMap<String, Object>(); attributes.put(ATTR_KEY, Long.toHexString(timeSec)); attributes.put(ATTR_TIMESTAMP, timestamp); attributes.put(ATTR_VALUE, value); GenericRecord record = new GenericRecord(request.getEntity(), attributes); recordList.add(record); } else { for (int i = -assocOffset; i <= assocOffset; i++) { Map<String,Object> attributes = new HashMap<String, Object>(); attributes.put(ATTR_TIMESTAMP_OFFSET, Long.toString(timeSec) + ':' + Integer.toString(i)); attributes.put(ATTR_WAVE_KEY, Long.toHexString(timeSec)); attributes.put(ATTR_TIMESTAMP, timestamp); attributes.put(ATTR_OFFSET, i); attributes.put(ATTR_DESCRIPTION, "Offset " + i + " from value " + value + " is " + (value + i)); GenericRecord record = new GenericRecord(request.getEntity(), attributes); recordList.add(record); } } }

Map<ObjectReference, GenericRecord> records = null; if (recordList != null) { response.addGenericRecords(recordList); records = response.getGenericRecords(); } return records; }

Da-collections.png


Da-previewcollection.png