Intro
Once in a while you will come across very complex business requirements while implementing FIM in a large environment. These requirements often require a classic architecture (with VB or C# extensions), but can create very messy code that is hard to maintain. This article does not start another discussion on whether or not you should (try to) use 100% declarative (codeless) or a classic architecture when implementing such large scenarios. A good article on this topic: codeless architecture and when you are not able to use declarative configuration. Instead, this article will focus on how you should implement a proper classic architecture, in a way that is performant, readable, agile and easy to maintain.Advanced flow rules
A basic map attributes for import method is written as follows:void IMASynchronization.MapAttributesForImport(
string flowRuleName, CSEntry csentry, MVEntry mventry)
{
switch (flowRuleName)
{
case "SomeFlowRuleName":
{
// Some code ...
}
break;
default:
{
throw new EntryPointNotImplementedException(
string.Concat("Flow rule name not found: ",
flowRuleName));
}
}
}
Reflection
Reflection is a programming concept that enables you to inspect (or even change) source code. You can apply reflection on a class itself, but also on other classes. Reflection enables you to use the same implementation of the methodMapAttributesForImport
in all your rules extensions. Similar code can also be used for MapAttributesForExport
, MapAttributesForJoin
and ResolveJoinSearch
.
Each attribute flow is implemented in its own method, eg importMustChangePassword
. By using a simple naming convention, import/export target attribute name, readability is very good. Because each attribute can only be target of one export attribute flow, this convention also ensures uniqueness.
Code using reflection
First line constructs an array of objects. These are the parameters that will be passed to our advanced attribute flow method. Second line constructs a BindingFlags
object. This object determines what kind of method we will call. The binding flags describes the method signature. InvokeMethod
indicates we will not call a constructor. NonPublic
indicates the access modifier: we will call a private, internal or protected method. Instance
specifies that instance members are to be included in the search.
The third line does the actual call. It will invoke the method on the current class. The reflection mechanism will search in the current class for methods matching the flowRuleName, bindingFlags and the number and type of parameters. If one and only one match is found, the method is invoked.
void IMASynchronization.MapAttributesForImport(string flowRuleName,
CSEntry csentry, MVEntry mventry)
{
try
{
object[] parameters = { mventry, csentry };
BindingFlags bindingFlags = BindingFlags.InvokeMethod |
BindingFlags.NonPublic | BindingFlags.Instance;
this.GetType().InvokeMember(flowRuleName, bindingFlags,
null, this, parameters);
}
catch (MissingMethodException)
{
throw new EntryPointNotImplementedException(
string.Concat("Flow rule name not found: ",
flowRuleName));
}
}
Example attribure flow rule
An example to illustrate the mechanism is this rule to set a flag on the metaverse person object whether he has to change his password./// Imports the must change password flag.
/// This flag is true if the pwdLastSet timestamp
/// attribute is set to 0.
/// </summary>
/// <param name="mventry">Destination metaverse entry.</param>
/// <param name="csentry">Source connector space entry.</param>
private void importMustChangePassword(MVEntry mventry, CSEntry csentry)
{
if (csentry["pwdLastSet"].IsPresent)
{
mventry["mustChangePassword"].BooleanValue =
csentry["pwdLastSet"].IntegerValue == 0;
}
}