Wednesday, January 6, 2010

Multi-valued attributes aren't multi-valued

I'm back on FIM after a brief hiatus, and I've begun updating my FIM Query Tool with the new unsupported web service client for RC1.

Today I noticed that my multi-valued attributes didn't have multiple values. I'm calling Enumerate on the DefaultClient to check the computed members of a Group. After some digging, I discovered that the attributes weren't recognized as multi-valued because I didn't refresh the schema after instantiating the DefaultClient.

Here's a code snippet from the unsupported WS client sample program:

// First need to construct the client
// We will assume all default contracts

DefaultClient client = new DefaultClient();
// We set the client credentials since often the test cases or client apps run under different accounts
client.ClientCredential = Credential.GetAdminCredential();
// We refresh the schema so that the web service put operations are better informed
client.RefreshSchema();

I interpreted the last comment as, "We only need to call RefreshSchema() when using Put operations." Since I'm only using Enumerate/Pull operations, I just left it out of my code. Well, I was wrong. Turns out that the RmFactory needs a schema refresh before it can determine whether an attribute is multi-valued.

Since this is a potential pitfall every time you use the DefaultClient, I decided to refactor its constructors. Originally, there were three constructors. I added three more that accept an additional NetworkCredential, and now they all call RefreshSchema(). Of course, if you use the original constructors (without the NetworkCredential), they'll use the caller's credentials. Since the FIM Query Tool is a Windows app, it'll use your credentials.

Here are the refactored constructors (note the additional helper method):

public DefaultClient() : this(null)
{
}

public DefaultClient(NetworkCredential clientCredential)
{
this.wsTransferClient = new WsTransferClient();
this.wsTransferFactoryClient = new WsTransferFactoryClient();
this.wsEnumerationClient = new WsEnumerationClient();
this.mexClient = new MexClient();

this.resourceFactory = new RmResourceFactory();
this.requestFactory = new RmRequestFactory();

init(clientCredential);
}

public DefaultClient(
String wsTransferConfigurationName,
String wsTransferFactoryConfigurationName,
String wsEnumerationConfigurationName,
String mexConfigurationName
) : this(
null,
wsTransferConfigurationName,
wsTransferFactoryConfigurationName,
wsEnumerationConfigurationName,
mexConfigurationName
)
{
}

public DefaultClient(
NetworkCredential clientCredential,
String wsTransferConfigurationName,
String wsTransferFactoryConfigurationName,
String wsEnumerationConfigurationName,
String mexConfigurationName
)
{
this.wsTransferClient = new WsTransferClient(wsTransferConfigurationName);
this.wsTransferFactoryClient = new WsTransferFactoryClient(wsTransferFactoryConfigurationName);
this.wsEnumerationClient = new WsEnumerationClient(wsEnumerationConfigurationName);
this.mexClient = new MexClient(mexConfigurationName);

this.resourceFactory = new RmResourceFactory();
this.requestFactory = new RmRequestFactory();

init(clientCredential);
}

public DefaultClient(
String wsTransferConfigurationName,
String wsTransferEndpointAddress,
String wsTransferFactoryConfigurationName,
String wsTransferFactoryEndpointAddress,
String wsEnumerationConfigurationName,
String wsEnumerationEndpointAddress,
String mexConfigurationName,
String mexEndpointAddress
) : this(
null,
wsTransferConfigurationName,
wsTransferEndpointAddress,
wsTransferFactoryConfigurationName,
wsTransferFactoryEndpointAddress,
wsEnumerationConfigurationName,
wsEnumerationEndpointAddress,
mexConfigurationName,
mexEndpointAddress
)
{
}

public DefaultClient(
NetworkCredential clientCredential,
String wsTransferConfigurationName,
String wsTransferEndpointAddress,
String wsTransferFactoryConfigurationName,
String wsTransferFactoryEndpointAddress,
String wsEnumerationConfigurationName,
String wsEnumerationEndpointAddress,
String mexConfigurationName,
String mexEndpointAddress
)
{
this.wsTransferClient = new WsTransferClient(wsTransferConfigurationName, wsTransferEndpointAddress);
this.wsTransferFactoryClient = new WsTransferFactoryClient(wsTransferFactoryConfigurationName, wsTransferFactoryEndpointAddress);
this.wsEnumerationClient = new WsEnumerationClient(wsEnumerationConfigurationName, wsEnumerationEndpointAddress);
this.mexClient = new MexClient(mexConfigurationName, mexEndpointAddress);

this.resourceFactory = new RmResourceFactory();
this.requestFactory = new RmRequestFactory();

init(clientCredential);
}

private void init(NetworkCredential clientCredential)
{
if (clientCredential != null)
{
ClientCredential = clientCredential;
}
RefreshSchema();
}

Extra Credit

Can anyone tell me why there are warning messages on the following methods in the RmFactory class?
  • IsMultiValued
  • IsReference
  • IsRequired
  • RequiredAttributes

No, really, please tell me; I don't know why they're there. For example:

/// <summary>
/// DO NOT USE THIS METHOD -- FOR TESTING ONLY!
/// </summary>
/// <param name="attributeName"></param>
/// <returns></returns>
public bool IsMultiValued(RmAttributeName attributeName)
{
RmAttributeInfo retValue = null;
RmAttributeCache.TryGetValue(attributeName, out retValue);
if (retValue == null)
{
return false;
}
else
{
return retValue.IsMultiValue;
}
}

5 comments:

  1. It's great to have you back on the FIM-track Joe, Welcome back!

    ReplyDelete
  2. By the way, maybe they simply didn't put that much work on it so it could be considered stable enough and proof for that is the bug Paolo Tedesco found in the IsMultiValue property -
    https://espace.cern.ch/idm/Lists/Posts/Post.aspx?ID=16

    Check out his blog, he's got a lot of nice stuff around the Web Service Client - https://espace.cern.ch/idm/default.aspx

    ReplyDelete
  3. Thanks Henrik! Good to see you've been so active in the community. Keep up the great work!

    ReplyDelete
  4. I have been visiting various blogs for my term papers writing research. I have found your blog to be quite useful. Keep updating your blog with valuable information... Regards

    ReplyDelete