Page tree

ESA Interface Reference

This topic describes the methods in the ESA Java interface in the com.supportwizard.sync.interfaces.esa.ExternalSystemAdapter class. This can also be found in the com/supportwizard/sync/interfaces/esa/ExternalSystemAdapter.java file.

The ESA interface is the same for other interfaces, so the class and method comments can still be used. 

checkDelayedCreate

Signature

ExternalRecord checkDelayedCreate(String structure, String token)

Description

Checks for a delayed record creation result.

If a record creation has been delayed by the ESA, the sync core will call this method to get the actual operation result

Rationale

For some systems, it might be very inefficient to create records one by one, in the order Sync Core calls ESA. In this case, record creations can be delayed to form a single, or a few bulk updates to the external system.

If such a delay is required, the create() method should schedule the creation and throw EsaRecordDelayedException exception. After Sync Core performs all create() calls, it will call the checkDelayedCreate() method to get the actual creation result.

The moment ESA does the actual External System update is not important for the Sync Core. Common strategies are to either wait until checkDelayedCreate() is called, accumulating all creates or to do some more intermediate updates, one for every N create() calls.

Parameters
  • structure - structure to check operation for
  • token - operation token, as supplied within EsaRecordDelayedException
Returns

Created record

Exceptions
  • EsaRecordException if the delayed operation fails for the record
  • EsaRecordDelayedException if the operation is to be delayed again, or not yet done. Sync will then re-call this method later.
