Architectural review and refinement: Difference between revisions

From Thunderforce
Jump to navigation Jump to search
No edit summary
No edit summary
 
(27 intermediate revisions by the same user not shown)
Line 4: Line 4:
[[Image:Deployment View.png]]
[[Image:Deployment View.png]]


[https://www.moonlightdesign.org/thunderforce/svn/trunk/architecture/Notional%20Architecture/Deployment%20View.uxf Source UMLet diagram]
[https://www.moonlightdesign.org/thunderforce/svn/trunk/architecture/Architecture/Deployment%20View.uxf Source UMLet diagram]


Thunderforce lives inside Mozilla Thunderbird as an extension that communicates with the remote Salesforce.com servers using the simple object access protocol (SOAP) and caches information locally using SQLite. A SOAP implementation and SQLite are both built into the Mozilla platform.
Thunderforce lives inside Mozilla Thunderbird as an extension that communicates with the remote Salesforce.com servers using the simple object access protocol (SOAP) and caches information locally using SQLite. A SOAP implementation and SQLite are both built into the Mozilla platform.
Line 15: Line 15:
[[Image:Runtime View.jpg]]
[[Image:Runtime View.jpg]]


[https://www.moonlightdesign.org/thunderforce/svn/trunk/architecture/Notional%20Architecture/Runtime%20View.uxf Source UMLet diagram]
[https://www.moonlightdesign.org/thunderforce/svn/trunk/architecture/Architecture/Runtime%20View.uxf Source UMLet diagram]


Thunderforce is largely structured using the model-view-controller pattern to present Salesforce.com data to the end-user using bidirectional interconnects. With Salesforce.com as the master record, changes from any part of the system are pushed down to the lowest layer, and notifications regarding that change propagate from the lowest layer to the top, which is the view.
Thunderforce is largely structured using the model-view-controller pattern to present Salesforce.com data to the end-user using bidirectional interconnects. With Salesforce.com as the master record, changes from any part of the system are pushed down to the lowest layer, and notifications regarding that change propagate from the lowest layer to the top, which is the view.
Line 26: Line 26:


===Salesforce.com data layer (model)===
===Salesforce.com data layer (model)===
[[Image:Module View - Salesforce.com Model.jpg]]
[[Image:Architecture Module View - Salesforce.com Model.png]]


[https://www.moonlightdesign.org/thunderforce/svn/trunk/architecture/Notional%20Architecture/Module%20View%20-%20Salesforce.com%20Model.uxf Source UMLet diagram]
[https://www.moonlightdesign.org/thunderforce/svn/trunk/architecture/Architecture/Module%20View%20-%20Salesforce.com%20Model.uxf Source UMLet diagram]


The diagram above depicts how the Salesforce.com data layer components relate to each other.
The diagram above depicts how the Salesforce.com data layer components relate to each other.
Line 34: Line 34:
In general, the methods in the API interface are those that are in the Salesforce.com AJAX toolkit. The AJAX toolkit's implementation will, indeed, exist mostly unchanged as the thunderforce.sforce.Salesforce object.
In general, the methods in the API interface are those that are in the Salesforce.com AJAX toolkit. The AJAX toolkit's implementation will, indeed, exist mostly unchanged as the thunderforce.sforce.Salesforce object.


To turn Salesforce.com into a bidirectional API with event notification, the BidirectionalAPIPoller will wrap the Salesforce object to implement the BidirectionalAPI interface. This permits future extensibility with both non-polling solutions such as workflow outbound messages and aggregators such as local Salesforce.com cache servers.
To turn Salesforce.com into a bidirectional API with event notification, the nsISalesforceBidirectionalPartnerPoller will wrap the nsSalesforcePartner object to implement the nsISalesforceBidirectionalPartner interface. This permits future extensibility with both non-polling solutions such as workflow outbound messages and aggregators such as local Salesforce.com cache servers.


To promote performance, the Cache object will store the returned values of method calls into memory and, with the offline functionality, a SQLite database. When notifications of changes from the backing store arrive, the cached return values that might be affected by those changes are invalidated. To balance performance with memory, the cache will manage its use of memory based on call and time statistics.
To promote performance, the nsSalesforceBidirectionalPartnerCache object will store the returned values of method calls into memory and, with the offline functionality, a SQLite database. When notifications of changes from the backing store arrive, the cached return values that might be affected by those changes are invalidated. To balance performance with memory, the cache will manage its use of memory based on call and time statistics.
*As an example of cache invalidation, a change to an account object invalidates that object, that object's ancestor objects, queries that involve that entity, and searches that involve that entity
*As an example of cache invalidation, a change to an account object invalidates that object, that object's ancestor objects, queries that involve that entity, and searches that involve that entity
*Queries and searches are special functions that might be handled intelligently in specific update cases to further improve cache performance
*Queries and searches are special functions that might be handled intelligently in specific update cases to further improve cache performance
Line 42: Line 42:
In the cache, a write-back flag is included to effectively switch between online mode and offline mode. When write-back caching is enabled, the only the cache is updated. The backing store is not notified of the cached changes until the write-back flag is turned off. When the write-back flag is off, the caching becomes write-through, meaning that the backing store is notified synchronously. Note that event generation is performed by the cache when write-back is enabled. When write-back is disabled, the backing store generates the events that the cache propagates to its listeners. The cache will not, however, propagate events that do not alter the cached state, regardless of whether or not write-back is enabled.
In the cache, a write-back flag is included to effectively switch between online mode and offline mode. When write-back caching is enabled, the only the cache is updated. The backing store is not notified of the cached changes until the write-back flag is turned off. When the write-back flag is off, the caching becomes write-through, meaning that the backing store is notified synchronously. Note that event generation is performed by the cache when write-back is enabled. When write-back is disabled, the backing store generates the events that the cache propagates to its listeners. The cache will not, however, propagate events that do not alter the cached state, regardless of whether or not write-back is enabled.


One point of current architectural ambiguity is how Thunderbird's undo/redo manager will interoperate with the cache. More research through an experiment will clarify this.
This architectural pattern permits the upper layers to primarily interact with the nsISalesforceBidirectionalPartner interface. As a result, the nsSalesforceBidirectionalPartnerCache class can be implemented in the future to get to the M1 version as soon as possible. The nsSalesforceLocalBidirectionalPartner will be implemented for M1 as a naive bidirectional API that only generates events for local changes.


More investigation into how to leverage Mozilla's built-in cache manager is required. Ideally, the Thunderforce cache will not need to implement most of what it provides.
The nsISalesforceBidirectionalPartnerPoller presents a unidirectional API as a nsISalesforceBidirectionalPartner through periodic polling for changes. To reduce the API call impact, only those entities that the user subscribed to within Thunderbird will be polled (question: will [http://ideas.salesforce.com/article/show/22256/Crossobject_Formulas_RollUp_Summary_fields_coming_in_Summer_07 summary fields] also cause the last-modified timestamp to change and trigger replication (WFOM and getUpdated)?). The set of subscribed entities is also what the Thunderforce searches are limited to, so we don't have to care about those entities that we are not subscribed to.
*Each entity will have its own polling frequency that can be set in entityRefreshIntervals. For now, these will likely be set to default values internally. Eventually, this can be dynamically adjusted based on how frequently that entity is changing. An exponential back-off can be used when that entity has not changed since the last check.
*For subscribed entities that have the replicateable attribute, the replication methods getUpdated() and getDeleted() will be used to reduce the number of API calls to query different entities. If the user does not have permissions to use those methods, the poller will fall back to using entity queries
*If a large amount of data changed, the poller will notify listeners that possibly the entire state of the Salesforce.com data changed and that they should re-query anything that they are interested in. The nsSalesforceBidirectionalPartnerCache object will invalidate its entire cache upon receiving that event


This architectural pattern permits the upper layers to primarily interact with the BidirectionalAPI interface. As a result, the Cache class can be implemented in the future to get an alpha version of Thunderforce ready as soon as possible.
The nsSalesforceLocalBidirectionalPartner class simply generates events based on local changes, such as calls to update(), deleteIds(), etc. This will be implemented in the M1 version and can be used in the full version as an option to enhance local performance, though the nsSalesforceBidirectionalPartnerCache class will be needed to suppress duplicate events in the full version.  


The BidirectionalAPIPoller presents a unidirectional API as a BidirectionalAPI through periodic polling for changes. To reduce the API call impact, only those entities that the user subscribed to within Thunderbird will be polled (question: will [http://ideas.salesforce.com/article/show/22256/Crossobject_Formulas_RollUp_Summary_fields_coming_in_Summer_07 summary fields] also cause the last-modified timestamp to change and trigger replication (WFOM and getUpdated)?). The set of subscribed entities is also what the Thunderforce searches are limited to, so we don't have to care about those entities that we are not subscribed to.
The nsSalesforcePartnerMetricsCollector class collects API usage statistics and includes methods for getting the average number of API calls per hour, the total number of API calls performed in the previous 24 hours, the predicted amount of time left before API usage is exceeded, and other useful numbers. In the full version, a special window can show this information.
*Each entity will have its own polling frequency that can be set in setEntityRefreshIntervals(). For now, these will likely be set to default values internally. Eventually, this can be dynamically adjusted based on how frequently that entity is changing. An exponential back-off can be used when that entity has not changed since the last check.
*For subscribed entities that have the replicateable attribute, the replication methods getUpdated() and getDeleted() will be used to reduce the number of API calls to query different entities. If the user does not have permissions to use those methods, the poller will fall back to using entity queries
*If a large amount of data changed, the poller will notify listeners that possibly the entire state of the Salesforce.com data changed and that they should re-query anything that they are interested in. The Cache object will invalidate its entire cache upon receiving that event


A class that collects API usage statistics is needed. It can probably extend APIDelegator and include methods for getting the average number of API calls per hour, the total number of API calls performed in the previous 24 hours, and other useful numbers.
In the full implementation, the classes will likely be invoked with the following chain of delegates:
*nsSalesforceBidirectionalPartnerCache -> nsSalesforceLocalBidirectionalPartner -> nsISalesforceBidirectionalPartnerPoller -> nsSalesforcePartnerMetricsCollector -> nsSalesforcePartner


In the implementation, the classes will likely be invoked with the following chain of delegates:
In the M1 implementation, the classes will likely be invoked with the following chain of delegates:
*Cache -> BidirectionalAPIPoller -> APIMetricsCollector -> Salesforce
*nsSalesforceLocalBidirectionalPartner -> nsSalesforcePartner


===Thunderbird interface implementation layer (model)===
===Thunderbird interface implementation layer (model)===
This section describes the classes and interfaces used in the Thunderbird interface implementation layer from a high level. Further experimentation and refinement is required to learn more about the Thunderbird interfaces and which ones are the right ones to use. Thunderbird includes a substantial amount of functionality in its base classes, so the experiments will also dig into which functionality to reuse and which functionality to implement ourselves.
The following tree lists each high-level class that will be implemented and the interfaces that they implement.
*Account
*'''[[Requirements#R1: Account Type|Account Type]]'''<br>[[Image:Thunderbird Inferface Module View.png]]<br>[https://www.moonlightdesign.org/thunderforce/svn/trunk/architecture/Architecture/Thunderbird%20Inferface%20Module%20View.uxf Source UMLet diagram]
**class nsThunderforceProtocolInfo implements [http://lxr.mozilla.org/mailnews/source/mailnews/base/public/nsIMsgProtocolInfo.idl#49 nsIMsgProtocolInfo]
**nsThunderforceServer
**class nsThunderforceServer implements [http://lxr.mozilla.org/mailnews/source/mailnews/base/public/nsIMsgIncomingServer.idl#61 nsIMsgIncomingServer]
***@mozilla.org/messenger/server;1?type=imap
*Address Book
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIMsgIncomingServer.html nsIMsgIncomingServer]
*Extension
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsISubscribableServer.html nsISubscribableServer]
*File I/O
**nsThunderforceMessageService
*Filters
***@mozilla.org/messenger/messageservice;1?type=thunderforce
*Folders
***@mozilla.org/messenger/messageservice;1?type=thunderforce-message
**class nsSalesforceMsgFolder implements [http://lxr.mozilla.org/mailnews/source/mailnews/base/public/nsIMsgFolder.idl nsIMsgFolder], [http://lxr.mozilla.org/mailnews/source/mailnews/base/public/nsIMsgFolderNotificationService.idl nsIMsgFolderNotificationService], nsIDBChangeListener, nsISupportsWeakReference, nsRDFResource, nsIUrlListener
***@mozilla.org/messenger/protocol/info;1?type=thunderforce
*Offline
***@mozilla.org/network/protocol;1?name=thunderforce
***@mozilla.org/uriloader/content-handler;1?type=x-application-thunderforcefolder
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIMsgDBService.html nsIMsgDBService]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIMsgProtocolInfo.html nsIMsgProtocolInfo]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIProtocolHandler.html nsIProtocolHandler]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIMsgMessageFetchPartService.html nsIMsgMessageFetchPartService]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIContentHandler.html nsIContentHandler]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIMsgMessageService.html nsIMsgMessageService]
**nsThunderforceFolder
***@mozilla.org/rdf/resource-factory;1?name=thunderforce
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIMsgFolder.html nsIMsgFolder]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIMsgDatabase.html nsIMsgDatabase]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIDBFolderInfo.html nsIDBFolderInfo]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsICollection.html nsICollection]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsICopyMessageListener.html nsICopyMessageListener]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIClassInfo.html nsIClassInfo]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIRDFResource.html nsIRDFResource]
**nsThunderforceMessageHeader
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIMsgDBHdr.html nsIMsgDBHdr]
*'''[[Requirements#R2: Address Book|Address Book]]'''
**nsThunderforceAddressBook
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIAbDirectory.html nsIAbDirectory]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIAbDirectoryQuery.html nsIAbDirectoryQuery]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIAbDirectorySearch.html nsIAbDirectorySearch]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIClassInfo.html nsIClassInfo]
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIRDFResource.html nsIRDFResource]
**nsThunderforceAddressBookFactory
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIAbDirFactory.html nsIAbDirFactory]
**nsThunderforceAddressCard
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIAbCard.html nsIAbCard]
**nsThunderforceAddressGroup
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIAbDirectory.html nsIAbDirectory]
*'''[[Requirements#R3: Message Composition|Message Composition]]'''
**Implement [http://www.xulplanet.com/references/xpcomref/ifaces/nsISmtpService.html nsISmtpService] in nsThunderforceProtocol
**Thunderforce-specific UI customizations can be handled via XUL and Javascript behavior extension
**More research is required
*'''[[Requirements#R4: Message Filters|Message Filters]]'''
**nsThunderforceMatchesSearchTerm
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIMsgSearchTerm.html nsIMsgSearchTerm]
**nsThunderforceFilterListener
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIMsgFilterHitNotify.html nsIMsgFilterHitNotify]
**nsThunderforceAssignmentRuleAction
***[http://www.xulplanet.com/references/xpcomref/ifaces/nsIMsgRuleAction.html nsIMsgRuleAction]
**XUL and Javascript behavior extensions can be used to inject the "Generate..." button, filter criteria additions, "matches" and "does not match" operators, filter values, filter actions, and filter action targets
**More research is required
*'''[[Requirements#R5: Offline Mode|Offline Mode]]'''
**Handled by implementing the offline methods in nsThunderforceServer and nsThunderforceFolder
 
====Folders====
The following table lists which entity types as columns can be used to store an email message within an entity instance. Because the combination of Attachment and Document can be used on all entities, the M1 release will turn all emails into either Attachment or Document objects when copying or moving a message to Salesforce.com.
 
{|border="1"
|'''Parent Entity'''
|'''[http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_objects_attachment.htm Attachment]'''
|'''[http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_objects_note.htm Note]'''
|'''[http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_objects_task.htm Task]'''
|'''[http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_objects_emailmessage.htm EmailMessage]'''
|'''[http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_objects_document.htm Document]'''
|-
|Account
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|-
|Asset
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|-
|Campaign
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|-
|Case
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|-
|Contact
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|-
|Contract
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|-
|Custom Objects
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|-
|EmailTemplate
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|-
|Event
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|-
|Folder
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|<font color="#007700">yes</font>
|-
|Lead
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|-
|Opportunity
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|-
|Product2
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|-
|Solution
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|-
|Task
|<font color="#007700">yes</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|-
|User
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|<font color="#AA0000">no</font>
|<font color="#007700">yes</font>
|}

Latest revision as of 18:02, 16 September 2007

This page includes the details of the post-experiment refined architecture.

Deployment View

Deployment View.png

Source UMLet diagram

Thunderforce lives inside Mozilla Thunderbird as an extension that communicates with the remote Salesforce.com servers using the simple object access protocol (SOAP) and caches information locally using SQLite. A SOAP implementation and SQLite are both built into the Mozilla platform.

Note that this approach will limit the number of Thunderforce clients that can run simultaneously due to API call limits. Thunderforce will include features that limit the number of API calls made in addition to basic API call statistics, such as expected versus actual API calls per hour and the total number of API calls over the past 24 hours. Those statistics can be divided into your organization's daily API call limits to determine how many Thunderforce installations you can have active during the day.

A future extension to the deployment view can involve an optional network server that receives workflow outbound messages from Salesforce.com and broadcasts those changes to local clients to reduce the amount of polling that Thunderforce has to do. Such a server can also substantially increase Thunderforce's scalability as it can be deployed to more computers without drastically increasing the number of API calls performed in a day. Perhaps a generic, client-agnostic solution similar to this might get built in the future (note that this is not a promise of future functionality).

Runtime View

Runtime View.jpg

Source UMLet diagram

Thunderforce is largely structured using the model-view-controller pattern to present Salesforce.com data to the end-user using bidirectional interconnects. With Salesforce.com as the master record, changes from any part of the system are pushed down to the lowest layer, and notifications regarding that change propagate from the lowest layer to the top, which is the view.

Salesforce.com's AJAX Toolkit and Mozilla Thunderbird's interfaces differ in various ways. To resolve this impedance mismatch, the Thunderbird interface implementation layer performs the necessary translations, implementing Salesforce.com as regular Thunderbird objects in the process.

Above the model layer, Thunderforce implements a minimal set of XML user interface language (XUL) overlays to extend the view and JavaScript-based user interface behavior override code to extend the controller. With this strategy, most of Thunderforce's functionality lives within the model and leverages Thunderbird's built-in functionality as much as possible.

Module View

Salesforce.com data layer (model)

Architecture Module View - Salesforce.com Model.png

Source UMLet diagram

The diagram above depicts how the Salesforce.com data layer components relate to each other.

In general, the methods in the API interface are those that are in the Salesforce.com AJAX toolkit. The AJAX toolkit's implementation will, indeed, exist mostly unchanged as the thunderforce.sforce.Salesforce object.

To turn Salesforce.com into a bidirectional API with event notification, the nsISalesforceBidirectionalPartnerPoller will wrap the nsSalesforcePartner object to implement the nsISalesforceBidirectionalPartner interface. This permits future extensibility with both non-polling solutions such as workflow outbound messages and aggregators such as local Salesforce.com cache servers.

To promote performance, the nsSalesforceBidirectionalPartnerCache object will store the returned values of method calls into memory and, with the offline functionality, a SQLite database. When notifications of changes from the backing store arrive, the cached return values that might be affected by those changes are invalidated. To balance performance with memory, the cache will manage its use of memory based on call and time statistics.

  • As an example of cache invalidation, a change to an account object invalidates that object, that object's ancestor objects, queries that involve that entity, and searches that involve that entity
  • Queries and searches are special functions that might be handled intelligently in specific update cases to further improve cache performance

In the cache, a write-back flag is included to effectively switch between online mode and offline mode. When write-back caching is enabled, the only the cache is updated. The backing store is not notified of the cached changes until the write-back flag is turned off. When the write-back flag is off, the caching becomes write-through, meaning that the backing store is notified synchronously. Note that event generation is performed by the cache when write-back is enabled. When write-back is disabled, the backing store generates the events that the cache propagates to its listeners. The cache will not, however, propagate events that do not alter the cached state, regardless of whether or not write-back is enabled.

This architectural pattern permits the upper layers to primarily interact with the nsISalesforceBidirectionalPartner interface. As a result, the nsSalesforceBidirectionalPartnerCache class can be implemented in the future to get to the M1 version as soon as possible. The nsSalesforceLocalBidirectionalPartner will be implemented for M1 as a naive bidirectional API that only generates events for local changes.

The nsISalesforceBidirectionalPartnerPoller presents a unidirectional API as a nsISalesforceBidirectionalPartner through periodic polling for changes. To reduce the API call impact, only those entities that the user subscribed to within Thunderbird will be polled (question: will summary fields also cause the last-modified timestamp to change and trigger replication (WFOM and getUpdated)?). The set of subscribed entities is also what the Thunderforce searches are limited to, so we don't have to care about those entities that we are not subscribed to.

  • Each entity will have its own polling frequency that can be set in entityRefreshIntervals. For now, these will likely be set to default values internally. Eventually, this can be dynamically adjusted based on how frequently that entity is changing. An exponential back-off can be used when that entity has not changed since the last check.
  • For subscribed entities that have the replicateable attribute, the replication methods getUpdated() and getDeleted() will be used to reduce the number of API calls to query different entities. If the user does not have permissions to use those methods, the poller will fall back to using entity queries
  • If a large amount of data changed, the poller will notify listeners that possibly the entire state of the Salesforce.com data changed and that they should re-query anything that they are interested in. The nsSalesforceBidirectionalPartnerCache object will invalidate its entire cache upon receiving that event

The nsSalesforceLocalBidirectionalPartner class simply generates events based on local changes, such as calls to update(), deleteIds(), etc. This will be implemented in the M1 version and can be used in the full version as an option to enhance local performance, though the nsSalesforceBidirectionalPartnerCache class will be needed to suppress duplicate events in the full version.

The nsSalesforcePartnerMetricsCollector class collects API usage statistics and includes methods for getting the average number of API calls per hour, the total number of API calls performed in the previous 24 hours, the predicted amount of time left before API usage is exceeded, and other useful numbers. In the full version, a special window can show this information.

In the full implementation, the classes will likely be invoked with the following chain of delegates:

  • nsSalesforceBidirectionalPartnerCache -> nsSalesforceLocalBidirectionalPartner -> nsISalesforceBidirectionalPartnerPoller -> nsSalesforcePartnerMetricsCollector -> nsSalesforcePartner

In the M1 implementation, the classes will likely be invoked with the following chain of delegates:

  • nsSalesforceLocalBidirectionalPartner -> nsSalesforcePartner

Thunderbird interface implementation layer (model)

The following tree lists each high-level class that will be implemented and the interfaces that they implement.

Folders

The following table lists which entity types as columns can be used to store an email message within an entity instance. Because the combination of Attachment and Document can be used on all entities, the M1 release will turn all emails into either Attachment or Document objects when copying or moving a message to Salesforce.com.

Parent Entity Attachment Note Task EmailMessage Document
Account yes yes yes no no
Asset yes yes yes no no
Campaign yes no yes no no
Case yes no yes yes no
Contact yes yes yes no no
Contract yes yes yes no no
Custom Objects yes yes yes no no
EmailTemplate yes no no no no
Event yes no no no no
Folder no no no no yes
Lead yes yes yes no no
Opportunity yes yes yes no no
Product2 yes yes yes no no
Solution yes no yes no no
Task yes no no no no
User no no no no yes