实现用于预先验证的验证器
假设基于解决方案、基于角色和筛选器检查均已通过,则验证服务执行预先验证活动时将进行的最后一项操作是调用与 UI 组件关联的验证器。如前所述,典型验证器将包含单个 UI 组件或小型相关组件集的唯一预先验证逻辑。换句话说,如果这些组件的验证逻辑相同,则可以将单一验证器与多个 UI 组件关联。但在这种情况下,您可能也会考虑使用筛选器。使用筛选器的优点是:如果某个组件的逻辑与其他组件的逻辑不同,您始终可以选择稍后添加一个验证器。
限制的预先验证与完全预先验证
实际上,在验证器中定义了两种预先验证方法 - 一种方法适用于“限制的”预先验证,另一种方法适用于“完全”预先验证。您要实现的方法分别称为 performLimitedPreValidation() 和 performFullPreValidation()。
之所以有限制的预先验证和完全预先验证之分,是出于性能考虑。在性能至关重要的情况下,客户端基础结构将要求验证服务执行限制的预先验证。目前,要求对操作进行限制的预先验证的唯一情况是在表或树中对行级别的“操作图标”进行预先验证。对于属性,始终要求进行限制的预先验证。
作为验证器开发人员,您不需要考虑何时调用限制的预先验证,何时调用完全预先验证。您只需在验证器中实现以下两种方法 (performLimitedPreValidation() 和 performFullPreValidation()),并假设基础结构将在给定条件下调用适当的方法。在许多情况下,可能您的 performLimitedPreValidation() 和 performFullPreValidation() 方法都具有完全相同的逻辑。
限制的预先验证介绍
以一个包含 50 行且每行有五个行操作图标的表为例。在呈现表之前,将会执行 250 次 (50 行 x 5 个操作) 预先验证检查,以确定每一行中应包含的可用操作。如果平均每个验证检查需要 0.01 秒,那就意味着在页面呈现之前将花费 2.5 秒进行预先验证。
在此情况下,我们愿意为了“性能”而牺牲验证“精度”。在此情况下,客户端基础结构将从验证服务请求“限制的”预先验证。预期目标是:当调用限制的预先验证时,您的验证器不会执行任何“昂贵的”验证检查,启用操作时会出错。
换句话说,假设您需要检查某些访问权限以实际确定某一操作是否应该对用户可用,但众所周知,访问权限检查成本很高。在验证器的 performLimitedPreValidation() 中,您将跳过访问权限检查,并假设用户具有相应的权限。最坏的情况是:操作已对某个用户显示并启用,但在用户尝试调用该操作时,选择后验证会执行更全面的检查并拒绝用户执行该操作。这就是在限制的预先验证中牺牲“精度”来换取“性能”的含义。
完全预先验证介绍
另一方面,假设要预先验证表行或信息页面“下拉”操作列表中的某个操作。因为我们在用户选择这些下拉列表时 (而不是在页面最初呈现时) 使用 Ajax 对其进行填充,所以性能不是很关键。我们最多将验证几个操作,但仅针对单个上下文对象。
在这种情况下,为了提高验证精度而降低性能是可接受的。考虑我们针对限制的预先验证所介绍的相同示例,如果您需要执行某些访问权限检查以确定某一操作是否对用户可用,则在验证器的 performFullPreValidation() 方法中进行检查是可接受的。执行检查可能需要较长时间,但由于在呈现页面 (或 Ajax 组件) 之前执行的验证数是可管理的,因此少量对性能有影响的检查是可接受的。
切记,在许多情况下,您的验证器的 performFullPreValidation() 和 performLimitedPreValidation() 方法实现将是相同的。
创建验证器
创建验证器类应相当简单。唯一需要做的是扩展 com.ptc.core.ui.validation.DefaultUIComponentValidator 来创建一个类。
下面的类表示简单验证器类的基干。
package com.ptc.windchill.enterprise.myPackage.validators;
import com.ptc.core.ui.validation.DefaultUIComponentValidator;
public class MyValidator extends DefaultUIComponentValidator{
//override one or more validation methods from
DefaultUIComponentValidator
}
实现预先验证方法
创建验证器类基干后,如果要为属性添加预先验证逻辑,则需要实现 performLimitedPreValidation() 方法。如果要为操作添加预先验证逻辑,则要同时实现 performFullPreValidation() 和 performLimitedPreValidation() 方法。如上文中关于限制的预先验证和完全预先验证的章节所述,这两种方法的实现可能相同,也可能不同。下一页中的类包含这些方法的一些基干实现。
public class MyValidator extends DefaultUIComponentValidator{
@Override
public UIValidationResultSet performFullPreValidation()
(UIValidationKey validationKey,
UIValidationCriteria validationCriteria, Locale
locale) throws WTException {
UIValidationResultSet resultSet =
UIValidationResult.newInstance();
// perform your business logic here
// if you want to enable the action/component, do this:
//
resultSet.addResult(UIValidationResult.newInstance(validationKey,
// UIValidationStatus.ENABLED));
// if you want to disable the action/component, do this:
//
resultSet.addResult(UIValidationResult.newInstance(validationKey,
// UIValidationStatus.DISABLED));
// if you want to hide the action/component, do this:
//
resultSet.addResult(UIValidationResult.newInstance(validationKey,
UIValidationStatus.HIDDEN));
return resultSet;
}
@Override
public UIValidationResultSet performLimitedPreValidation()
(UIValidationKey validationKey,
UIValidationCriteria validationCriteria, Locale
locale) throws WTException {
UIValidationResultSet resultSet =
UIValidationResultSet.newInstance();
// perform your business logic here
// if you want to enable the action/component, do this:
//
resultSet.addResult(UIValidationResult.newInstance(validationKey,
// UIValidationStatus.ENABLED));
// if you want to disable the action/component, do this:
//
resultSet.addResult(UIValidationResult.newInstance(validationKey,
// UIValidationStatus.DISABLED));
// if you want to hide the action/component, do this:
//
resultSet.addResult(UIValidationResult.newInstance(validationKey,
UIValidationStatus.HIDDEN));
return resultSet;
}
}
注册验证器
创建验证器并实现相应的预先验证方法后,唯一要做就是进行注册。您需要注册验证器,以便将其与要验证的操作或属性关联。
为操作注册验证器时,您可以仅使用操作名称或使用操作名称和对象类型的组合来建立关联。在大多数情况下,仅使用操作名称来标识您的验证器即可,而且此为首选设置。为属性注册验证器时,可使用属性的描述符 ID 来生成关联。
基本验证器注册
要注册您的验证器 (仅使用操作的操作名称),需要向 *service.properties.xconf 中添加如下所示的条目:
<Service context="default"
name="com.ptc.core.ui.validation.UIComponentValidator">
<Option requestor="null" serviceClass="[您的完全限定验证器类]"
selector="[操作名称/属性描述符 ID]" />
</Service>
传播到 *service.properties 后,它应生成如下所示的条目:
wt.services/svc/default/com.ptc.core.ui.vali
dation.UIComponentValidator/[操作名称/属性描述符 ID]/null/0=[您的完全限定验证器类]/duplicate
• 请注意,在此情况下,requestor 属性为空,这意味着在查找中不使用操作的对象类型。
基于类型的验证器注册
如果您认为在注册验证器时,除操作名称外还需要使用操作的对象类型,其注册方法与仅使用操作名注册验证器非常相似。差异在于特性条目中的 requestor 属性。对于不使用对象类型的验证器,requestor 属性设置为空。对于要使用对象类型注册的验证器,requestor 值将是要注册的类型的完全限定类名称。
requestor 属性中使用的类名称与 actions.xml 中的类名称相对应。
例如,考虑 actions.xml 文件的此片段 (注意:为提高可读性,某些文本已删除):
<objecttype name="problemReport" class="wt.change2.WTChangeIssue"
...>
<action name="create" >
...
</action>
...
</objecttype>
在此情况下,我们正在查看的操作名称为 create。很显然,系统中可能会有多个操作被定义为 create。但是,每个 create 操作的验证规则可能会不同,具体取决于正在创建的对象类型。因此,针对 create 操作 (例如,“问题报告”和“部件”的创建操作) 使用单独的验证器可能是有意义的。
假设已定义一个名为com.ptc.windchill.enterprise.change2.validators.ChangeMgmtCreateWizardsValidator 的验证器,且要为其注册 create 操作,但该操作仅用于创建“问题报告”时。
我们可以查看上面的 actions.xml 条目,可以看到:“问题报告”的 create 操作实际上是针对名为 problemReport 的 objecttype 定义的,更重要的是,它的类是 wt.change2.WTChangeIssue。
通过将 actions.xml 中的类值用作我们的特性条目中的 requestor 属性,可以指示验证服务:我们只想针对对象类型为 wt.change2.WTChangeIssue 的 create 操作注册我们的验证器 (com.ptc.windchill.enterprise.change2.validators.ChangeMgmtCreateWizardsValidator),如下所示:
<Service context="default"
name="com.ptc.core.ui.validation.UIComponentValidator">
<Option
serviceClass="com.ptc.windchill.enterprise.change2.validators.Chan
geMgmtCreateWizardsValidator"
selector="create" requestor="wt.change2.WTChangeIssue" />
</Service>
以上即为其全部代码。基本上,如果要为操作注册验证器,但该操作仅与某一特定对象类型 (在 actions.xml 中) 关联时,即可使用 actions.xml 中的类属性作为属性条目中的 requestor 属性。
请注意一下几点:
• 此基于类型的查找当前仅适用于在 actions.xml 中定义的操作。它不适用于属性或其他 UI 组件。
• 为使其可用,actions.xml 条目中的类属性必须是一个具体类 (而不是接口 - 很多实例当前将类属性设置为 wt.fc.Persistable)。在大多数情况下,可以将现有类属性从接口更改为具体类。但在更改之前,您应该首先检查要修改的 actions.xml 文件的所有者。
验证验证器注册
您可以运行命令行报告来查看针对给定操作或属性注册的验证器。如果您的验证器未显示在此报告中,则表示它未正确注册且永远不会被调用。
示例如下:
查找单一验证器 (非基于类型的验证器)
• 使用示例 1 - 查找为 pasteAsCopy 操作注册的验证器:
Y:\>java com.ptc.core.ui.validation.UIComponentValidatorFactory
pasteAsCopy
已注册的验证器:
pasteAsCopy ->
com.ptc.core.foundation.saveas.validators.PasteValidator
查找多个验证器 (非基于类型的验证器)
• 使用示例 2 - 查找为 setState 和 pasteAsCopy 操作注册的验证器:
Y:\>java com.ptc.core.ui.validation.UIComponentValidatorFactory
setState pasteAsCopy
已注册的验证器:
setState ->
com.ptc.windchill.enterprise.lifecycle.validators.SetStateValid
ator
pasteAsCopy ->
com.ptc.core.foundation.saveas.validators.PasteValidator
查找单一验证器 (非基于类型的验证器)
• 使用示例 3 - 查找针对在 actions.xml 中 objecttype 名称属性为 problemReport 的 create 操作注册的验证器。(请注意:必须针对基于类型的查找运行方法服务器)
Y:\>java com.ptc.core.ui.validation.UIComponentValidatorFactory
create:problemReport
已注册的验证器:
create:problemReport(wt.change2.WTChangeIssue) ->
com.ptc.windchill.enterprise.change2.validators.ChangeMgmtCreat
eWizardsValidator
查找多个验证器 (基于类型和非类型的验证器相混合)
• 使用示例 4 - 查找针对在 actions.xml 中 objecttype 名称属性为 problemReport 的 pasteAsCopy 操作和 create 操作注册的验证器。(请注意:必须针对基于类型的查找运行方法服务器)
Y:\>java com.ptc.core.ui.validation.UIComponentValidatorFactory
pasteAsCopy create:problemReport
已注册的验证器:
pasteAsCopy ->
com.ptc.core.foundation.saveas.validators.PasteValidator
create:problemReport(wt.change2.WTChangeIssue) ->
com.ptc.windchill.enterprise.change2.validators.ChangeMgmtCreat
eWizardsValidator