为操作和特性添加验证逻辑
本部分为开发人员提供了为对特定操作或 UI 组件执行验证而实现验证委派 (也称为“验证器”) 所需的信息。
目标
创建 UI 组件验证服务以为 Windchill 客户端提供中心服务,以便对 Windchill UI 中出现的操作和其他组件执行验证。对服务和结果解释的调用应由版本 9.0 中开发的多个公用组件管理。应用程序开发人员的主要职责是开发验证器类,该类由验证服务调用以验证特定操作或 UI 组件。本文档对编写验证器类的过程和最佳做法进行了概述。
适用性
此文档应由负责编写一个或多个验证器的开发人员使用,以确定某一操作或 UI 组件在给定页面或上下文等项目上是否有效。文档应介绍调用验证器时执行的每种验证操作类型的示例。
结构
UI 验证服务中的所有类 (已注明项除外) 均在 com.ptc.core.ui.validation 包中定义。
验证器开发人员无需与此图中的所有类交互 (但可与其中的很多类交互)。此文档将讨论各种类,但在开始时,编写验证器的开发人员应始终定义其验证器类以扩展 DefaultUIComponentValidator。
|
另请注意,随着需求的变化,这些类可能会进行更新。要获取在每个类中定义的最新方法和属性集,请参阅 Windchill Javadoc。
|
参与者
本部分的读者应全面了解 Java 编程语言,并且还会熟悉 Windchill 解决方案套件。
协作
验证器开发人员需要与公用组件开发人员和验证服务的其他调用方进行协作。为确保服务的调用方将所有数据传递到给定验证器需要执行验证的服务,这种协作必不可少。强烈建议验证器开发人员在该方法的 Javadoc 中包括给定验证方法所需的数据列表。此外,还可以包括此方法说明的验证键 (操作名称) 的列表。例如:
public class DefaultWIPValidator extends DefaultUIComponentValidator
{
…
/**
* This implementation of performLimitedPreValidation will check the checkout
* state of all the Workable objects in the ValidationCriteria's targetObjects
* WTCollection, and base its validation results on whether an object in the
* given state can have the specified action performed on it. (e.g., an object
* in the checked-in state can not have an undo checkout action performed on it)
*
* At a minimum, a caller of this method should provide the targetObjects
* WTCollection in the validationCriteria argument.
*
* The expected validationKey arguments for this method are:
* checkin
* checkout
* undocheckout
*
* <BR><BR><B>Supported API: </B>false
*
* @param validationKey The String identifying the action or component being validated.
* @param validationCriteria Object holding information required to perform validation tasks.
* @param locale The user's Locale. If a <i>null</i> value is passed in, the session locale will be used.
* @return UIValidationResultSet
**/
public UIValidationResultSet performLimitedPreValidation (String validationKey,
UIValidationCriteria validationCriteria, Locale locale)
throws WTException
{
…
}
…
}
后果
通过将此文档用作参考,开发人员应创建一致的性能验证器。验证服务的调用方应确信其验证的任何操作或 UI 组件都将以一致的方式进行验证。
实现
概述
从术语 validation 的定义开始非常有帮助。出于此讨论的目的,术语 validation 指的是执行的活动,用于确定用户可以查看或执行的操作。例如:
• 是否应显示“创建部件”操作?
• 是否允许检出此对象?
• 用户在此创建向导中输入的所有内容是否均有效?
为了便于讨论,可将 validation 分为三个广义类别:
预验证
• 尝试回答问题:“是否应在 UI 中向用户显示内容?如果是,是否应显示可编辑/可选内容?”
• 例如,我们是否应为容器 B 中的用户 A 显示并启用“创建部件”操作?
• 可对操作或其他 UI 组件 (状况符号、属性、表格等) 执行预先验证操作。
选择后验证
• 尝试回答问题:“是否应允许继续执行刚刚在 UI 中选择的操作?”
• 例如,我们是否可以允许检出部件 A、B 和 C?
提交后验证
• 尝试回答问题:“用户刚刚输入的数据有效吗?”
• 例如,当用户在“创建部件”向导中单击“下一步”时,是否让他们转到下一步,或者他们是否需要修改当前步骤中的某些数据 (例如名称、编号等)?
UI 组件 (操作) 验证服务针对以上列出的每种验证类型显示一个或多个 API。
在高级别案例中,公用组件或其他客户端会针对验证服务调用验证 API,同时传递一个或多个验证键 (可视为操作名称,例如 create) 和一个 UIValidationCriteria bean,后者包含客户端已知的执行验证所需的数据。验证服务使用验证键执行查找,并标识用于执行验证活动的验证器类。然后,服务会将键和 UIValidationCriteria 传递给标识的验证器,并等待结果 (集)。服务会在从验证器获得结果时 (创建结果束并) 将其返回到客户端。
本文档将集中介绍验证器类和方法的编写。
封装/模块化
所有与 UI 组件验证服务相关的类均封装在 com.ptc.core.ui.validation 中。其源位于模块 \Windchill\src\com\ptc\core\ui\validation 中。强烈建议开发人员在执行验证器开发时更新此目录中的所有文件,并编译最新版本的 Java 类。
编写验证器的开发人员应将验证器类放在对验证器验证的操作/UI 组件有意义的包或模块中。
编写验证器类
操作和 UI 组件所有者将负责编写验证这些操作和 UI 组件的验证器。要创建验证器,您需要创建 com.ptc.core.ui.validation.DefaultUIComponentValidator.java 的子类。当前在 DefaultUIComponentValidator.java 中定义了五种公用方法。您的子类可以覆盖其中一种方法:
performFullPreValidation()
performLimitedPreValidation()
validateFormSubmission()
validateSelectedAction()
validateSelectedMultiSelectAction()
对于未覆盖的方法,将从 DefaultUIComponentValidator 类继承默认行为 (始终启用/允许)。
您还需要创建特性条目,以将验证器类与特定的验证键 (操作) 相关联。验证服务使用这些条目为给定的验证键 (操作) 查找正确的验证器。条目将进入 service.properties 或应用程序团队的服务特性文件 (请向您的组主管询问应放置条目的位置),并应具有以下格式:
wt.services/rsc/default/com.ptc.core.ui.UIComponentValidator/<validationKey>/
null/0=com.ptc.my.validators.MyValidator
其中,<validationKey> 是您的操作/组件的验证键,右侧值是验证器的完全限定的类名称。
在验证器实现中,您不必执行三种类型的检查。在调用验证器之前,系统会通过验证服务执行这些检查。其中包括:
• 基于角色的检查 (基于 RBUI 系统输入的操作的可见性,此检查不会与访问控制检查相混淆,后者需要在验证器中完成。)
• 基于安装的检查 (如果已在给定系统上安装解决方案集,则是否应使用操作或 UI 组件?)
• 基于客户端的检查 (是否应在 DTI 或 PSB 等给定客户端中使用操作或 UI 组件?)
示例代码
示例验证器 - DefaultWIPValidator.java
| 可在以下位置找到此源代码:<Windchill>\src\com\ptc\windchill\enterprise\wip\DefaultWIPValidator.java |
/* bcwti
*
* Copyright (c) 2004 Parametric Technology Corporation (PTC). All Rights
* Reserved.
*
* This software is the confidential and proprietary information of PTC.
* You shall not disclose such confidential information and shall use it
* only in accordance with the terms of the license agreement.
*
* ecwti
*/
package com.ptc.windchill.enterprise.wip;
import com.ptc.core.ui.validation.*;
import java.lang.ClassNotFoundException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Locale;
import org.apache.log4j.Logger;
import wt.access.AccessPermission;
import wt.access.AccessControlHelper;
import wt.epm.workspaces.EPMWorkspaceHelper;
import wt.fc.Persistable;
import wt.fc.ReferenceFactory;
import wt.fc.WTReference;
import wt.fc.collections.WTArrayList;
import wt.fc.collections.WTCollection;
import wt.folder.CabinetBased;
import wt.folder.CabinetMember;
import wt.folder.Foldered;
import wt.folder.FolderHelper;
import wt.inf.container.WTContained;
import wt.inf.container.WTContainerHelper;
import wt.log4j.LogR;
import wt.org.WTUser;
import wt.sandbox.SandboxHelper;
import wt.session.SessionHelper;
import wt.util.WTException;
import wt.vc.Iterated;
import wt.vc.VersionControlHelper;
import wt.vc.wip.Workable;
import wt.vc.wip.WorkInProgressHelper;
import wt.vc.wip.WorkInProgressState;
public class DefaultWIPValidator extends DefaultUIComponentValidator
{
private Logger
logger = LogR.getLogger("wt.method.server.httpgw");
private ReferenceFactory refFactory = null;
private static final String FULL = "full";
private static final String LIMITED = "limited";
private static final String SELECTED = "selected";
/**
* This implementation of performLimitedPreValidation will
* check the checkout state of all the Workable objects
* in the ValidationCriteria's targetObjects
* WTCollection, and base its validation results on whether
* an object in the given state can have the specified
* action performed on it. (e.g., an object
* in the checked-in state can not have an undo checkout
* action performed on it)
*
* At a minimum, a caller of this method should provide the
* targetObjects
* WTCollection in the validationCriteria argument.
*
* The expected validationKey arguments for this method are:
* checkin
* checkout
* undocheckout
*
* <BR><BR><B>Supported API: </B>false
*
* @param validationKey The String identifying the action
* or component being validated.
* @param validationCriteria Object holding information required
* to perform validation tasks.
* @param locale The user's Locale. If a null value is passed in,
* the session locale will be used.
* @return UIValidationResultSet
**/
public UIValidationResultSet performLimitedPreValidation (String valida
tionKey,
UIValidationCriteria validationCriteria, Locale loc
ale)
throws WTException
{
logger.debug("ENTERING DefaultWIPValidator.performLimitedPreValidati
on");
logger.trace(" validtionKey -> " + validationKey);
logger.trace(" validationCriteria -> " + validationCriteria.toStrin
g());
UIValidationResultSet resultSet = performWIPValidation(validationKey,
validationCriteria, locale, LIMITED);
logger.trace("RETURNING " + resultSet.toString());
logger.debug("EXITING DefaultWIPValidator.performLimitedPreValidat
ion");
return resultSet;
}
public UIValidationResultSet performFullPreValidation (String validation
Key,
UIValidationCriteria validationCriteria, Locale loca
le)
throws WTException
{
logger.debug("ENTERING DefaultWIPValidator.performFullPreValidation");
logger.trace(" validtionKey -> " + validationKey);
logger.trace(" validationCriteria -> " + validationCriteria.toString(
));
UIValidationResultSet resultSet = performWIPValidation(validationKey,
validationCriteria, locale, FULL);
logger.trace("RETURNING " + resultSet.toString());
logger.debug("EXITING DefaultWIPValidator.performFullPreValidation");
return resultSet;
}
public UIValidationResult validateSelectedAction (String validation
Key,
UIValidationCriteria validationCriteria, Locale locale)
throws WTException
{
logger.debug("ENTERING DefaultWIPValidator.validateSelectedAction");
logger.trace(" validtionKey -> " + validationKey);
logger.trace(" validationCriteria -> " + validationCriteria.toString(
));
UIValidationResult result = null;
WTReference wtRef = validationCriteria.getContextObject();
Persistable persistable = wtRef.getObject();
if (!(persistable instanceof Workable)){
return new UIValidationResult(validationKey, wtRef, UIValidat
ion
Status.DENIED, null);
}
Workable workable = (Workable)persistable;
if (validationKey.equalsIgnoreCase("checkin") || validationKey.equals
IgnoreCase("undocheckout")){
result = performCheckinValidation(validationKey, workable, SELE
CTED,
(WTUser)(validationCriteria.getUser().getPrincipal()));
}
else if (validationKey.equalsIgnoreCase("checkout")){
result = performCheckoutValidation(validationKey, workable, SELE
CTED);
}
logger.trace("RETURNING " + result.toString());
logger.debug("EXITING DefaultWIPValidator.validateSelectedAction");
return result;
}
public UIValidationResultSet validateSelectedMultiSelectAction
(String validationKey,
UIValidationCriteria validationCriteria, Locale locale)
throws WTException
{
logger.debug("ENTERING DefaultWIPValidator.
validateSelectedMultiSelectAction");
logger.trace(" validtionKey ->
" + validationKey);
logger.trace(" validationCriteria ->
" + validationCriteria.toString());
UIValidationResultSet resultSet =
performWIPValidation(validationKey,
validationCriteria, locale, SELECTED);
logger.trace("RETURNING " + resultSet.toString());
logger.debug("EXITING DefaultWIPValidator.validateSelectedMultiSelect
Action");
return resultSet;
}
// ***NOTE:
// There is no post-submit validation for the WIP actions
// (checkin, checkout, undocheckout), since there is
// no wizard launched when one of the actions
// is performed. Therefore, there is no need to define a
// validateFormSubmission method in this class.
//
// public UIValidationResult validateFormSubmission (String validationKey,
// UIValidaitonCriteria validationCriteria, Locale locale)
private UIValidationResultSet performWIPValidation(String validationKey,
UIValidationCriteria validationCriteria, Locale locale, String
validationType)
throws WTException
{
UIValidationResultSet resultSet = new UIValidationResultSet();
WTCollection targetObjects = new WTArrayList(validationCriteria.get
TargetObjects());
Iterator workableIter = getWorkableIterator(targetObjects);
Workable workable = null;
while (workableIter.hasNext()){
workable = (Workable)workableIter.next();
if (validationKey.equalsIgnoreCase("checkin") || validationKey.equal
sIgnoreCase("undocheckout")){
resultSet.addResult(performCheckinValidation(validationKey, worka
ble, validationType,
(WTUser)(validationCriteria.getUser().getPrincipal())));
}
else if (validationKey.equalsIgnoreCase("checkout")){
resultSet.addResult(performCheckoutValidation(validationKey, worka
ble, validationType));
}
}
resultSet.appendResults(processNonWorkables(targetObjects, validationKey,
validationType));
return resultSet;
}
private UIValidationResult performCheckinValidation(String validationKey,
Workable workable,
String validationType, WTUser user)
throws WTException
{
WTReference wtRef = getWTReference(workable);
if (validationType.equals(LIMITED)){
WorkInProgressState state = workable.getCheckoutInfo().getState();
if (state.equals(WorkInProgressState.CHECKED_OUT) || state.equals(
WorkInProgressState.
CHECKED_OUT_TO_SANDBOX)){
return new UIValidationResult(validationKey, wtRef, UIValidation
Status.ENABLED, null);
}
else{
return new UIValidationResult(validationKey, wtRef, UIValidation
Status.DISABLED, null);
}
}
else if (validationType.equals(FULL) || validationType.equals(SELECTED)){
UIValidationStatus goodStatus = null;
UIValidationStatus badStatus = null;
if (validationType.equals(FULL)){
goodStatus = UIValidationStatus.ENABLED;
badStatus = UIValidationStatus.DISABLED;
}
else{
goodStatus = UIValidationStatus.PERMITTED;
badStatus = UIValidationStatus.DENIED;
}
if (workable instanceof CabinetBased){
CabinetBased orig;
if (WorkInProgressHelper.isWorkingCopy(workable)) {
orig = (CabinetBased)WorkInProgressHelper.service.originalCopyOf
(workable);
}
else {
orig = (CabinetBased)workable;
}
if (isNewInWorkspace(orig)){
return new UIValidationResult(validationKey, wtRef, badStatus,
null);
}
}
if (WorkInProgressHelper.isCheckedOut(workable, user) ||
(WorkInProgressHelper.isCheckedOut(workable) &&
WTContainerHelper.service.isAdministrator(((WTContained)workable).
getContainerReference(), user))){
return new UIValidationResult(validationKey, wtRef, goodStatus,
null);
}
else{
return new UIValidationResult(validationKey, wtRef, badStatus,
null);
}
}
return new UIValidationResult(validationKey, wtRef, UIValidationStatus.
ENABLED, null);
}
private UIValidationResult performCheckoutValidation(String validationKey,
Workable workable,
String validationType)
throws WTException
{
WTReference wtRef = getWTReference(workable);
if (validationType.equals(LIMITED)){
WorkInProgressState state = workable.getCheckoutInfo().getState();
if (state.equals(WorkInProgressState.CHECKED_OUT) || state.equals(
WorkInProgress
State.CHECKED_OUT_TO_SANDBOX)){
return new UIValidationResult(validationKey, wtRef, UIValidation
Status.DISABLED,null);
}
else{
return new UIValidationResult(validationKey, wtRef, UIValidation
Status.ENABLED, null);
}
}
else if (validationType.equals(FULL) || validationType.equals(SELECTED)){
UIValidationStatus goodStatus = null;
UIValidationStatus badStatus = null;
if (validationType.equals(FULL)){
goodStatus = UIValidationStatus.ENABLED;
badStatus = UIValidationStatus.DISABLED;
}
else{
goodStatus = UIValidationStatus.PERMITTED;
badStatus = UIValidationStatus.DENIED;
}
if (isNewInWorkspace(workable)){
return new UIValidationResult(validationKey, wtRef, badStatus,
null);
}
if ((AccessControlHelper.manager.hasAccess(workable, AccessPermis
sion.MODIFY)) &&
(!WorkInProgressHelper.isCheckedOut(workable) &&
(VersionControlHelper.isLatestIteration((Iterated)workable)) &&
(!SandboxHelper.isCheckedOutToSandbox(workable)))){
return new UIValidationResult(validationKey, wtRef, goodStatus,
null);
}
else{
return new UIValidationResult(validationKey, wtRef, badStatus,
null);
}
}
return new UIValidationResult(validationKey, wtRef, UIValidationStat
us.ENABLED, null);
}
private Iterator getWorkableIterator(WTCollection targetObjects)
{
try{
return targetObjects.persistableIterator(Class.forName("wt.vc.wip.
Workable"), true);
}
catch(Exception e){
return (new ArrayList(0)).iterator();
}
}
private WTReference getWTReference(Workable workable)
{
if (refFactory == null){
refFactory = new ReferenceFactory();
}
try{
return refFactory.getReference(workable);
}
catch(WTException wte){
return null;
}
}
private static boolean isNewInWorkspace(Object object)
throws WTException
{
if(object instanceof Foldered || object instanceof CabinetMember) {
WTArrayList objArray = new WTArrayList();
objArray.add(object);
if(FolderHelper.service.getObjsInPersonalCabinets(objArray).size()
> 0) {
if(EPMWorkspaceHelper.manager.getNewObjects(objArray).size() >
0) {
return true;
}
}
}
return false;
}
private UIValidationResultSet processNonWorkables(WTCollection
targetObjects, String validationKey, String validationType)
{
UIValidationResultSet resultSet = new UIValidationResultSet();
UIValidationStatus status = null;
if (validationType.equals(SELECTED))
status = UIValidationStatus.DENIED;
else
status = UIValidationStatus.HIDDEN;
try{
targetObjects.removeAll(Class.forName("wt.vc.wip.Workable"), true);
}
catch(ClassNotFoundException cnfe){
// do nothing
}
Iterator nonWorkableIter = targetObjects.referenceIterator();
WTReference wtRef = null;
while(nonWorkableIter.hasNext()){
wtRef = (WTReference)nonWorkableIter.next();
resultSet.addResult(new UIValidationResult(validationKey, wtRef, status,
null));
}
return resultSet;
}
}
已知使用
此文档应由任何负责为操作或 UI 组件编写验证器的开发人员使用。