Example - see ExternalSystemAdapterBase base class
public ExternalRecord
checkDelayedCreate(String structure, String token) throws RemoteException,
EsaException, EsaRecordException { 
throw new EsaException("Delaying is not supported by that
ESA"); 
} 

checkDelayedDelete

Signature

Date checkDelayedUpdate(String structure, String token)

Description

Checks for a delayed record deletion result.

If a record delete has been delayed by the ESA, the sync core will call this method to get the actual operation result

Rationale

Same as checkDelayedCreate(), but applied to delete().

Parameters
  • structure - structure to check operation for
  • token - operation token, as supplied within EsaRecordDelayedException
Returns

Nothing

Exceptions
  • EsaRecordException if delayed operation fails for the record
  • OptimisticLockFailureException if the record is modified after the lastSeen timestamp. This means that a record was modified while sync was in progress.
  • EsaRecordDelayedException if the operation is to be delayed again, or not yet done. Sync will then re-call this method later.
Example - see ExternalSystemAdapter base class
public void checkDelayedDelete(String structure, String token) throws
RemoteException, EsaException,
EsaRecordException { 
throw new EsaException("Delaying is not supported by that
ESA"); 
} 

checkDelayedUpdate

Signature

Date checkDelayedUpdate(String structure, String token)

Description

Checks for a delayed record update result.

If a record update has been delayed by the ESA, the sync core will call this method to get the actual operation result

Rationale

Same as checkDelayedCreate(), but applied to update().

Parameters
  • structure - structure to check operation for
  • token - operation token, as supplied within EsaRecordDelayedException
Returns

New/updated record timestamp

Exceptions
  • EsaRecordException if delayed operation fails for the record
  • OptimisticLockFailureException if record is modified after lastSeen timestamp - this means that a record was modified while sync was in progress.
  • EsaRecordDelayedException if the operation is to be delayed again, or not yet done. Sync will then re-call this method later.
Example - see ExternalSystemAdapterBase base class
public Date checkDelayedUpdate(String
structure, String token) throws EsaException,
EsaRecordException { 
throw new EsaException("Delaying is not supported by that
ESA"); 
} 

closeCursor

Signature

void closeCursor(String cursorID)

Description

Closes the cursor. This call indicates that Agiloftdoesn’t need the cursor anymore and guarantees that readDataPage will never be called for this cursor.

Rationale

Indicates that the sync subsystem is not going to use the cursor anymore and ESA may free all cursor-related resources.

Parameters

cursorID - cursor ID

Returns

Nothing

Exceptions

None

Example - see ExternalSystemAdapterBase base class
public void closeCursor(String cursorID) throws EsaException,
RemoteException { 
throw new EsaException("Not implemented"); 
}

Configure

SignatureString configure(String externalSystemID, boolean force)
Description

Indicates that the ESA is used by a Sync configuration. The method is called on the ESA when a new Sync configuration is created.

Simple ESAs may just ignore this call and return the passed externalSystemID.

If, for any reason, the ESA should not be used by the configuration, the ESA should return null unless the parameter 'force' is true. If force is true, ESA should adjust itself to be used by the configuration.
Rationale

A complex ESA may store additional per-configuration data or may require that only a single sync configuration exist per ESA installation.

The configure() method provides a means to control this by notifying the ESA that it is about to be used by a configuration with the given External System ID.

If the ESA returns null, the Agiloftadmin is asked to actively confirm that he wishes to use the ESA with the configuration. If he confirms, configure() is called again, with the force parameter set to true. ESA should treat this as a direct order from admin to be used with this External System ID from now on. The ESA may throw a ConfigurationInvalidException from the startSync() method if called with an old inactive External System ID.
Parameters
  • externalSystemID -  External System ID as set in the sync configuration.
  • force - indicates that admin insists, confirms a warning, configuring the ESA for this configuration.
ReturnsexternalSystemID or null if ESA can't be configured for this configuration.
ExceptionsNone
Example - see ExternalSystemAdapterBase base class
public String configure(String
externalSystemID, boolean force) throws EsaException, RemoteException { 
// Try new parameters 
startSync(externalSystemID); 
endSync(); 
return externalSystemID; 
}

countRange

Signature

int countRange(String externalStructure, String idMin, String idMax)

Description

Counts the number of tickets, with IDs in range of [IDmin;IDmax] (inclusive). This method is a callback for the HelperApi.detectDeleted(ExternalSystemAdapter, String, String, java.util.Date) method and is only called if HelperApi.detectDeleted is called by the ESA

Rationale

This method is only called if the ESA calls the HelperApi.detectDeleted() method.

detectDeleted() is based on binary division, checking the numbers of records, whose IDs fall in a range. Therefore, it needs to count the number of such external records through the ESA.

Parameters
  • structure - structure to check records in
  • idMin - minimal value of ID range
  • idMax - maximum value of ID range
Returns

Nothing

Exceptions

None

Example - see ExternalSystemAdapterBase base class
public int countRange(String
externalStructure, String idMin, String idMax) throws EsaException,
RemoteException { 
return 0; 
} 

Create

Signature

ExternalRecord create(String structure, ExternalRecord values)

Description

Creates a record in the external system

Rationale

Sync core calls this method to create a new external record, matching a new Agiloft record.

Parameters
  • structure - structure to create record in
  • values - field and relations of the record to create
Returns

Newly created record. In particular, the ID and Timestamp fields must be filled.

Exceptions
  • EsaRecordException if a record can’t be created,
  • EsaRecordDelayedException if ESA wants to delay the creation to form a bulk update.
Example - see SampleEsa class
public ExternalRecord create(String structure, ExternalRecord values) throwsRemoteException, EsaException, EsaRecordException { 
// Log debug info for troubleshooting 
log.debug("create (" + structure + ")"); 
TableServant servant = name2servant.get(structure); 
ExternalRecord result = servant.create(values); 
assert result != null; 
return result; 
}

Delete

Signature

void delete(String structure, Date lastSeen, String pk)

Description

Deletes a record in the external system

Rationale

Sync Core calls this method to propagate deletion of an Agiloftrecord to the external system.

Note: By default, deletions are not propagated at all. This can be changed within Sync Configuration editor, when editing table mapping in the Field Mapping wizard

Parameters
  • structure - structure to delete record in
  • lastSeen - modification time of the record, as seen by sync last time
  • pk - of the record to update
Returns

Nothing

Exceptions
  • EsaRecordException if a record cannot be deleted,
  • EsaRecordDelayedException if ESA wants to delay the delete to form a bulk update.
  • OptimisticLockFailureException if record is modified after lastSeen timestamp; this means that a record was modified while sync was in progress.
Example - see SampleEsa class
public void delete(String structure, Date
lastSeen, String pk) throws RemoteException, EsaException, EsaRecordException,
OptimisticLockFailureException { 
// Log debug info for troubleshooting 
log.debug("delete (" +
structure + ", " + pk + ")"); 
TableServant servant = name2servant.get(structure); 
servant.delete(lastSeen, pk); 
}

endSync

Signaturevoid endSync()
Description

Finishes the synchronization session. This call denotes the successful end of a synchronization.

ESA may clean up any resources, release connections and so on needed to perform a synchronization.

See also Release.
Rationale

Notify ESA that a synchronization is finished.

ESA should release “per-synchronization” resources, typically allocated in startSync() by closing connections, freeing internal data structures, and so on.
ParametersNone
ReturnsNothing
ExceptionsEsaException if the ESA fails to free resources.
Example - see SampleEsa class
public void endSync() throws EsaException,
RemoteException { 
// This ends sync 
// Do nothing. Real ESA might disconnect / free resources / etc 
// Log debug info for troubleshooting 
log.debug("Sync is ended."); 
}  

getAllowedRunModes

Signatureint getAllowedRunModes()
Description

Returns Bit-OR'ed constants from RunModes, restricting synchronization run modes.

For example, RunModes.RUN_SYNC_ACTIONS bit-OR RunModes.RUN_EXTERNAL_TRIGGERED would designate that ESA supports non-interactive synchronizations by both Agiloft and External System requests.
Rationale

An ESA can be run in three ways:

  • Manual - this is when synchronization is manually triggered through the Agiloft GUI - the Sync menu item in the table toolbar. The synchronization itself is always run in the background, but some ESAs such as Google Contacts and Calendar ESA, require user interaction and can only be run in this mode.
  • By Agiloft actions - Agiloft actions can be executed either by a rule or workflow which are configurable for the admin.
    For example, it is possible to set up a rule, running a sync action - an action type which runs the synchronization with a given External System ID - every time a record is modified. This allows for 'online' synchronizations. Another possibility is to set up a time-based rule to do batch synchronization.
  • External Triggered - a synchronization can be triggered by calling the HelperApi.startSync() method from outside Agiloft.
    For some ESAs, it is impossible to initiate synchronization in any way other than by a request from the External System. In this case, it is advisable to implement the ESA as an HTTPS ESA, listen for an External System request in the system-specific manner and call HelperApi.StartSync() when it is time to run sync. In this case HttpXmlEsaTransport should be used as a transport in HelperApi. See also HelperApi Interface.

This method tells The sync core which types of operations are suitable for the ESA. Most ESAs are agnostic to the way synchronization is run and support all methods - RunModes.ANY.

ParametersNone
ReturnsBit-OR'ed mask, composed from constants in RunModes class.
ExceptionsNone
Example - see ExternalSystemAdapterBase base class
public int getAllowedRunModes() throws
EsaException, RemoteException { 
return RunModes.ANY; 
}

getCollections

SignatureSet<ExternalCollection>getCollections(String structureOrCollection, Locale locale)
DescriptionGets a list of collections from the given structure.
Rationale
Parameters
  • structureOrCollection - a collection to return fields for
  • locale - locale to use for message localization
ReturnsCollections list
ExceptionsNone
Example - see SampleEsaclass
public Set<ExternalCollection>
getCollections(String structureOrCollection, Locale locale) throws
EsaException, RemoteException { 
// Collections are used to represent hierarchical data and are something
// in between of relations and fields. It is a bit advanced concept. 
// It is like relation, because it stores other records and is mapped to some EW table. 
// It is like field, because it is updated at once, as a field 
// This ESA doesn't use them. 
Set<ExternalCollection> result = new
HashSet<ExternalCollection>(); 
assert result != null; 
return result; 
}

getCurrentUTCTime

SignatureDate getCurrentUTCTime()
DescriptionGets current UTC time of a remote system, if available. If it is impossible to determine current time on the External System machine, this method should return null.
Rationale

Sync subsystem uses this method to compute the time shift between the Agiloft server where the sync subsystem is running and the External System. This is taken into account when checking if the record is modified or not.

The shift is applied for all date-time "control" values passed to ESA – that is, timestamps in getModified() / getDeleted(), record.modifiedAt, lastSeen parameter and so on.

The shift is not applied for converting record date/date-time/time fields inside ExternalRecord if the record data values are not affected.

ParametersNone
ReturnsCurrent UTC time of a remote system, if available, or null.
ExceptionsNone
Example - see ExternalSystemAdapterBase base class
public Date getCurrentUTCTime() throws
EsaException, RemoteException { 
return null;  // No time
synchronization 
}

getDeleted

Signature

Set<String>getDeleted(String structure, Date since)

Description

Gets IDs of records deleted in the external system after a given timestamp.

ESA may or may not pay attention to this timestamp. It is acceptable for the ESA to respond to this call with all of the IDs ever deleted in the system - sync will handle this properly, but this will result in more traffic between the ESA and Agiloft.

The timestamp corresponds to the time of the last successful invocation of getDeleted().

If an ESA accumulates deletion information, it may always return all accumulated IDs and purge them after successful endSync().

In any case, this call may result in a large amount of data to be transferred. If the ESA predicts such traffic, it should return NULL from this method (NOT an empty set!). In this case, Agiloftwill make use of the getDeletedPaged method.

The ESA may decide how to transfer data at runtime depending on the amount.

In any case, Agiloft will call getDeleted first and only call getDeletedPaged if getDeleted returns NULL <nullValue>true</nullValue>

Rationale

This method provides the sync subsystem with a list of deletions that happened in the External System since the last sync.

Parameters
  • structure - structure to read
  • since - return records deleted since the timestamp
Returns

IDs of records modified after the given timestamp

Exceptions

None

Example - see SampleEsa class
public Set<String> getDeleted(String
structure, Date since) throws EsaException, RemoteException { 
// Log debug info for troubleshooting 
log.debug("getDeleted (" + structure + ", " + since + ")"); 
TableServant servant = name2servant.get(structure); 
Set<String> result = servant.getDeleted(since); 
assert result != null; 
return result; 
} 

getDeletedPaged

Signature

Cursor getDeletedPaged(String structure, Date after)

Description

Gets deleted records in a paged manner. See getDeleted description.

This method is only called if the getDeleted call returned NULL (<nullValue>true</nullValue>).

Rationale

getDeleted() may return a large amount of data. For an HTTPs ESA, this may cause problems because of network timeouts and proxy server settings. In this case, it is advised to use the getDeletedPaged() method.

Parameters
  • structure - structure to read
  • since - timestamp
Returns

Cursor object, used as a token in readDataPage() method

Exceptions

None

Example - see ExternalSystemAdapterBase base class
public Cursor getDeletedPaged(String
structure, Date since) throws EsaException, RemoteException { 
throw new EsaException("Not implemented"); 
}

getDetailedReport

Signature

String getDetailedReport ()

Description

A detailed, debug level, progress report or null, if ESA does not provide this feature

Rationale

Provide a debug-level progress report.

This report is accessible when clicking 'To view raw log file, click here' in the synchronization progress window.

Parameters

None

Returns

Report text - plain text or HTML markup - or null

Exceptions

None

Example - see ExternalSystemAdapter base class
public String getDetailedReport() throws
RemoteException, EsaException { 
return null; 
} 

getFieldList

SignatureSet<ExternalField>getFieldList(String structureOrCollection, Locale locale)
Description

Gets a list of fields for a given structure or collection.

It is not necessary, but it is allowed, to include ID and timestamp “Modified At” fields here.
Rationale

The sync subsystem maintains the mapping between the fields of an AL table and the fields of the mapped external structure. To edit the mapping, select Setup > Sync Configuration, visit the Mapping tab and map a table to a structure, or click the Edit button for already mapped pairs.

The list of External Fields in the GUI is the set returned by this ESA method.
Parameters
  • structureOrCollection - a collection to return fields
  • locale - locale to use for message localization
ReturnsList of external fields of a structure
ExceptionsNone
Example - see SampleEsaclass
public Set<ExternalField>
getFieldList(String structureOrCollection, Locale locale) throws EsaException, RemoteException {ResourceBundle i18n = getResources("com.supportwizard.sync.sampleesa.sampleesa", locale);
Set<ExternalField> result = new HashSet<ExternalField>(); 
// Switch on the logical structure name (ones, that were returned by getStructureList())
if(CONTACTS.equals(structureOrCollection)) { 
// Return fields for contacts. 
// ID field. result.add(new ExternalField( 
CONTACT_ID,                     
// Logical name. This one is used in ExternalRecord, passed to create()/update() 
i18n.getString("contacts.id"),  
// Localized name, shown to EW administrator when setting up field mappings 
XsdType.INT,                    
// Type. This one is a numeric ID false,                          
// Not used for unknown records identification.
// If IDs are somewhat persistent among two systems (i.e. John Bull should have ID #1 in both systems, it should be identifying field 
// If IDs are allowed to differ, they usually will and we are better 
// to match records on something more meaningful, such as full name true,                           
// Required false, true,                    
// Not updatable except on create 
20));                           
// No longer than 20 digits 
// Note we have not specified
that the field is a primary key. PK value is
supplied separately with each record 
// and is not a must o
expose the ID field at all. 
// You may well do not, if you
don't want to map it and use its value somewhere
in EW 
// Simpler full name and email
fields 
result.add(new
ExternalField(CONTACT_FULLNAME, 
i18n.getString("contacts.fullname"),
XsdType.STRING, 
true, true, true, true,
250)); 
result.add(new
ExternalField(CONTACT_EMAIL, i18n.getString("contacts.email"), 
XsdType.STRING,
true, false, true, true,
250)); 
} else if(CASES.equals(structureOrCollection)) { 
// Fields for cases table 
result.add(new
ExternalField(CASE_ID, i18n.getString("cases.id"), XsdType.INT, 
false, true, false, true,
20)); 
result.add(new
ExternalField(CASE_SUMMARY, i18n.getString("cases.summary"), 
XsdType.STRING,
true, true, true, true,
250)); 
} 
// Always return something non-null. 
assert result != null; 
return result; 
}

getModified

SignatureSet<ExternalRecord>getModified(String structure, Date after)
Description

Gets all records modified in the external system after the given timestamp. For some systems, this may result in a very large amount of data to be transferred, especially for the initial sync. If the ESA predicts such high traffic, it should return null from this method - not an empty set.

In this case, Agiloft will make use of the getModifiedPaged method. An ESA may decide how to transfer data at runtime depending on the amount.

In any case, Agiloftwill call getModified first and only call getModifiedPaged if getModified returns null - <nullValue>true</nullValue>.
Rationale

To do the synchronization, the Sync subsystem must know which external records have been modified since the last sync and read their content in order to update Agiloft records.

Basically this method is equivalent to:

select * from structure where structure.modified >= since;

With the special case where “since” is null, which should return all records.

Parameters
  • structure - structure to read
  • after – timestamp, or null if all records are to be returned
ReturnsRecords modified after given timestamp
ExceptionsNone
Example - see SampleEsaclass
public Set<ExternalRecord>
getModified(final String structure, final Date after) throws EsaException,
RemoteException { 
// Log debug info for troubleshooting 
log.debug("getModified (" + structure + ", " + after + ")"); 
// Though it is not a must, having a "servant" class per table is usually convenient.
// It is a pure implementation detail, but it is often convenient to organize ESA in that way, 
// avoiding lengthy IFs switches on structure name in in 
getModified/getDeleted/create/update/delete methods 
// Note: if meta-data is not hardcoded, it is usually a good idea to place 
// meta-data reading (getFields(), getRelations(), getCollections()) to "servants" as well. 
TableServant servant = name2servant.get(structure); 
Set<ExternalRecord> result = servant.getModified(after); 
assert result != null; 
return result; 
}

getModifiedPaged

SignatureCursor getModifiedPaged(String structure, Date after)
Description

Gets modified records in a paged manner. See the getModified description.

This method is only called if the getModified call returned NULL - <nullValue>true</nullValue>.
RationalegetModified() may return a large amount of data, especially for initial synchronization, when all records are to be read. For an HTTPs ESA, this may cause problems because of network timeouts and proxy server settings. In this case, it is advised to use getModifiedPaged methods.
Parameters
  • structure - structure to read
  • after – timestamp, or null if all records are to be returned
Returns

Cursor object, used as a token in readDataPage() method

Exceptions

None

Example - see ExternalSystemAdapterBase base class
public Cursor getModifiedPaged(String
structure, Date after) throws EsaException, RemoteException { 
throw new EsaException("Not implemented"); 
}

getParametersMeta

SignatureList<EsaParameterMeta>getParametersMeta(Locale locale)
DescriptionGets localized ESA parameter metadata.
Rationale

Agiloft maintains ESA parameter values through the Sync Configuration wizard. To generate the GUI, Agiloft must know the parameter name, type, etc. This method provides that information.

The Sync Configuration wizard ESA Parameters tab is constructed based on the information returned by that method.
Parameters

Locale - locale to use for message localization

ReturnsGets localized ESA Parameters metadata.
ExceptionsNone
Example - see SampleEsaclass
public List<EsaParameterMeta>
getParametersMeta(Locale locale) throws EsaException, RemoteException { 
List<EsaParameterMeta> result = new ArrayList<EsaParameterMeta>();
// Screen names can be localized, see resource properties bundle. 
// A simpler ESA may just ignore "locale" parameter and return hard-coded names 
ResourceBundle i18n = getResources("com.supportwizard.sync.sampleesa.sampleesa",locale);
// There is a single parameter we need - where to store our files 
result.add(new EsaParameterMeta(WORK_DIR,                               
// Logical name, used to obtain parameter value 
EsaParameterValueType.TEXT,            
// String 
EsaParameterType.SINGLE,               
// Just one plain value 
true,                                   
// Must be set 
i18n.getString("workdir.label"),        
// Screen name, short 
i18n.getString("workdir.hint"),         
// Screen hint, explanatory and long 
new
EsaParameter("/tmp/sample-esa-data")// Default value 
)); 
// Always return something non-null. 
assert result != null; 
return 
}

getProgressReport

Signature

String getProgressReport ()

Description

HTML progress report, to the current moment or null, if ESA does not provide this feature. The report should be provided in fully rendered HTML, starting with an <html>and ending with </html>

Rationale

Provides a nicely-formatted ESA-specific progress screen.

If an ESA provides this report, that is, returns non-null from this method, the report HTML text completely replaces the standard progress screen.  The ESA should provide the full-featured progress GUI, including automatic page refresh and other GUI elements such as a Close button if necessary.

Please see ProgressUIHelper utility class for organizing self-refreshing page. Please also see Example 2 below.

ESA may also use this mechanism to interact with the user, providing a fully interactive GUI. In this case, you should return RunModes.MANUAL from the getAllowedRunModes() method.

Parameters

None

Returns

Report HTML text - <html> to </html> - or null

Exceptions

None

Example 1 - ExternalSystemAdapter base class
public String getProgressReport() throws
RemoteException, EsaException { 
return null; 
} 
Example 2
private ProgressUIHelper progressHelper =
new ProgressUIHelper(3000); // refresh every 3 sec
...
public String startSync() {
...
progressHelper.setRefreshing(true); // Enable progress page refreshes
}
public void endSync() {
...
progressHelper.setRefreshing(false); // All done - stop progress page
refreshes
}
public String getProgressReport() throws
RemoteException, EsaException { 
return progressHelper.htmlProgressReport("This is a <b>custom</b> Progress Report!"); 
} 

getRelations

SignatureSet<ExternalRelation>getRelations(String structureOrCollection, Locale locale)
Description

Gets a list of relations from the given structure.

This should only include relations that are navigable from this object: relations -from- this structure to others.
Rationale

Sync subsystem maintains a mapping of links between Agiloft tables - Linked Field Sets - and relations of the mapped external structures. For example, a table of Widgets might include a Purchased By field, which links to the prospect who purchased that Widget. To see the mapping, edit Sync Configuration > Relations tab.

The list of External Relations in the GUI is the set returned by this ESA method.
Parameters
  • structureOrCollection - a collection to return fields
  • locale - Locale to use for message localization
ReturnsA list of relations from the structure
ExceptionsNone
Example - see SampleEsaclass
public Set<ExternalRelation>
getRelations(String structureOrCollection, Locale locale) 
throws EsaException, RemoteException 
{ResourceBundle i18n = getResources("com.supportwizard.sync.sampleesa.sampleesa", 
locale);
Set<ExternalRelation> result = new HashSet<ExternalRelation>(); 
// There is a single relation - a contact who submits a case 
if(CONTACTS.equals(structureOrCollection)) 
{result.add(new ExternalRelation( 
CASE_CUSTOMER,                       
// Logical name 
CONTACTS,                                  
// Logical name of a linked table 
i18n.getString("cases. customer"),    
// Screen name 
false, 

// Doesn't hold multiple values 
false, 

// Not required, can be empty 
true));                                 
// Allows postponed updates. 
// This option is used to resolve cycles in dependencies 
// A rule of the thumb is to allow postponed updates on 
// all non-required relations 
} 
assert result != null; 
return result; 
}

getStructureList

SignatureSet<ExternalStructure>getStructureList(Locale locale)
Description

Gets a list of external data structure names.

A 'structure' is a logical data unit, mappable to an Agiloft table. It may be a table, a folder, a class in OODB, or any other type of a data grouping.
Rationale

The sync subsystem maintains the mapping between AL tables and external structures. You may edit this mapping in the Sync Configuration wizard, mappings tab.

The list of External Structures in the GUI is the set returned by this ESA method.
ParametersLocale - locale to use for message localization
ReturnsThe list of external system structures
ExceptionsNone
Example - see SampleEsaclass
public Set<ExternalStructure>
getStructureList(Locale locale) throws EsaException, 
RemoteException
{ 
ResourceBundle i18n =
getResources("com.supportwizard.sync.sampleesa.sampleesa", 
locale);
// CONTACTS, CASES are internal "logical" names. They are used
in all other ESA calls. 
Set<ExternalStructure> structures = new
HashSet<ExternalStructure>(); 
structures.add(new ExternalStructure(CONTACTS, 
i18n.getString("contacts.screenname")));   
structures.add(new ExternalStructure(CASES,
i18n.getString("cases.screenname"))); 
return structures; 
}

leaseCursor

Signature

void leaseCursor(String cursorID)

Description

Resets cursor expiration timer, if ESA maintains a timer.

Rationale

Allows sophisticated timeout-based resource management inside ESA

Parameters

cursorID - cursor ID

Returns

Nothing

Exceptions

None

Example - see ExternalSystemAdapterBase base class
public void leaseCursor(String cursorID)
throws EsaException, RemoteException { 
throw new EsaException("Not implemented"); 
}

leaseSession

Signaturevoid leaseSession()
Description

Resets the session timeout counter if the ESA has one. If the ESA does use a timeout-based resource de-allocation, it may use this method as an indication that the ESA is still used.

Otherwise, the ESA may simply ignore this call.
RationaleAllows sophisticated resource management within the ESA.
ParametersNone
ReturnsNothing
ExceptionsEsaException if the ESA has already expired due to internal timeout.
Example - see ExternalSystemAdapterBase base class
public void leaseSession()
throws EsaException,
RemoteException { 
// NO-OP
} 

needSyncAgain

Signatureboolean needSyncAgain()
Description

Checks whether another synchronization cycle should be run immediately.

The method is invoked after endSync()
Rationale

Sometimes it might be necessary to re-run synchronization again after it just finished. For example, a record update may trigger cascade-updates of other records, possibly after these other records were synchronized. To propagate those changes back to Agiloft, it is necessary to run synchronization once more.

In such cases, ESA should return true from this method. 
ParametersNone
ReturnsTrue if ESA wants another synchronization round to be run immediately.
ExceptionsNone
Example - see ExternalSystemAdapterBase base class
public boolean needSyncAgain() throws
RemoteException, EsaException { 
return false; 
}

Read

Signature

ExternalRecord read(String structure, String pk)

Description

Reads a single record in the External System

Rationale

Sync subsystem may need to read a single external record. For example, this method is called if a record was not updated during the last synchronization because of an error.

Parameters
  • structure - read record of that structure
  • pk - read record with that ID
Returns

Read ExternalRecord

Exceptions

EsaRecordException, if a record can’t be read/does not exist

Example - see SampleEsa class
public ExternalRecord read(String structure, String pk) throws
RemoteException, EsaException, EsaRecordException { 
// Log debug info for troubleshooting 
log.debug("read (" + structure + ", " + pk + ")"); 
TableServant servant =
name2servant.get(structure); 
ExternalRecord result = servant.read(pk); 
assert result != null; 
return result; 
} 

readDataPage

Signature

Set readDataPage(String cursorID, int pageIndex)

Description

Gets data from a cursor. This method is used to actually access the data in the cursor.

Rationale

getModifiedPaged()/getDeletedPaged() do not return any data. They allocate the Cursor object on the ESA side. Actual data are read through this method.

Parameters
  • cursorID - cursor ID
  • pageIndex - data page number, 0 based
Returns

Data page consisting of either External Records, if the cursor was created within getModifiedPaged, or record IDs, if the cursor is from getDeletedPaged.

Exceptions

None

Example - see ExternalSystemAdapterBase base class
public Set readDataPage(String cursorID, int pageIndex) throws
EsaException, RemoteException
{ 
throw new EsaException("Not implemented"); 
}

Release

Signaturevoid release()
Description

Releases the ESA. This call indicates that the sync core will not use the ESA instance anymore and the ESA may therefore free all used resources such as connection factories, and shutdown.

This is the last method called on the ESA instance during the sync cycle. 
Rationale

Completely releases the ESA.

There could be several sync cycles, run on the single ESA instance in sequence. For example, this happens if needSyncAgain() returns true. Every cycle is ended with an endSync() call. In contrast, release() is called only once, after the last endSync().

This allows the ESA to keep some resources during all sync cycles, between first startSync() and release() calls.
ParametersNone
ReturnsNothing
ExceptionsEsaException if the ESA fails to free resources.
Example - see ExternalSystemAdapterBase base class
public void release() throws EsaException,
RemoteException { 
// NO-OP 
}  

setHelperAPI

Signaturevoid setHelperApi(HelperApi helperApi)
Description

Provides the ESA with a means to call the Sync Core. This is the first method called on the ESA after the instance is created.

ESA should store a reference to HelperApi in this method.
RationaleThe Helper API interface is fully described in ESA HelperAPI Interface. Its major functions are to provide the actual ESA Parameters values stored inside Agiloft and to help with deletion tracking. It is also used to start externally triggered synchronizations.
ParametershelperApi - a reference to the sync subsystem Core API interface
ReturnsNothing
ExceptionsNone
Example - see ExternalSystemAdapterBase base class
/** 
* Sets
HelperApi to be used by ESA, if necessary 
* @param
helperApi 
*/ 
public void
setHelperApi(HelperApi helperApi) {         
this.helperApi = helperApi; 

startSync

SignatureString startSync(String externalSystemID)
DescriptionStarts a synchronization. This is the very first method called during Sync.
RationaleNotify ESA that a synchronization is to be started now. The ESA should prepare itself for doing the synchronization by opening connections to the external system, allocating internal data structures and so forth.
ParametersexternalSystemID - External System ID, as defined in the Sync Configuration wizard.
ReturnsSync version, supported by the ESA. This should be one of the SYNC_VERSION_xxx constants.
ExceptionsEsaException if a sync can't be started.
Example - see SampleEsa class
public String startSync(String externalSystemID)
throws EsaException,
RemoteException { 
this.externalSystemID = externalSystemID; 
// Get callback reference
to the sync core
HelperApi syncCoreApi = getHelperApi();
// Read work dir parameter value
List<EsaParameter>
paramValues = syncCoreApi.getParameter(externalSystemID, 
WORK_DIR);
assert paramValues.size() == 1; // There must be a single string (parameter
type is TEXT, SINGLE).
String workDirPath =
paramValues.get(0).getStrValue();
assert workDirPath != null; 
// Initialize work
dir
workDir = new File(workDirPath); 
if(!workDir.exists()) { 
workDir.mkdirs(); 
} 
// Create servant classes
name2servant = new
HashMap<String, TableServant>(); 
name2servant.put(CONTACTS, new ContactsServant()); 
name2servant.put(CASES, new CasesServant()); 
// Log debug info
for troubleshooting log.debug("Started sync successfully, work dir is " + workDir.getAbsolutePath()); 
// This ESA support sync version 1.1. Please always use the latest you can.
return ExternalSystemAdapter.SYNC_VERSION_1_1; 
} 

syncErrorNotify

Signature

void syncErrorNotify(String message)

Description

Called by Sync to notify the ESA if an error occurs on sync. The exact processing scenario is not defined and depends on the ESA, which may take actions such as notifying the user or logging the error.

Rationale

Notify ESA that sync ends with an error. The ESA may log this or notify the admin somehow.

Usually, this method is most important for HTTPs ESA that are running as part of another system.

Parameters

Message - error message

Returns

Nothing

Exceptions

None

Example - see ExternalSystemAdapter baseclass
public void syncErrorNotify(String message)
throws RemoteException, EsaException { 
log.error("Error on sync: " + message); 
}

Update

Signature

Date update(String structure, Date lastSeen, ExternalRecord values)

Description

Updates a record in the external system

Rationale

Sync subsystem calls this method to propagate changes in an Agiloft record to its External System peer record.

Parameters
  • structure - structure to update record in
  • lastSeen - modification time of the record, as seen by sync last time
  • values - field and relations of the record to update
Returns

New record modification timestamp

Exceptions
  • EsaRecordException if a record can’t be updated,
  • EsaRecordDelayedException if the ESA wants to delay the update and run a bulk update.
  • OptimisticLockFailureException if record is modified after lastSeen timestamp. This means that a record was modified while a sync was in progress.
Example - see SampleEsa class
public Date update(String structure, Date
lastSeen, ExternalRecord values) throws RemoteException,
EsaException, EsaRecordException, OptimisticLockFailureException { 
// Log debug info for troubleshooting 
log.debug("update (" + structure + ", " + values.getId() + ")"); 
TableServant servant = name2servant.get(structure); 
Date result = servant.update(lastSeen, values); 
assert result != null; 
return result; 
} 

CONTENTS
  • No labels