总览
IBM®Rational Team Concert™是协作项目计划和变更管理软件。 单个Rational Team Concert服务器可以托管多个项目区域。 项目区域是不同团队的工作区域。
Rational Team Concert管理员通过可用的UI或Java API控制用户的权限。 接口的选择取决于工作量。
如果您只需要进行少量更改并且这些更改仅限于较小的用户组,则UI方法很实用。
但是,当要进行多个更改或更改影响大量用户时,API驱动的方法更合适。 两个例子:
- 通过迁移过程在Rational Team Concert中添加大量用户
- 根据另一个系统中的更改来管理访问控制(例如,归档用户帐户,以防从企业目录中删除该用户帐户)
在这两种情况下,选择API方法都可以消除系统管理员的干预,并且常规任务可以自动运行。 Rational Team Concert的用户数据输入源可以是平面文件,现有的变更管理系统或企业目录。
本文介绍了如何使用Rational Team Concert API在Rational Team Concert上创建,激活和归档用户。
情境
有两种使用Rational Team Concert用户界面(UI)来添加用户的方法:创建用户或导入用户。
问题
对这两种方法使用UI的局限性在于,管理员一次只能添加一个用户。 当您需要作为一个组进行添加时(例如,当另一个系统中存在多个用户需要导入到Rational Team Concert中时),这些限制变得非常具有挑战性。
用户管理的另外两个方面是归档和激活用户。 管理员可以使用UI来执行这些操作,但是当必须使两个系统(Rational Team Concert和任何其他变更管理系统)保持同步时,这将成为一个挑战。 在手动方式下,与其他变更管理系统一样,在整个过程中都需要Rational Team Concert管理员参与,以复制Active或Archive的用户设置。
解
API驱动的创建用户的方法避免了这些限制。 与在这种自动模式下一样,通过从任何基础系统读取的输入以编程方式创建用户。 如果两个系统(Rational Team Concert和任何其他变更管理系统)处于同步状态,并且需要自动创建,归档或还原用户,而不必等待手动干预才能使两个系统保持同步,则它也可以提供帮助。
通过用户界面管理用户
下面是对Rational Team Concert UI用户管理步骤的简短描述,如屏幕截图所示。 您需要成为Rational Team Concert管理员才能执行这些操作。
创建一个用户
- 使用Web客户机登录到Rational Team Concert服务器。
- 单击“ 用户”选项卡,然后单击“ 创建用户” 。
- 添加必要的用户详细信息,然后保存用户。
图1.在Rational Team Concert中创建用户
从任何目录服务导入用户
- 使用Web客户机登录到Rational Team Concert服务器。
- 单击“ 用户”选项卡,然后单击“ 导入用户” 。
- 搜索用户,然后选择要导入的用户。
图2.导入用户
图3. Import Users中的Select Users视图
图4.确认:用户成功导入
存档活动用户
- 使用Web客户机登录到Rational Team Concert服务器。
- 单击“ 用户”选项卡,然后单击“ 活动用户” 。
- 搜索用户,然后在“操作”列中单击“ 存档用户 ”,如图5的屏幕截图中突出显示的那样。
图5.在Rational Team Concert上归档用户
恢复存档的用户
- 使用Web客户端登录到Rational Team Concert服务器。
- 点击“ 用户”标签,然后点击“ 存档的用户” 。
- 搜索用户,然后单击“操作”列下的“ 还原用户 ”,如图6的屏幕截图中突出显示的那样。
图6.恢复用户
通过API以编程方式管理用户
您还可以通过Rational Team Concert API执行所有先前显示的UI用户管理操作。 通过API进行编程将使流程自动化和标准化,并在批量处理用户帐户时使其快速且无错误。
注意:
要使用这些方法,您需要属于JazzAdmins组。
创建一个用户
如代码清单1所示,通过IContributor类的对象设置所需的用户详细信息,并通过贡献者管理器保存贡献者将在Rational Team Concert上创建一个新用户。
清单1.通过Rational Team Concert API创建用户
private IContributor createRTCUser(ITeamRepository repo,String userName,String
emailAddress,String userId) throws TeamRepositoryException{
//Create Item Type Contributor and set its properties
IContributor i1 = (IContributor) IContributor.ITEM_TYPE.createItem();
i1.setName(userName);
i1.setUserId(userId);
i1.setEmailAddress(emailAddress);
i1.setArchived(false);
return repo.contributorManager().saveContributor(i1, null);
}
通过API导入和创建用户
Rational Team Concert还提供了一种将用户直接从外部用户注册表导入到Rational Team Concert存储库的方法。 为此,需要将Rational Team Concert服务器配置为使用ExternalUser Registry (清单2中的示例中的LDAP)。
清单2.从ExternalUser注册表导入用户
private boolean importUserToRTC(ITeamRepository repo,IContributor cont) throws
TeamRepositoryException{
//Get the ExternalUser Registry and retrieve the required user
IExternalUserRegistryManager externalUserRegMgr = repo.externalUserRegistryManager();
IExternalUser exUser = externalUserRegMgr.fetchUser("test1@in.ibm.com", null);
if (exUser!=null) {
//Once User is found in ExternalUser Registry import it
cont = (IContributor) cont.getWorkingCopy();
cont.setEmailAddress(exUser.getEmailAddresses().get(0));
cont.setName(exUser.getFullNames().get(0));
cont.setUserId(exUser.getUserId());
cont.setArchived(false);
cont = repo.contributorManager().saveContributor(cont, null);
return true;
}
else{
System.out.println("User doesn't exist in ExternalUser Registry");
return false;
}
}
存档和还原用户
在用户的帐户存储在Jazz存储库中之后,这些用户将无法登录到Rational Team Concert Server,除非将他们恢复为活动用户状态。
清单3.在Rational Team Concert中归档用户的方法
public void archiveUsers(ITeamRepository repo)
throws TeamRepositoryException{
// Archive the user in Rational Team Concert
IContributor cont = repo.contributorManager()
.fetchContributorByUserId("user_id", null);
if (cont.isArchived())
return;
cont = (IContributor) cont.getWorkingCopy();
cont.setArchived(true);
repo.contributorManager().saveContributor(cont, null);
}
清单4.恢复已归档用户的方法
public void restoreUser(ITeamRepository repo)
throws TeamRepositoryException, IOException{
IContributor cont = repo.contributorManager().
fetchContributorByUserId(("abc@in.ibm.com"), null);
if (!cont.isArchived())
return;
// Restore the user in Rational Team Concert
cont = (IContributor) cont.getWorkingCopy();
cont.setArchived(false);
repo.contributorManager().saveContributor(cont, null);
}
从外部存储库同步用户
将Rational Team Concert与任何外部系统集成可能需要迁移数据,以使两个存储库保持同步。 否则,如果另一个系统上不存在该用户,则在另一个系统上添加用户的更新可能会在另一个系统上失败。 因此,两个存储库中的用户需要同步。 为简单起见,在此示例中,我们将从属性文件中获取用户信息。 实际做法将涉及从外部变更配置管理存储库中提取用户详细信息。
图7.跨两个存储库同步用户
跨两个存储库映射用户属性
您需要创建一个用户同步规则,以指定Rational Team Concert项目属性如何映射到外部存储库对象字段。
- 创建一个用户同步规则(此示例为Rational Team Concert-User.xml)。
- 然后插入以下字段值:
类型映射部分
领域 | 值 |
---|---|
物品种类 | 贡献者– com.ibm.team.repository |
项目类型限定符 | (留着空白) |
项目经理 | 贡献者经理 |
外部资料库 | 您的外部存储库名称 |
外部经理 | 用户的外部存储库管理器(可变) |
外型 | 用户 |
物业部分
添加所有需要同步的必需属性,并将它们与item属性适当地映射。
物品属性 | 传播 | 外部财产 |
---|---|---|
电子邮件地址 | 双向 | 地址 |
名称 | 双向 | 名称 |
用户身份 | 双向 | 用户身份 |
登录 | ||
储存库名称 |
图8是开发的同步规则文件的屏幕截图。
注意:
在此示例中,我们将unique_identifier作为外部标识符,将User_id作为项目标识符。 唯一标识符可以是外部存储库中唯一的任何东西,也可以是两个或三个外部属性的组合。 在这里,我们将把唯一标识符作为登录名,下划线和属性文件名的组合,例如:login_property文件名。
图8.用户同步规则
将用户与Rational Team Concert同步
对于此示例,我们假设需要在Rational Team Concert中进行同步的用户的详细信息在一个名为User.properties的简单属性文件中可用(有关文件内容,请参见清单5)。 管理员将创建一个读取该文件的程序,将用户的详细信息转换为用户列表,以便我们可以与Rational Team Concert同步。
清单5. User.properties文件的内容
#User details stored in simple property file
#User 1 details
login = Test1
email_address = test1@in.ibm.com
name = Test1 User
user_id = test1@in.ibm.com
deleteDate = 12/08/2011
#User2 details
login = Test2
email_address = test2@in.ibm.com
name = Test2 User
user_id = test2@in.ibm.com
deleteDate = 20/02/2012
因此,每个用户的详细信息将被放入具有相应字段名称(登录名,email_address,名称,user_id,deleteDate)作为键的哈希映射中。 每个UserHashMap将需要添加到ArrayList对象中。 您将遍历此列表,以编程方式将用户与Rational Team Concert同步。
首先,您将检查被同步用户的代理是否已经存在(请参见清单6)。 用户代理是外部存储库和Jazz存储库之间用户的表示。 它与外部对象的URI关联,URI只是外部对象的唯一标识符。 如果存在,则表示用户已经同步,因此在同步过程中将跳过特定用户。
清单6.检查用户代理是否存在的方法
private boolean
userProxyExists(ITeamRepository repo,String uniqueIdentifier,String login)throws
URISyntaxException, TeamRepositoryException{
IInteropManager interopManager = (IInteropManager)
repo.getClientLibrary(IInteropManager.class);
URI u = new URI(uniqueIdentifier);
IExternalProxy proxy = interopManager.findProxybyUri(u, null);
if (proxy != null) {
System.out.println("User with login id :" + login + " is already synched in
Rational Team Concert ");
return true;
}elsereturn false;
}
}
当用户代理不存在时,同步将继续。 用户的代理可能不存在,但相应的贡献者(用户)已存在于Rational Team Concert服务器上。 在这种情况下,这意味着该用户较早被导入了Rational Team Concert,但是可能具有不同的存储库。 在这种情况下,是否继续由管理员决定。 清单7中的示例仅在这种情况下更改了用户的姓名和电子邮件地址。
清单7.将用户与Rational Team Concert同步的方法
public void syncUser(ITeamRepository jazzRepo,IProjectAreaHandle
projectAreaHandle,Map<String,String> UserMap) throws
TeamRepositoryException, URISyntaxException
{
String address = (String) UserMap.get("email_address");
IContributor cont =null;
try {
cont = fTeamRepository.contributorManager().fetchContributorByUserId(address, null);
}catch (ItemNotFoundException e) {
System.out.println("User :" + address +"
not found in contributorManager repository");
}
if (cont != null) {
// now user with this id is already present in Rational Team Concert
// simply change the name to avoid confliction.
UserMap.put("name", cont.getName());
UserMap.put("email_address", cont.getEmailAddress());
}
else
{//Contributor doesn't exist in Rational Team Concert. Need to import the User from
ExternalUserRegistry
if (manager == null)
manager = fTeamRepository.externalUserRegistryManager();
IExternalUser user = null;
user = manager.fetchUser(address, new NullProgressMonitor());
if (user!=null)
System.out.println("User found in directory service :"+ address);
}
//Fetch User Sync Rule to get the External Identifier
IInteropManager interopManager = (IInteropManager)
jazzRepo.getClientLibrary(IInteropManager.class);
ISyncRuleHandle[] syncRuleHandles = interopManager
.findSyncRulesByExternalTypeNameAndProjectArea("User",projectAreaHandle, null);
ISyncRule syncRule = (ISyncRule) fTeamRepository.itemManager()
.fetchCompleteItem(syncRuleHandles[0],IItemManager.REFRESH, null);
IPropertyMapping externalIDmapping = syncRule
.getExternalIdentifierPropertyMapping();
String externalID = externalIDmapping.getExternalPropertyName();
String uniqueId = (String) UserMap.get(externalID);
URI u = new URI(uniqueId);
IExternalProxy proxy = interopManager.findProxybyUri(u, null);
IExternalState externalState = null;
if (proxy == null) {
System.out.println("Creating a new proxy.");
proxy = InteropFactory.INSTANCE.createExternalProxy();
proxy.setSyncRule(syncRule);
proxy = interopManager.saveProxyWithUri(proxy, u, null);
}
if (proxy.getExternalStateItemHandle() == null) {
externalState = InteropFactory.INSTANCE.createExternalState();
} else {
externalState = (IExternalState) interopManager
.getExternalState(proxy, null).getWorkingCopy();
}
externalState.setState(UserMap);
externalState = interopManager.saveExternalState(externalState,null);
proxy =
interopManager.synchronizeIncomingAndWait(proxy,externalState,null,syncRule ,null);
SynchronizationStatus syncStatus = proxy.getSynchronizationStatus();
if (syncStatus != SynchronizationStatus.INCOMING_ERROR) {
// Check if there are any blocking proxies and display status as
// pending blocked.
IExternalProxyHandle[] blockingProxies = interopManager
.getBlockingProxies(proxy, true, null);
if (blockingProxies.length != 0) {
System.out.println("Synchronization is blocked for
User"+UserMap.get("name"));
} else
System.out.println("Synchronization for User + "+UserMap.get("name")+
"successfully completed");
}
else {
System.out.println("Synchronization has error for User +
"+UserMap.get("name")+ "Error is"+proxy.getLastErrorString());
}
}
如果提供者不存在,则从外部用户注册表(在此示例中为LDAP)导入用户。 从UserMap中提取外部标识符值,并构造相应的URI。 如果与该URI对应的ExternalProxy没有退出,则创建一个代理,设置一个相应的syncRule,然后保存该代理。 UserMap被保存为代理的ExternalState,并触发该用户的同步以完成操作。 同步过程将等待该请求先完成,然后再与其他用户同步。 如果请求成功完成,则返回由于处理请求而导致的代理对象的完整状态。 如果请求未成功完成(意味着请求已中止或取消,而不是出现同步错误),则返回null。 用户的同步过程可能会出现以下状态:-
-
未初始化
-
这表明尚未收到外部状态。
这是新创建的代理的默认状态。
好
-
外部对象的最新已知状态已与Jazz项目的最新已知状态同步,没有任何错误。
待处理
-
该操作正在等待代理。
传入错误
-
同步过程由于某种原因而失败。
用户的同步可能由于某种原因而被阻止。 在这种情况下,需要首先删除代理,以便您可以尝试再次同步它。 如果状态为INCOMING ERROR,则可以检索错误原因并采取措施避免再次发生。
将为所有需要同步的用户执行此过程。 您可以通过在“同步规则”视图中右键单击用户的同步规则并选择显示未同步来检查同步状态。
小费:
最好使用syncnizeIncomingAndWait方法而不是syncinizeIncoming方法,因为它会等待请求完成,然后移至仅处理其他同步请求,而与syncinizeIncoming方法无关。 这会将请求放在同步队列的末尾,并立即返回而无需验证完成。 这可能会导致错误或阻塞其他同步项(缺陷或功能)的代理,而这些同步项(缺陷或功能)可能会被仍在同步队列中的用户引用,等待同步。
存档和还原用户
当用户由于已被归档或删除而不再存在于外部存储库中时,要同步此类用户引用的外部对象,您需要将这些用户归档在Rational Team Concert服务器上。 您可以通过调用每个贡献者的的isArchived()方法检查是否贡献者在服务器上进行归档cont.isArchived()
如果用户存档服务器上或False如果不是这将返回True。
要跨存储库运行同步,必须首先恢复已归档的用户,对外部对象执行同步,然后在Rational Team Concert服务器上再次归档用户。 要成功同步外部对象,必须恢复用户,因为已归档的用户将无法登录到存储库。 清单8演示了如何恢复已归档的Rational Team Concert用户。 在这里,您可以传递UserMap.get(“ user_id”)来通过user_id获取贡献者。
同步完成后,您需要再次存档已删除的用户。 在UserMap中删除日期的用户将被存档。
清单8.在Rational Team Concert中归档用户的方法
public void archiveUsers(ITeamRepository repo, Map<String,String> UserMap){
for(String key :UserMap.keySet()){
if (UserMap.get("deleteDate")!= null && !(UserMap.get("deleteDate").equals(""))) {
// Archive the user in Rational Team Concert
IContributor cont = repo.contributorManager()
.fetchContributorByUserId(UserMap.get("user_id"), null);
if (cont.isArchived())
continue;
cont = (IContributor) cont.getWorkingCopy();
cont.setArchived(true);
repo.contributorManager().saveContributor(cont, null);
}
}
}
因此,这些用户的更新仍将作为归档用户复制到Jazz存储库。
将Rational Team Concert用户与外部存储库同步
将用户从Rational Team Concert同步到外部存储库涉及从Rational Team Concert服务器到外部存储库的传出同步。 默认情况下,Jazz服务器上的“发送同步”过程被禁用。 要启用它并创建外部存储库连接,请参阅信息中心中的“ Rational Team Concert中的变更管理”(请参阅参考资料中的链接)。
您将创建一个实现IInteropExternalManager接口的类,并扩展AbstractInteropExternalManager抽象类。 此类将触发来自Rational Team Concert服务器的传出同步。 该类将实现canCreateObject,updateState,createObject和findObject方法,以完全执行发送同步过程,并将外部存储库中完成的任何更新或创建的结果返回给Rational Team Concert。
为了在此简单地解释这一点,我们再次获取一个属性文件并在其中创建或更新用户详细信息。 实际上,可以在任何外部存储库中替换平面文件,并且可以使用其API创建或更新用户详细信息。 请参阅外部存储库的实现,以了解更多(请参阅相关信息中的链接)。
发送同步发生如下:
- findObject方法尝试在外部存储库中查找现有对象。
- 如果找到匹配的对象,则返回该对象的当前属性,并调用getState方法以检索连接的外部对象的当前状态。 可以应用适当的属性值来更新现有外部对象上的更改。
- 如果找不到,则将CanCreateObject设置为True,则在外部存储库中创建一个新对象。
清单9.在属性文件中查找外部对象方法(findObject)
public URI findObject(final String typeName, final String idPropName,
final Map<String, ?> state,final Map<String, Object> returnedState,
final List<String> propertyNames,final
IExternalRepositoryConnection externalConnection) {
String uniqueID = "";
String name = null;
try {
if(idPropName==null)
return null;
if(state.containsKey(idPropName)){
name = (String) state.get(idPropName);
if(name ==null)
return null;
}else{
System.out.println("Unique property not available");
return null;
}
Map<String,Object> externalState = new HashMap<String,Object>();
HashMap UserMap = new HashMap();
uniqueID = (String) state.get(idPropName);
String temp[] = uniqueID.split("_"); //uniqueID is combination of login External
Repository (property file in our case)
String login = temp[0];
Properties propertyValues = new Properties();
// Read properties file
readPropertyFile(propertyValues);
//Get property value depending on key
Object value = propertyValues.getProperty(login);
if(value!=null){
UserMap.put("login", value);
UserMap.put("email_address", propertyValues.getProperty("email_address"));
UserMap.put("name", propertyValues.getProperty("name"));
UserMap.put("user_id", propertyValues.getProperty("user_id"));
UserMap.put("deleteDate", propertyValues.getProperty("deleteDate "));
externalState = UserMap;
}elsereturn null;
returnedState.putAll(externalState);
return URI.create((String)returnedState.get("uniqueID"));
} catch (final Exception erEx) {
ExceptionHandler.handleException(erEx);
throw new RuntimeException(erEx);
}
}
// Read properties file
public void readPropertyFile(Properties propertyValues) throws IOException{
File dir = new File("file:\\C:\\User.properties");
String url = dir.toString();
InputStream stream = null;
String path = "C:\\User.properties";
URL fileURL = new URL(url);
stream = fileURL.openStream();
stream = new FileInputStream(path);
propertyValues.load(stream);
}
上面调用的Find方法将通过登录从属性文件中查找用户的详细信息,该文件是从unique_identifier中检索的。 如果在属性文件中找到用户,则该方法将创建并返回用户的URI。 否则,它返回null 。
创建外部对象方法(createObject)
如果findObject()返回null (如果对象的外部状态为null,则意味着不存在带有必需的unique_identifier的外部对象),可以创建外部对象,具体取决于管理员是否要在外部存储库中创建外部对象或不。 如果管理员要求在外部存储库中创建外部对象,则canCreateObject将返回True,并且InteropService将调用createObject方法,从而在外部存储库中创建外部对象(在本示例中,将为新用户提供详细信息(添加到属性文件中),并将返回CreationInfo对象。
清单10.在外部存储库中创建对象的方法
public boolean canCreateObject(String typeName) {
// Users can be created in ExternalRepository
return true;
}
public CreationInfo createObject(final String typeName,
final Map<String, ?> state,final Map<String, Object> returnedState,
final List<String> propertyNames,final IExternalRepositoryConnection externalConnection,
final IProcessArea processArea) {
Properties propertyValues = new Properties();
URI uri = null;
String address = (String) state.get("address");
String name = (String) state.get("name");
String userId = (String) state.get("user_id");
Map<String,String> UserMap =new HashMap<String,String>();
Map<String,String> externalState = new HashMap<String, String>();
/*Query if the User exists in User.properties or not */
try {
readPropertyFile(propertyValues);
} catch (IOException e) {
e.printStackTrace();
}
Object value = propertyValues.getProperty(userId);
if(value==null || value.toString().equals("")){
//Put User details as the user doesn't exists in User.properties
propertyValues.setProperty("name2", name);
propertyValues.setProperty("user_id2", userId);
propertyValues.setProperty("email_address2", address);
propertyValues.setProperty("login2", userId);
propertyValues.setProperty("deleteDate2", "");
try {
propertyValues.store(new FileOutputStream("C:\\User.properties",true),
"User3 details");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
uri = URI.create(userId+"_"+"User.properties");
} else
//User already exists in External Repository, just create uri
uri = URI.create(value+"_"+"User.properties");
UserMap.put("address", address);
UserMap.put("name", name);
UserMap.put("user_id", userId);
externalState = UserMap;
if (externalState == null) {
return null;
}
returnedState.putAll(externalState);
return new CreationInfo(uri, null);
}
因此,如果在User.properties文件中找不到用户,并且canCreateObject也设置为True,则会在属性文件中插入一个新用户。
更新外部对象方法(updateState)
此方法更新属性文件中外部对象的状态。 更新成功后,它将返回保存状态。 管理员还可以选择是否更新外部存储库中的更改。 清单11中的示例仅在Rational Team Concert服务器上更改用户名的情况下更新属性文件。
清单11.更新外部存储库中的对象的方法
public boolean updateState(final String typeName, final URI uri,final Map<String, ?>
newState,
final Map<String, ?> lastState,final Map<String, Object> returnedState,final List<String>
propertyNames,final ExternalRepositoryConnection externalConnection,final IProcessArea
processArea) {
Properties propertyValues = new Properties();
HashMap UserMap = new HashMap();
Map<String,Object> newMap = null;
//Iterating over newState fetched from Rational Team Concert Server and
//updating property file
only in case the user name has changed
for (Iterator<String> it = newState.keySet().iterator(); it.hasNext();){
String key = it.next();
if(key.equals("name")){
String s2 = (String)newState.get(key);
//Update UserName in property file
try {
readPropertyFile(propertyValues);
} catch (IOException e) {
e.printStackTrace();
}
propertyValues.setProperty(key, s2);
try {
propertyValues.store(new FileOutputStream("C:\\User.properties",true), "User3
details");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
UserMap.put("login", propertyValues.getProperty("login"));
UserMap.put("email_address", propertyValues.getProperty("email_address"));
UserMap.put("name", propertyValues.getProperty("name"));
UserMap.put("user_id", propertyValues.getProperty("user_id"));
UserMap.put("deleteDate", propertyValues.getProperty("deleteDate "));
newMap = UserMap;
returnedState.putAll(newMap);
return true;
}
}
return false;
}
小费:
此代码在SynchroniseUser.zip文件中(请参阅可下载资源部分。)
摘要
您已经看到,Rational Team Concert管理员可以在运行时跨外部和Jazz存储库创建,导入,同步和管理Rational Team Concert用户,而无需任何手动工作。 使用Rational Team Concert API可以节省在两个存储库之间完成同步过程的时间