Salesforce partners often have to dodge complexities that relate to the multi-tenancy nature of the Salesforce platform. Many of these complexities can be handled by dynamic handling of SOQL and object; however, sometimes more drastic measures need to be taken. Join us as we cover the required steps to writing a completely generic interface architecture that can call any external class, as well as other tips and tricks that make AppExchange package development a snap! You will learn how to use an extension architecture that implements an interface to generically call external packages or even methods in a target org that are unknown during base package build.
2. Safe Harbor
Safe harbor statement under the Private Securities Litigation Reform Act of 1995:
This presentation may contain forward-looking statements that involve risks, uncertainties, and assumptions. If any such uncertainties materialize or if any of
the assumptions proves incorrect, the results of salesforce.com, inc. could differ materially from the results expressed or implied by the forward-looking
statements we make. All statements other than statements of historical fact could be deemed forward-looking, including any projections of product or service
availability, subscriber growth, earnings, revenues, or other financial items and any statements regarding strategies or plans of management for future
operations, statements of belief, any statements concerning new, planned, or upgraded services or technology developments and customer contracts or use of
our services.
The risks and uncertainties referred to above include – but are not limited to – risks associated with developing and delivering new functionality for our service,
new products and services, our new business model, our past operating losses, possible fluctuations in our operating results and rate of growth, interruptions or
delays in our Web hosting, breach of our security measures, the outcome of any litigation, risks associated with completed and any possible mergers and
acquisitions, the immature market in which we operate, our relatively limited operating history, our ability to expand, retain, and motivate our employees and
manage our growth, new releases of our service and successful customer deployment, our limited history reselling non-salesforce.com products, and utilization
and selling to larger enterprise customers. Further information on potential factors that could affect the financial results of salesforce.com, inc. is included in our
annual report on Form 10-K for the most recent fiscal year and in our quarterly report on Form 10-Q for the most recent fiscal quarter. These documents and
others containing important disclosures are available on the SEC Filings section of the Investor Information section of our Web site.
Any unreleased services or features referenced in this or other presentations, press releases or public statements are not currently available and may not be
delivered on time or at all. Customers who purchase our services should make the purchase decisions based upon features that are currently available.
Salesforce.com, inc. assumes no obligation and does not intend to update these forward-looking statements.
5. Managed Package Challenges
Flexibility of platform creates (un)expected variability
Edition specific features
• Access and permissions issues
Optional standard features
• Person accounts, record types, etc.
Unknown sharing models
• Org specific user access
Varying target org business logic
• Pre-existing or a-typical business logic
6. Customer Wants… Customer Gets…
“...in our org, we do
<process x> this
way.”
“…your package
does <x>, can it
do <y>?”
“…we use <custom object> instead
of <standard object>“
7. Extension Package Flow
Salesforce Org
Optional Native Base Package Extension Package
Trigger Trigger
Trigger Handler
Business logic Business logic Business logic
Callout Handler
External Application
8. Generic Extension Package Architecture Advantages
Completely
generic
Can handle any
org-specific
complexity
Flexible and
reusable
Extends to
separate packages
or target orgs
10. Custom Settings
• Very similar to Custom Objects
Familiar
• Can be exposed to or hidden from end-users
Visibility
• Won’t count against SOQL limits
Efficient
Readily Available
• Apex
• Visualforce
• Formula Fields
• Web Services API
11. Interfaces
// A new global class w/in the Base Package
global with sharing class <className> {
}
// Define an interface with method blueprints
global interface <interfaceName>{
}
// Define a generic class with generic params
<returnType> <methodName(<parameters>);
12. Dynamic Instantiation of Classes
// get the type of the class outside the package that impl. the generic interface
Type t = Type.forName(<namespace>, <className>);
// use a concrete type of the intf. To create a new inst. of the ext. impl.
Extension ExtensionClass = (Extension)t.newInstance();
13. Code Examples - Outbound
3 Steps to Implement this Design Pattern
14. Step 1: Create Interface in the Base Package
// A new global class w/in the Base Package
global with sharing class BasePackage {
}
// Define an interface with method blueprints
global interface Extension {
}
// Define a generic class with generic params
object GenericMethod(list<object> params);
15. Step 2: Create External Class
public with sharing class MyExtension implements BasePackageNameSpace.BasePackage.Extension {
// define a the generic class that is present in the interface
public static object GenericMethod(list<object> params) {
// replace input params with variables that have concrete types.
set<id> RecordIds = (set<id>)params[0];
string somestring = (string)params[1];
// use the concrete type variables to call the class that does all the work
ProcessFutureCallout(RecordIds, somestring);
// in this example the business logic needs to make a callout to a different API
@future(callout = true)
public static void ProcessFutureCallout(set<id> RecordIds, string somestring) {
// anything inside this class can be custom
}
// in this case there is no need to return anything so return null;
return null;
}
}
16. Step 3: Call External Class from Within the Base Package
// Get the external class name from a custom setting
PackageExtension__c ext = PackageExtension__c.getInstance('extensionName');
if (ext == null) {
else {
// Get the type of the class outside the package that implements the generic interface
Type t = Type.forName(ext.Namespace__c, ext.ClassName__c);
// Using a concrete type of the int., create a new instance of the ext. implementation
BasePackage.Extension ExtensionClass = (BasePackage.Extension)t.newInstance();
// Use ExtensionClass to call the generic method with any relevant variable
list<object> retList = ExtensionClass.GenericMethod(
new list<object>{someRecordIds, someString}
);
}
// Process normal business logic
}
17. Code Examples - Inbound
Re-using Common Code from the Base Package
18. Utilize Base Package Code from the Extension
// Define a global entry point to the package that can be called from outside the package
global object CallMethod(string classDotMethod, list<object> params) {
// Instantiate a generic object to return
object retValue;
// Using a Base Package Class' internal public static method with parameters
else if (classDotMethod == 'BasePackageClass2.Method1')
BasePackageClass2.Method1(
(set<Id>)params[0],
(string)params[1],
(string)params[2]
);
// Return methods results to caller as an object
return retValue;
}
// Using a Base Package Class' internal public static method
if (classDotMethod == 'BasePackageClass1.Method1')
retValue = BasePackageClass1.Method1();