100% found this document useful (2 votes)
3K views93 pages

Windchill Customization Document

The document provides instructions for configuring Eclipse to work with a Windchill installation. It outlines steps to install Eclipse and the FileSync plugin, run an ant task to generate an Eclipse project, and import the project into Eclipse. It also discusses adding custom actions and models to Windchill by modifying XML configuration files.

Uploaded by

Nagaraj
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (2 votes)
3K views93 pages

Windchill Customization Document

The document provides instructions for configuring Eclipse to work with a Windchill installation. It outlines steps to install Eclipse and the FileSync plugin, run an ant task to generate an Eclipse project, and import the project into Eclipse. It also discusses adding custom actions and models to Windchill by modifying XML configuration files.

Uploaded by

Nagaraj
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 93

Eclipse Configuration 

 
1. Install Eclipse(Mars 2 for WC 11). 
2. Start the Windchill.  
3. Goto windchill shell. 
4. Run the following command. Follow the procedure as it is in the windchill shell.   
 
ant -f bin/tools.xml eclipse_project.help 
 
Creates an Eclipse workspace and project for C:\ptc\Windchill_11.0\Windchill/src. Works with Eclipse 3.6.x (Helios) 
 
Requires FileSync plugin (http://andrei.gmxhome.de/filesync/index.html) 
To use: 
 
0) Install FileSync 
a) Start Eclipse 
b) Accept the default workspace (we'll, later, assign a different one) 
c) Select Help -> Install new software... 
d) Click Add... (next to "Work with:") 
e) Set Name to FileSync and Location to http://andrei.gmxhome.de/eclipse/ 
f) Click OK, then wait until the "Pending..." text disappears 
g) Type FileSync in the text are with the grayed-out text reading "type filter text" 
h) Select the FileSync for Eclipse 3.6 (currently under Eclipse 3.5 - 3.7 plugins) 
i) Click Next >, wait, then click Next > again 
j) Select the "I accept the terms of the license agreement" radio button 
k) Click Finish 
l) OK the Security Warning 
m) Click Not Now, then exit Eclipse. 
 
1) Run the eclipse_project task 
2) Launch Eclipse, specifying C:\ptc\Windchill_11.0\Windchill/../eclipse as the workspace 
3) Import the cust_Windchill_src project 
a) File -> Import... -> General -> Existing Projects into Workspace 
b) Select Browse (next to "Select root directory") and click OK 
c) Your project will be listed and checked; click Finish. 
 
 
 
 
Custom Tab Action Models​ and ​Object Action Models 
 
Prerequisite for Custom Actions and Models  

 
1. Understanding xml file and formats 
2. Basic Windchill file structure 

Action Customization Guidelines 

It is important to note the following: 

• If you have an ​<objecttype>​ element in the ​custom-actions.xml​ file, and that type exists within another ​actions.xml​ file, then actions from 
your custom file will be added to the full set of actions supported for that type (defined in the ​actions.xml​ file). 
• This ​custom-actions.xml​ is read last of all the ​actions.xml​ files, so if action names are duplicated, the one in this file will be used 

If you have a <model> element in the ​custom-actionmodels.xml ​file, and a model by that name already exists, your model will completely 
override the ones processed before your custom action models file. Be very careful in the naming of your models so that you do not wipe 
out others from files read in prior unless that is the intent. 
 
Action Framework for Windchill Client Architecture 

The action framework for the Windchill client architecture supports the ability to configure new actions and action models in the system. 
This section describes the action framework for the Windchill Client Architecture. It does not include information on how to control the display 
of the actions. For more information on controlling the display 
Intended Outcome and Objectives 
When you are finished reading this, you should understand how the action framework works, and how to register actions and action models into 
the action framework. You should also be familiar with the tools and debug settings that can help in your development. 
These topics cover the following objectives: 
• You want to add a new action that will be exposed in the user interface. 
• You want to add a new action model to the system. 
• You want to remove an action from an action model. 

Prerequisite knowledge 

• XML file structures 

Related Documentation 

• Windchill Client Architecture Wizard 


• Constructing and Rendering a Table Using the JSP Framework 
• Windchill Client Architecture Tree 
• ​Information Pages 
• ​Customization Tools Overview 
 
Defining a New Action 

Actions in the system are uniquely identified by the name of the action and the object type. They should be defined in an XML file that follows 
the structure based on ​codebase/config/actions/actions.dtd​. 
Here is an example of an action definition for the New Document window. 
<objecttype name="document" class="wt.doc.WTDocument" 
resourceBundle="com.ptc.windchill.enterprise.doc.documentResource"> 
<action name="create" uicomponent="CREATE_DOC" dtiUpload="true"> 
<command 
class="com.ptc.windchill.enterprise.doc.forms.CreateDocFormProcessor" 
method="execute" windowType="popup" onClick="validateCreateLocation(event)"/> 
<includeFilter name="projectM4D" /> 
<nonSupportedTypes value="wt.part.WTPart"/> 
<supportedTypes value="wt.doc.WTDocument"/> 
</action> 
</objecttype> 
 

objecttype Tag 

The ​objecttype​ is a way to create a name space as well as packaging for actions related to a specific object or functional area. In the above 
example, the name “document” creates a unique name space for actions that apply to ​wt.doc.WTDocuments​. 
Naming conventions for the name of an ​objecttype​ can be any combination of alpha-numeric characters. Most ​objecttypes​ are an alias for the 
persistable object to which the actions relate. Actions that apply to any object type, such as copy, can be put within the ​objecttype​ of “object”. 

PTC recommends that all custom ​objecttypes​ have a prefix specific to the company to prevent collisions with object types delivered with the 
product. 

The table below describes the valid parameters for ​objecttype​. Details about these parameters can also be found in 
codebase/config/actions/actions.dtd​. 

Parameter  Default  Possible Values  Req?  Description 


Value 

name    any combination of alpha-numeric  Yes  The name used to reference this object type. 
characters 

class    A valid Java class  Yes  Object class for the enclosed actions 

resourceBundle    Any valid resource bundle class name  No  Class name for the default resource bundle to use for the 
properties of the actions that are to be localized 

action Tag 

The action ​name​ is a unique identifier for an action within the context of the object type. The object type in conjunction with the action name 
make the action unique within the system. 
By default, the action name corresponds to the name of a JSP within the package named for the object type. The packaging is relative to 
codebase/netmarkets/jsp​. For example, the action name in the XML example above is “create.” Within the document object type, this 
corresponds to ​codebase/netmarkets/jsp/document/create.jsp​. 
Naming conventions for the name of an action can be any combination of alpha-numeric characters. 

PTC recommends that all your custom actions have a prefix specific to your company to prevent collisions with names of actions 
delivered with the product. 

The table below describes the valid parameters for ​action​. Details about these parameters can also be found in 
codebase/config/actions/actions.dtd​: 
 
Create a Tab Action Model in the Navigator in Windchill  
 

 
 
 
Steps 
 
1. Login in to Windchill as site admin and enable the client customization preference from the site admin level 

 
 
2. Check if the client customization tool is visible 

 
 
3. Click on tools and enable the jcaDebug so that in the navigation tab the xml file responsible for the customization is revealed, if the 
customization was already done the custom xml file would be ​custom-actionModels.xml  

 
 
4. In our case the files required for customization are, 
navigation-actionModels.xml 
Navigation-actions.xml ​located at​ %WT_HOME%\codebase\config\actions. 
 
5. Open both the files and check for provision for adding custom models and actions for users 

 
 
6. Copy the model from the parent files and add it to the corresponding custom*.xml files 
 

7.  
 
8. See the custom submodel added to the one copied from the ​navigation-actionModels.xml 

 
 
9. Now create the submodel definitions and actions in the custom-actions.xml 
 

10.  
 
11. Create the contents for sub actions with a jsp file, see below a sample file, 

 
12. Restart the method server and check if the actions appear. 
 

Adding Validation Logic for Your Actions 

When  creating  a  new  action,  there  might  be  logic  that  you  want  executed  to  determine  if  that  action should be enabled, disabled, or hidden in 
the  user  interface.  You  can  write  this  logic  in  a  class  called  a  validator.  See  UI  Validation  for  details  about  the  validation  service,  writing  a 
validator, and registering the validator for your action. 
nonSupportedTypes and supportedTypes Attributes 

By  specifying  ​supportedTypes  or  ​nonSupportedTypes​,  the  action  framework  either  enables  or  disables  the  action  depending  on  the  object 
types mentioned. This filtering is applied only for a customized menu. 
The  ​supportedTypes  or  ​nonSupportedTypes  filtering  is  applied  before  any  other  validation  service  class  defined 
by application developers. In other words, it is a universal filter. 
• If an action is disabled using the ​nonSupportedTypes​ attribute, then any custom validators that have been written are not executed. 
• If an action is enabled through the ​supportedTypes​ attribute, then any custom validators that have been written are invoked. 
For example: 
<objecttype name="object" class="java.lang.Object" > 
<action name="reports" enabledwhensuspended="true"> 
<command 
url="netmarkets/jsp/carambola/customization/reports/base.jsp"/> 
<nonSupportedTypes 
value="wt.doc.WTDocument,wt.part.WTPart"/> 
</action> 
</objecttype> 
Alternatively you can also use: 
<objecttype name="object" class="java.lang.Object" > 
<action name="reports" enabledwhensuspended="true"> 
<command 
url="netmarkets/jsp/carambola/customization/reports/base.jsp"/> 
<nonSupportedTypes> 
<type value="wt.doc.WTDocument" /> 
<type value=" wt.part.WTPart " /> 
</nonSupportedTypes> 
</action> 
</objecttype> 
You  can  specify  whether  this  filtering  is  applied  to  subtypes  of  an  object.  By  default,  subtypes  inherit  this  attribute.  You  can  control  this 
functionality by adding the additional attribute ​applyToDescendants​: 
<supportedTypes> 
<type value="wt.foo.Bar" applyToDescendants=”false” /> 
</supportedTypes> 
 
MCV 
Custom Action Menu for MVC Table Builder  
Display the table from the database  

 
 
Custom-actionModels.xml 
<action name="show_MVC_Table" type="object" resourceBundle="ext.test.hello.objectActionsRB"/> 
 
Custome-actions.xml 
 
<objecttype name="object" class="wt.fc.Persistable" > 
<action name="show_MVC_Table" resourceBundle="ext.test.hello.​objectActionsRB​"> 
<component name="ext.test.hello.​MVCTableBuilder​" windowType="page"/> 
</action> 
</objecttype> 
 
objectActionsRB.java 
 
package ext.test.hello; 
 
import wt.util.resource.NestableListResourceBundle; 
import wt.util.resource.RBEntry; 
import wt.util.resource.RBPseudo; 
import wt.util.resource.RBUUID; 
import wt.util.resource.RBComment; 
import wt.util.resource.*; 
 
@RBUUID("ext.test.resourceBundle.objectActionsRB") 
public class objectActionsRB extends WTListResourceBundle { 
 
@RBEntry("Show MVC Table") 
public static final String PRIVATE_CONSTANT_1="object.show_MVC_Table.description"; 
 

 
MVC Builders 
There are two types of Builders. The one that provides ComponentConfig are called ComponentConfigBuilder and the one that provides the 
ComponentData are called ​ComponentDataBuilder​. 
 
● Customization Guide• ComponentConfigBuilder : beans that implement ​com.ptc.mvc.components.ComponentConfigBuilder 
● ComponentDataBuilder : beans that implement any of the following interface 
➔ com.ptc.mvc.components.ComponentDataBuilder 
➔ com.ptc.mvc.components.ComponentDataBuilderAsync 
➔ com.ptc.mvc.components.TreeDataBuilderAsync 
 
Specifying the componentid 
Typically the component ID that a builder maps to is declared using the @ComponentBuilder annotation in your builder class declaration. 
 
@ComponentBuilder(value = “{<componetId1>, <componetId2>}”) 
public class MyBuilder extends ... {} 
 
If you wish to implement your config and data builders in separate classes, then you must supply an additional ComponentBuilderType 
parameter to the @ComponentBuilder annotation. 
 
For config builders, this looks like: 
@ComponentBuilder(value=“<componetId>", type=ComponentBuilderType.CONFIG_ONLY) public class MyConfigBuilder implements 
ComponentConfigBuilder .... {} 
 
 
 
For data builders, this looks like: 
@ComponentBuilder(value=“<componetId>", type=ComponentBuilderType.DATA_ONLY) public class MyDataBuilder implements 
ComponentDataBuilder... {} 
Note that framework will throw an error if two builders will have the same component id. Spring initialization will fail in the MethodServer 
startup phase. If you need to override an OOTB builder, please use OverrideComponentBuilder annotation. 
 
TypeBased 
To build certain components, you may need the Windchill Type of the context object playing a role in finding the appropriate builder. For 
example you have an info page and you want to populate the content based on the Windchill Type. For these scenarios, we have introduced an 
annotation “TypeBased” that can be used in the builder, to attach the builder to a specific Windchill Type. This works with ComponentBuilder. 
 
@ComponentBuilder("compIdA") 
@TypeBased(value="{WTPart, WTDocument}") 
public class OOTBBuilder1 extends ......{ 

@ComponentBuilder("compIdA") 
@TypeBased(“myPart”) 
public class OOTBBuilder2 extends ......{ 

• If the context object’s Windchill Type is WTPart and the componentId = “compIdA”, the best match builder for that component is OOTBBuilder1 
• If the context object’s Windchill Type is a sub-type of WTPart(myPart) and the componentId = “compIdA”, the best match builder for that 
component is OOTBBuilder2 
• If the context object’s Windchill Type is a sub-type of WTDocument(myDoc) and the componentId = “compIdA”, the best match builder for that 
component is OOTBBuilder1 While resolving to find the suitable builder, the context object’s Windchill type hierarchy is respected. The attribute 
value can take 
• Internal Name of the Windchill Type 
• If the representation has the domain name of the exchange container involved, then can use ${internet_domain_name} for its representation. 
e.g 
${internet_domain_name}.DynamicDocument 
 

Registering Builders 

Once the builders are created, you need to let Spring infrastructure know about it. Registration can be done either via explicit configuration or 
by automated scanning. Automated scanning is the preferred approach for typical use cases. 
1. Automated scanning: You can configure to automatically pick up all builders within a certain package 
hierarchy. To do this, add the ​<mvc:builder-scan/>​ configuration element to ​<​Windchill​>\codebase\config\mvc\custom.xml​. 
 
<beans xmlns="xmlns:mvc="http://www.ptc.com/schema/mvc" xsi:schemaLocation="http://www.ptc.com/schema/mvc 
http://www.ptc.com/schema/mvc/mvc-10.0.xsd"> 
<mvc:builder-scan base-package="com.ptc.windchill.enterprise.preference.mvc.builders"/> 
</beans> 
 
The  builder-scan  implementation  scans  the  entire  classpath,  so  you  should  take  care  to  be  specific  with  the  package  name  you  declare  if 
using  scanning.  (don’t  scan  com.ptc.*  for  example).  In  addition,  this  means  that  classes  that  are  outside  your  interest,  but  that  are  in  the 
classpath  and  match  the  package  hierarchy,  will  also  get scanned. From a performance standpoint (MethodServer startup) each scan adds 
up  time.  You  are  requested  to  take  advantage  of  the  OOTB  scan  provided  on  “com.ptc.mvc.builders”  base  package.  This  means that all the 
builders  that  you  author  should  be  under  com.ptc.mvc.builders package. If you are not using the OOTB scan, the rule of thumb is to use the 
scan if there are more than 10 builders available in the package. 
 
Explicit configuration : To do this, simply add a bean declaration for the builder to the ​<​Windchill​>\codebase\config\mvc\custom.xml 
e.g ​<bean class=" my.builder.class.name "/> 
You are encouraged not to specify the bean name while declaring the bean and use ​@ComponentBuilder​ annotation in the builder for 
proper registration of it in the Spring bean factory. 
 
 
Example Program 
MVCTableBuilder.java 
 
package ext.test.hello; 
import java.lang.Object; 
import com.ptc.mvc.components.AbstractComponentConfigBuilder; 
import com.ptc.mvc.components.AbstractComponentBuilder; 
import com.ptc.mvc.components.ComponentBuilder; 
import com.ptc.mvc.components.ComponentConfig; 
import com.ptc.mvc.components.ComponentConfigFactory; 
import com.ptc.mvc.components.ComponentParams; 
import com.ptc.mvc.components.TableConfig; 
import com.ptc.windchill.annotations.metadata.SupportedAPI; 
 
import wt.fc.PersistenceHelper; 
import wt.part.WTPart; 
import wt.query.QuerySpec; 
import wt.query.SearchCondition; 
import wt.util.WTException; 
 
@ComponentBuilder("ext.test.tables.MVCTableBuilder") 
public class MVCTableBuilder extends AbstractComponentBuilder { 
 
@Override 
public Object buildComponentData(ComponentConfig config, ComponentParams params) throws Exception { 
   
QuerySpec query = new QuerySpec(WTPart.class); 
// You can specify the search condition according to you 
query.appendWhere(new SearchCondition(WTPart.class,WTPart.NAME,SearchCondition.LIKE,"%P%"), null); 
return PersistenceHelper.manager.find(query); 

 
@Override 
public ComponentConfig buildComponentConfig(ComponentParams arg0) throws WTException { 
ComponentConfigFactory factory = getComponentConfigFactory(); 
TableConfig table = factory.newTableConfig(); 
 
table.setLabel("Selected Parts"); 
table.setSelectable(true); 
 
table.addComponent(factory.newColumnConfig("name", true)); 
table.addComponent(factory.newColumnConfig("number", true)); 
table.addComponent(factory.newColumnConfig("type", true)); 
table.addComponent(factory.newColumnConfig("thePersistInfo.modifyStamp", true)); 
table.addComponent(factory.newColumnConfig("thePersistInfo.createStamp", true)); 
 
return table; 


 
Custom.xml 
 
<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:mvc="http://www.ptc.com/schema/mvc"  
xsi:schemaLocation=" 
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd 
http://www.ptc.com/schema/mvc http://www.ptc.com/schema/mvc/mvc-10.0.xsd"> 
 
<!-- Configurations in this file override all other configurations --> 
<mvc:builder-scan base-package="ext.test.hello"/> 
<bean class="ext.test.hello.MVCTableBuilder" /> 
<bean class="ext.test.hello.CSMApproverMatrixStepTableBuilder" /> 
</beans> 
 
Another MVC Table Builder 
Custom-actionModels.xml 
 
<action name="Matrix_Step" type="object" resourceBundle="ext.test.hello.matrixActionsRB"/> 
 
Custom-action.xml 
 
<objecttype name="object" class="wt.fc.Persistable" > 
<action name="Matrix_Step" resourceBundle="ext.test.hello.matrixActionsRB"> 
<component name="ext.test.hello.CSMApproverMatrixStepTableBuilder" windowType="page"/> 
</action> 
</objecttype> 
 
CSMApproverMatrixStepTableBuilder.java 
 
package ext.test.hello; 
 
import com.ptc.jca.mvc.components.JcaTableConfig; 
import com.ptc.mvc.components.AbstractComponentBuilder; 
import com.ptc.mvc.components.ColumnConfig; 
import com.ptc.mvc.components.ComponentBuilder; 
import com.ptc.mvc.components.ComponentConfig; 
import com.ptc.mvc.components.ComponentConfigFactory; 
import com.ptc.mvc.components.ComponentParams; 
 
import wt.util.WTException; 
@ComponentBuilder("ext.test.hello.CSMApproverMatrixStepTableBuilder") 
 
public class CSMApproverMatrixStepTableBuilder extends AbstractComponentBuilder { 
@Override 
public Object buildComponentData(ComponentConfig arg0, ComponentParams arg1) throws Exception { 
 
return null; 

 
@Override 
public ComponentConfig buildComponentConfig(ComponentParams arg0) throws WTException { 
ComponentConfigFactory factory = getComponentConfigFactory(); 
JcaTableConfig table = (JcaTableConfig) factory.newTableConfig(); 
table.setLabel("Approver Matrix"); 
table.setSelectable(true); 
   
ColumnConfig columnConfig = factory.newColumnConfig("Approval Matrix Description", true); 
columnConfig.setInfoPageLink(false); 
//columnConfig.setDataUtilityId(""); 
columnConfig.setDefaultSort(true); 
table.addComponent(columnConfig); 
 
columnConfig = factory.newColumnConfig("Reviewer", true); 
columnConfig.setInfoPageLink(false); 
//columnConfig.setDataUtilityId(""); 
columnConfig.setDefaultSort(true); 
table.addComponent(columnConfig); 
   
columnConfig = factory.newColumnConfig("Content Approver", true); 
columnConfig.setInfoPageLink(false); 
//columnConfig.setDataUtilityId(""); 
columnConfig.setDefaultSort(true); 
table.addComponent(columnConfig); 
 
columnConfig = factory.newColumnConfig("Customer Approver", true); 
columnConfig.setInfoPageLink(false); 
//columnConfig.setDataUtilityId(""); 
columnConfig.setDefaultSort(true); 
table.addComponent(columnConfig); 
 
columnConfig = factory.newColumnConfig("Process Compliance Approver", true); 
columnConfig.setInfoPageLink(false); 
//columnConfig.setDataUtilityId(""); 
columnConfig.setDefaultSort(true); 
table.addComponent(columnConfig); 
 
return table; 

 

 
Custom.xml 
 
<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:mvc="http://www.ptc.com/schema/mvc"  
xsi:schemaLocation=" 
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd 
http://www.ptc.com/schema/mvc http://www.ptc.com/schema/mvc/mvc-10.0.xsd"> 
 
<!-- Configurations in this file override all other configurations --> 
<mvc:builder-scan base-package="ext.test.hello"/> 
<bean class="ext.test.hello.MVCTableBuilder" /> 
<bean class="ext.test.hello.CSMApproverMatrixStepTableBuilder" /> 
</beans> 
 
 
 
Listener Service to capture a check-in Event 
Creating Non-Modeled Services for Listening 

Here are the steps necessary to create a non-modeled service for listening: 
1. Create a service interface 
2. Create a standard service class to implement your service interface and extend the StandardManager class. 
3. Compile your interface and class into your Windchill codebase. 
4. Register your new service in the codebase with xconfmanager. 
Below  is  an  example  to  create  a  service  that  listens  for  pre-  and  ​post-checkin  events.  When  it  "hears"  one  of  these  events,  it  prints  a  simple 
message  to  standard  output.  The  example  assumes  the service is in a package called com.acme.listen, and that the service is called the "Listen" 
service.  
Use below steps to create new listener 
1. Create new interface as ​xxxxxService 

​package​ PTC.Listener;  

public​ ​interface​ MyListenerService {  

1.  Create  new  class  for  Standardservice  class  ​StandardxxxxService  ​which  implements  service  interface  created  in  step1  and  extends 
StandardManager class 

package​ PTC.Listener; 

public​ ​class​ StandardMyListenerService ​extends​ StandardManager ​implements​ MyListenerService { 

2. Create constructor of class type and return instance 

  ​public​ ​static​ ​StandardMyListenerService​ newStandardMyListenerService() ​throws​ WTException 



  StandardMyListenerService instance = ​new​ StandardMyListenerService(); 
  instance.​initialize​(); 
  ​return​ instance; 

3. Create internal class for Listening event StandardXXXXXEventListener which extends ServiceEventListenerAdapter class  

class​ StandardMyListenerServiceEventListener ​extends​ ServiceEventListenerAdapter 



  
    ​public​ StandardMyListenerServiceEventListener (String manager_name) { 
      ​super​ (manager_name); 
    } 
  
}   

4. Create ​performStartupProcess​ Method to and call getManagerService().addEventListener() Method to listen P


​ RE_MODIFY​ event  

protected​ ​synchronized​ ​void​ performStartupProcess( ) { 


    getManagerService().addEventListener 
  (​new​ ServiceEventListenerAdapter(​getConceptualClassname​())​{ 
  ​public​ ​void​ notifyVetoableEvent(Object obj) ​throws​ Exception  { 
  KeyedEvent persistencemanagerevent = (KeyedEvent)obj; 
  ​@SuppressWarnings​(​"unused"​) 
  String s = persistencemanagerevent.getEventKey(); 
  
/* Checkif event is ​PRE_MODIFY​*/ 
    ​if​ ( ((KeyedEvent)obj).getEventType().equals(PersistenceManagerEvent.​PRE_MODIFY​) ){ 
   
/*Write business logic here for event​*/ 
   
System.​out​.println(​"\n*\n*\n*\n*\n* ----Event---\n*\n*\n*\n*\n*"​); 
   
  } 
  } 
  ​}​,PersistenceManagerEvent.​generateEventKey​(​"POST_CHECKIN"​) 
  ); 

/*  to pass event key PersistenceManagerEvent.​generateEventKey​(​"PRE_MODIFY”)​*/ 


4. Register service and listener in wt.properties using xconfmanager command  

xconfmanager  –s 
”wt.services.service.​65001​=com.ptc.windchill.enterprise.datamonitor.DataMonitorService/com.ptc.windchill.enterprise.datamonitor.StandardData
MonitorService” -t codebase\wt.properties -p 

Note: You can select any random number instead on 65001 for registering service/Listener make sure that number is not already in used 
  
you can set below verbosity to true check event keys   
wt.services.verbose 
wt.services.verboseEvents 
 
There  is  no  restriction  to  copied/place  listener  in  specific  folder.  You  can  store  listener  in  any  folder  you  want  in  Windchill  you  can  place 
source java file of listener in windchill/src/ directory and use below command to compile 
  
ant -f bin/tools.xml class -Dclass.includes=/ PTC/Listener/** 
 
Example listener Program 
 
StandardListenService.java 
 
package ext.cust.services; 
 
import ext.cust.services.ListenService; 
import wt.services.ManagerException; 
import wt.services.ServiceEventListenerAdapter; 
import wt.services.StandardManager; 
import wt.session.SessionContext; 
import wt.session.SessionHelper; 
import wt.util.DebugProperties; 
import wt.util.DebugWriter; 
import wt.util.WTException; 
import wt.vc.wip.*; 
 
import java.lang.String; 
 
public final class StandardListenService extends StandardManager implements ListenService { 
 
private static final String CLASSNAME = StandardListenService.class.getName(); 
private static final boolean DEBUG = DebugProperties.isDebugOn(CLASSNAME); 
private static final DebugWriter LOG = (DEBUG ? DebugProperties.getWriter(CLASSNAME) : null); 
public String getConceptualClassname() { 
return CLASSNAME; 

 
public static StandardListenService newStandardListenService() throws WTException { 
StandardListenService instance = new StandardListenService(); 
instance.initialize(); 
return instance; 

 
protected void performStartupProcess() throws ManagerException { 
 
if (DEBUG && DebugProperties.isTrace(this)) 
LOG.enter(CLASSNAME, "performStartupProcess"); 
SessionContext prev = SessionContext.newContext(); 
try { 
SessionHelper.manager.setAdministrator(); 

catch (WTException wte) { 
System.err.println("StandardListenService: failed to set Administrator (ok if Installation)"); 
return; 

finally { 
SessionContext.setContext(prev); 

 
getManagerService().addEventListener(new ServiceEventListenerAdapter(this.getConceptualClassname()) { 
public void notifyVetoableEvent(Object event) throws WTException { 
final Workable target = ((WorkInProgressServiceEvent)event).getOriginalCopy(); 
System.out.println("Listen Hears Pre-Checkin:"); 
System.out.println(" target:"); 
System.out.println(target.toString()); 

}, 
WorkInProgressServiceEvent.generateEventKey (WorkInProgressServiceEvent.POST_CHECKIN)); 
if (DEBUG && DebugProperties.isTrace(this)) 
LOG.exit(CLASSNAME, "performStartupProcess"); 


 
Create a interface called ListenService (let it be empty) 
package ext.cust.services; 
 
public interface ListenService { 
 

 
Create a service in wt.properties as below, 
 
<Property name="wt.services.service.4160" overridable="true" 
targetFile="codebase/wt.properties" 
value="ext.cust.services.ListenService/ext.cust.services.StandardListenService"/> 
 
Add the above property in ​site.xconf​ and run the command​ xconfmanager -p​ in the windchill shell 
 
Restart the method server 
 
Create a Custom Form Wizard  
 
Wizard Processing 

Objective 

You have created a JSP wizard to gather information from a user about one or more object(s). You now need to create the code to process that 
information and perform a database operation(s) on the object(s). 

Background 

If  your  wizard  uses  one  of  the  built-in  button  sets,  when  a  user clicks the Finish, Apply, Save, or Check In button to submit the form, a javascript 
function  is  called  that  invokes  the  ​processRequest()  method  of  the  ​ActionController​.  The  ​WizardServlet  loads  the  HTTP  form  data  and  other 
wizard  context  information,  such  as  where  the  wizard  was  launched,  into  a  ​NmCommandBean​.  It  then  passes  the 
NmCommandBean​ to the ​FormDispatcher​ class. The ​FormDispatcher​ calls the F ​ ormProcessorController​. 
The  ​FormProcessorController  partitions  the  form  data  into  ​ObjectBeans​.  One  ​ObjectBean  is  created  for  each  target  object  of  the  wizard.  It 
contains  all the form data specific to that object and any form data that is common to all objects. The ​FormProcessorController then passes the 
ObjectBeans  to  classes  called  ​ObjectFormProcessors  that  perform  the  tasks appropriate to the wizard --- for example, creating a new object in 
the  database,  updating  an  object  in  the  database,  or  checking  in  an  object  ​ObjectFormProcessors​,  in  turn,  can  call  classes  called 
ObjectFormProcessorDelegates​ to perform one or more subtasks. 
If  your  wizard  is  performing  an  operation  on  a  single  object  you  may  need  to  create  your  own  ​ObjectFormProcessor  and/or 
ObjectFormProcessorDelegates  to  perform the tasks specific to your wizard. However, if your wizard is creating or editing an object, you may be 
able to take advantage of some processors that are delivered with the product for those purposes. 
If your wizard has multiple target objects you may or may not also need to create your own ​FormProcessorController​ to control the order in 
which objects are processed. 

Scope/Applicability/Assumptions 

Assumes that you have already created the necessary JSPs, data utilities, GUI components, and renderers to display your wizard. Also assumes 
that you have created the necessary actions to hook up your wizard to the UI. 

Intended Outcome 

Perform a database operation(s) related to one or more Windchill object. 

Solution 

Use the JSP client architecture framework and common components to process wizard form data and perform the appropriate database tasks 
for one or more object(s). 

Prerequisite knowledge 

To achieve this objective, you need to have an understanding of the following: 


• Java programming 
• Basic web development using HTML forms 
• Familiarity with the Windchill service APIs or other APIs necessary to perform the tasks appropriate to the wizard 
Definition of terms used in this section: 

Term  Definition 
target  Object(s) for which you are gathering data in your wizard. Some operation(s) will typically be performed on these 
object  objects in your wizard processing. 

Solution Elements 

Element  Type  Description 

ActionController  Java class  This is the class to which wizard form data gets posted and which sends the 
response page sent back to the browser after processing completes. 

Runtime location: <​Windchill​>/srclib/CommonComponents-web.jar 

FormProcessorController  Java  Classes implementing this interface instantiate and call ObjectFormProcessor(s) 
interface  to execute wizard tasks. 

Runtime location: <​Windchill​>/srclib/CommonComponents.jar 

DefaultFormProcessorController  Java class  A default implementation of FormProcessorController that should be sufficient 


for all single-object wizards. This controller partitions the HTML form data into 
ObjectBeans and passes those beans to ObjectFormProcessors. 

Wizards with multiple target objects may need to extend this class to control the 
order in which objects are processed. 

Runtime location: <​Windchill​>/srclib/CommonComponents.jar 


ObjectFormProcessor  Java  Classes implementing this interface perform the database and related tasks 
interfacer  appropriate to the wizard using the form data. Each wizard will have only one 
ObjectFormProcessor class but multi-object wizards may have multiple instances 
of that class. 

ObjectFormProcessors may call ObjectFormProcessor Delegates to perform 


subtasks. 

Runtime location: <​Windchill​>/srclib/CommonComponents.jar 

DefaultObjectFormProcessor  Java class  A default implementation of ObjectFormProcessor that contains the logic to 
execute ObjectFormProcessorDel egates and perform several other common 
tasks. This is the base class that should be extended by wizard-specific 
processors. 

Runtime location: <​Windchill​>/srclib/CommonComponents.jar 

ObjectFormProcessrDelegate  Java  Classes implementing this interface are called by ObjectFormProcessors to 
interface  perform processing subtasks. Multiple ObjectFormProcessorDel egates may be 
called by one processor and the same delegate may be used by multiple 
processors to handle a task common to multiple wizards. These are optional. 

Runtime location: <​Windchill​>/srclib/CommonComponents.jar 

DefaultObjectFormProcessorDelegate  Java class  A default implementation of ObjectFormProcessorDelegate. This provides no-op 


behavior for methods a subclass may not need to implement. This is the base 
class that should be extended by task-specific delegates. 

Runtime location: <​Windchill​>/srclib/CommonComponents.jar 


ObjectBean  Java class  A container for the form data specific to a specific target object and the data 
common to all objects. Provides methods to retrieve the form data for a given 
object. 

Runtime location:<​Windchill​>/srclib/CommonComponents.jar 

ProcessorBean  Java class  A container for ObjectBeans that knows which ObjectBeans should be processed 
by the same processor instance and the order in which they should be processed. 

Runtime location: <​Windchill​>/srclib/CommonComponents.jar 

FormResult  Java class  A class used to pass method results between server methods and from the server 
to the WizardServet. 

Runtime location: <Windchill>/srclib/CommonComponents.jar 

The relationship between the main Java classes in the wizard processing framework is shown in the UML diagram below. 

Form Processing Task Flow 


Frequently, multiple tasks are performed on wizard target object(s) when processing the form data. These typically 
follow the following sequence: 
1. ​Preprocessing 
During this phase the setup for the database operation is done. For example: 
◦ for an object creation wizard, instances of the object(s) are created and their attributes set from values in the form data 
◦ for an object edit wizard, the object(s) are retrieved from the database and their attributes are modified per the values in the form data 
2. Start a database transaction block 
3. Do the database operation. For example: 
◦ for an object creation wizard, the object(s) would be stored in the database 
◦ for an object edit wizard, the object(s) would be updated in the database 
4. ​Postprocessing 
Do database or other tasks that need to be done after the main database operation but within the same transaction block so that the 
database operation will be rolled back if these task fail. These tasks typically require that the target object(s) be persisted beforehand. For 
example: 
◦ share the object(s) to another container 
◦ submit the object(s) to a workflow 
5. End the database transaction block 
6. Post-transaction processing 
Do tasks that need to be done after the transaction block is closed so that the entire database transaction will not be rolled back if the 
tasks fail. For example: 
◦ check out an object 
Additional database operations may be performed in this phase if desired. 
The  tasks  in  each  processing  phase  are  performed  by  ​ObjectFormProcessors  and  ​ObjectFormProcessorDelegates​.  Every  wizard  must  have  an 
ObjectFormProcessor  which  should  extend  the  ​DefaultObjectFormProcessor​.  This  is  the  primary  class  that  controls  and  performs  the 
processing tasks appropriate to the wizard. 
​ bjectFormProcessorDelegates  that  are  called  by  the  ​ObjectFormProcessor  (via  the 
Wizards  may  or  may  not  have  one  or  more  O
DefaultObjectFormProcessor​)  to  perform  processing  subtasks.  ​ObjectFormProcessorDelegates  should  extend  the 
DefaultObjectFormProcessorDelegate​ class.  
Both  ​ObjectFormProcessor  and  ​ObjectFormProcessorDelegate  classes  have  ​preProcess()​,  ​doOperation()​,  ​postProcess()​,  and 
postTransactionProcess()  methods  for  carrying  out  tasks  during  each  processing  phase. Not every wizard will have tasks in every phase, so it is 
not  necessary  that  every  processor  and  every  delegate  implement  all  of  these  methods,  if  they  extend  from  the  default  classes 
DefaultObjectFormProcessor  and  ​DefaultObjectFormProcessorDelegate​,  respectively.  Those  parent classes provide default implementations of 
each  method.  If  an  ​ObjectFormProcessor  does  implement  one  of  these  methods,  it  should  call  the  ​super()  method 
of the ​DefaultObjectFormProcessor​, which will handle the calling of ​ObjectFormProcessorDelegates​. 
The HTML form data will be passed to ​ObjectFormProcessors​ and ​ObjectFormProcessorDelegates​ in a list of ​ObjectBeans​. An ​ObjectBean 
contains the data specific to one target object and the data common to all the target objects. For wizards with a single target object the list will 
contain only one ​ObjectBean​. For wizards with multiple target objects the list will contain one ​ObjectBean​ for each target object and the 
ObjectBeans​ may be organized into a tree structure representative of the relationship between the objects and the order in which they should 
be processed. The creation of ​ObjectBeans​ is handled by the ​FormProcessorController​. 
The ​FormProcessorController​ also handles the opening, closing, and, if necessary, rollback of the database transaction and the calling of the 
ObjectFormProcessors​. For the latter, the ​DefaultFormProcessorController​ uses objects called ​ProcessorBeans​. Target objects with the same 
parent object, which are of the same type, and which have the same ​ObjectFormProcessorDelegates​ are placed into the same ​ProcessorBean​. 
Each ​ProcessorBean​ also will have its own instances of the ​ObjectFormProcessor​ and ​ObjectFormProcessorDelegates​ for the ​ObjectBeans​ it 
contains. ​ProcessorBeans​ may be organized into a tree structure to control the order in which objects are processed. (Wizards with a single 
target object will have only one ​ProcessorBean​. ) 
The task flow of the ​DefaultFormProcessorController​ is as follows: 
1. Call the ​preProcess()​ method of ​ObjectFormProcessor​ for the root ​ProcessorBean​, passing it the ​ObjectBeans​ in the ​ProcessorBean​. Then 
call the ​preProcess()​ method of the processors for the children of the root ​ProcessorBean​, in the order in which they appear in the child 
list. Then call the ​preProcess()​ method for the children of the children, and so forth. 
2. Do ​Transaction.start() 
3. Call the ​doOperation()​ method of the ​ObjectFormProcessor​ for the root ​ProcessorBean​ and its children in the same way as ​preProcess()​. 
4. Call the ​postProcess()​ method of the ​ObjectFormProcessor​ for the root ​ProcessorBean​ and its children in the same way as ​preProcess()​. 
5. If steps 1-4 are successful, do ​Transaction.commit() 
6. Call the ​postTransactionProcess()​ method of the ​ObjectFormProcessor​ for the root ​ProcessorBean​ and its children in the same way as 
preProcess()​. 
If any method returns a status of ​FormProcessingStatus.FAILURE​, the controller will call the ​setResultNextAction()​ method of the 
ObjectFormProcessor​ that failed so it can set information needed for the HTML response page. 

  ObjectFormProcessors​ should not open/commit additional transaction blocks in steps 3 or 4 as nesting of transactions is not 
recommended. 

Wizards with Multiple Target Objects 

Two types of multiple-object wizards are supported: 


• wizards with multiple unrelated target objects 
Example: create multiple parts 
• wizards with a tree of related target objects 
Example: create a change notice and related change tasks 
The data specific to a given object should be contained either in a table row specific to that object or in a wizard step specific to that object. An 
example table where each row represents a part being created and each column is an attribute of the part is shown below. 

 
Each wizard step must display one of these types of data: 
• data in tabular format where each row represents a different object 
• data that is specific to one and only one of the objects created 
• data that is common to all objects created 
A step cannot contain object-specific data for multiple objects unless it is in tabular format. 
In multiple-object wizards, the object to which an input field applies is identified by an "​objectHandle​" embedded in the name attribute of the 
HTML input field. For example: 
<input id="null1188140328133" 
name="<someFieldIdString>!~objectHandle~newRowObj_430512131997223~! 
<someAdditionalText>" value="" size="60" maxlength="60" 
type="text"> 
In the example above, "​newRowObj_430512131997223​" is the object handle, "​!~objectHandle~​" is the required prefix, and "~!" is the required suffix. 
The HTML name attribute in which the object handle is embedded can be any string and the object handle may appear anywhere within the 
string. 
When the ​DefaultFormProcessorController​ loads the form data in to ​ObjectBeans​, it will strip off the object handle (including the required prefix 
and suffix) from the name attribute and use the resulting string as the key for the value in the form data parameter maps. For example, to 
retrieve the form value for the input field above you would call ​ObjectBean.getTextParameter()​ with the following key: 
<someFieldIdString><someAdditionalText> 
The framework generates object handles on name attributes for you in one of two ways: 
• If data for the objects is being captured in tabular format, where each row represents an object and each column an attribute of the 
object, the handle will be dynamically generated for you if you set ​rowBasedObjectHandle=true​ on the table config in the builder:: 
table.setRowBasedObjectHandle(true); 
The object handle for each row will be based on the row’s OID. 
objectHandle = CreateAndEditWizBean.​getNewObjectHandle​(next.getOid().toString()); 
• Where all the data on a given wizard step is for the same object, you specify the object handle for that object on the wizard step action: 
<jca:wizardStep action="setContextWizStep" type="object" 
objectHandle="<your object handle string>" … 
If data on the wizard step is common to all objects created, no object handle is needed on the input fields. The object handle associated to the 
data in an ​ObjectBean​ can be accessed by the ​ObjectBean.getObjectHandle()​ method. The ​FormProcessorController​ controls the order in 
which processors are called to process the ​ObjectBeans​, as described in the sections below. Note that in the illustrations below, circles are 
used to represent ​ObjectBeans​. Circles representing objects of the same type will have the same shading. 

Multiple unrelated target objects 

Typically, when a wizard has multiple unrelated target objects, the objects are the same type: 

 
An example of such a wizard is that in which you can create multiple parts. This wizard has three steps: 
1. Define Part 
User enters the type of parts to be created and other attributes common to all the parts being created. 
2. Set Attributes 
User enters the name and number of each part to be created in tabular format. This table is dynamic in that the user can enter any number 
of parts. 
3. Set Additional Attributes 
User enters some additional attributes common to all of the parts. 
If the user enters data for five parts, the ​DefaultObjectFormProcessorController​ will create five ​ObjectBeans​. Each ​ObjectBean​ will contain the 
data from step 2 specific to the part it represents and the data from steps 1 and 3. Because there is no relationship between the parts and they 
can be created independently, none of the ​ObjectBeans​ will have parents or children. Since the same ​ObjectFormProcessor​ and 
ObjectFormProcessorDelegates​ will be used to process all the ​ObjectBeans​ and all the objects are of the same 
type, they will all be placed in the same ​ProcessorBean​. 

Multiple related target objects 

Other wizards may have multiple related target objects. For example, you might have a wizard that creates a change notice and the change 
tasks related to that change notice. To create the associations between the change notice and the change tasks, the processor for the change 
notice will need to know how the objects relate to each other. 

 
The change notice ​ObjectBean​ has three child ​ObjectBeans​. The change task ​ObjectBeans​ have a parent ​ObjectBean​ and no children. 
In this case, you would need to write your own ​FormProcessorController​ to create the structure of ​ObjectBeans​. This can be a subclass of the 
DefaultFormProcessorController​. 
The default controller will create the ​ObjectBeans​ for you. You would override its ​createObjectBeanStructure()​ method, which is given a flat list 
of all the ​ObjectBeans​. In that method you would set the parents and children of the ​ObjectBeans​. You pass back a list of all the root 
ObjectBeans​. After you have created the ​ObjectBean​ structure, the ​DefaultFormProcessorController​ will call the ​ProcessorBean.newCollection() 
method which will group the ​ObjectBeans​ into ​ProcessorBeans​ as follows: 
 
In the diagram above the circles represent the ​ObjectBeans​ and the solid lines the relationships between them. The rectangles represent the 
two ​ProcessorBeans​ and the dotted line the relationship between them. 
Each ​ProcessorBean​ will have its own instances of the ​ObjectFormProcessor​ and the ​ObjectFormProcessorDelegates​ needed for the objects in 
it. If the processor for the root ​ProcessorBean​ is called "​ProcessorInstance1​" and the processor for the child ​ProcessorBean​ is called 
"​ProcessorInstance2​", the processor methods would be called as follows: 

  Method  Task Performed 

1  ProcessorInstance1.preProcess(ObjectBean in Processor  create an instance of a WTChangeOrder2 and store it in the "object" 


Bean 1)  attribute of the bean 

2  ProcessorInstance2.preProcess(ObjectBeans in Processor  create three instances of WTChangeActivity2 and store them in the 
Bean 2)  "object" attributes of the beans 

3  ProcessorInstance1.doOperation(ObjectBean in Processor  persist the WTChangeOrder2 


Bean 1) 

4  ProcessorInstance2.doOperation(ObjectBeans in  persist the WTChangeActivity2 instances 


Processor Bean 2) 
5  ProcessorInstance1.postProcess(ObjectBean in Processor  create the associations between the change notice and the change 
Bean 1)  tasks 

6  ProcessorInstance2.postProcess(ObjectBeans in Processor  none 


Bean 2) 

7  ProcessorInstance1.postTransactionProcess(ObjectBean in  none 


Processor Bean 1) 

8  ProcessorInstance2.postTransactionProcess(ObjectBeans  none 
in Processor Bean 2) 

The tasks could be arranged differently. For example, you could create the associations in method 6 instead of method 5 with the same effect. 
Or, you could create an ​ObjectFormProcessorDelegate​ to create the associations in its ​postProcess()​ method. The framework offers the 
flexibility to modularize your code as best fits your wizard. Just be sure to arrange your tasks correctly relative to the start and end of the main 
transaction. 
Your structure of ​ObjectBeans​ could be more complex. For example: 
 
As you can see from the diagram above, ​ObjectBeans​ will be placed in different ​ProcessorBeans​ if any of the following is true: 
• the object in the ​ObjectBeans​ are different types 
• the ​ObjectBeans​ have a different ​ObjectFormProcessor 
(Note: at this time all the ​ObjectBeans​ in the wizard must have the same ​ObjectFormProcessor​.) 
• the ​ObjectBeans​ have a different list of ​ObjectFormProcessorDelegates 
• the ​ObjectBeans​ have a different parent ​ObjectBean 
The ​DefaultFormProcessorController​ will call the processors associated with each ​ProcessorBean​ starting at the root ​ProcessorBean​ and 
proceeding down the tree to the leaf ​ProcessorBeans​. 

Procedure - Creating Your Wizard Processing Code 

You have two options in creating your wizard processing code: 


• ​Wizards with a Single Target Object 
• ​Wizards with Multiple Target Objects 

Wizards with a Single Target Object 

This process consists of the following steps: 


• ​Create your processor class 
• ​Specify the processor class for the wizard on the wizard action 
• ​Create any necessary ObjectFormProcessorDelegate classes for your wizard 
• ​Specifying the ObjectFormProcessorDelegate(s) to be used in your wizard 

Create your processor class 

Three processors for handling object creation and editing wizards are delivered with the product: 
• ​com.ptc.core.components.forms.CreateObjectFormProcessor 
• ​com.ptc.core.components.forms.DefaultEditFormProcessor 
• ​com.ptc.core.components.forms.EditWorkableFormProcessor 
These processors may be used as is or extended for your own purposes. 
If your wizard is not an object creation or editing wizard you will need to create your own ​ObjectFormProcessor​. ​ObjectFormProcessors​ should 
extend the ​DefaultObjectFormProcessor​ class. 
You  should  place  your  form  processing  logic  into  the  ​preProcess()​,  ​doOperation()​,  ​postProcess()​,  and  ​postTransactionProcess()  methods  of  the 
processor,  as  appropriate.  Your  methods  should  call  the  corresponding  super  method  of  the  ​DefaultObjectFormProcessor​,  which  will  handle 
the  calling  of  ​ObjectFormProcessorDelegates​.  These  methods  will  be  passed  a  single  ​ObjectBean​,  which  will  contain  all the form data from the 
wizard. The form data can be accessed using the getter methods of that object. The following getter methods are commonly used: 
public Map<String,List<String>> getChangedComboBox() 
public Map<String,String> getChangedRadio() 
public Map<String,String> getChangedText() 
public Map<String,String> getChangedTextArea() 
public Map<String,List<String>> getChecked() 
public Map<String,List<String>> getUnChecked() 
public List getRemovedItemsByName(String paramName) 
public List getAddedItemsByName(String paramName) 
public String getTextParameter(String key) 
public String[] getTextParameterValues String key) 
See the javadoc for more information. 
The "object" attribute of the ​ObjectBean​ represents an instance of the target object . Where appropriate, it should be set by one of your 
processor methods (most likely ​preProcess()​) for use by downstream methods. Other information can be passed from one method to another 
using processor instance variables. 
The ​NmCommandBean​ object passed to these methods contains information about the page from which the wizard was launched and the 
object selected on the parent page when the wizard was launched. It also contains all the HTML form data but you should use the methods on 
the ​ObjectBean​ rather than the ​NmCommandBean​ to access that data. See the javadoc for ​NmCommandBean​ for more information. 
You pass the outcome of the ​preProcess()​, ​doOperation()​, ​postProcess()​, and p
​ ostTransactionProcess()​ methods back to the 
DefaultFormProcessorController​ using the ​com.ptc.core.component.FormResult​ object. Before returning, each of these methods should call 
FormResult.setStatus()​ to return the processing status. Three options are available: 
• ​FormProcessingStatus.SUCCESS​ - if the method executed without error 
• ​FormProcessingStatus.FAILURE​ - if the method encountered a fatal errors ​FormProcessingStatus.NON_FATAL_ERROR​ - if the method 
succeeded but encountered one or more problems that should be reported to the user. 
The ​DefaultFormProcessorController​ passes the returned ​FormResult​ to its ​continueExecuting()​ method to determine whether processing 
should continue to the next phase or be aborted. By default, it will abort only if the status is ​FormProcessingStatus.FAILURE​. If processing is to 
be aborted or after all processing competes successfully, the controller will call the ​setResultNextAction()​ method of the ​ObjectFormProcessor 
to set information in the ​FormResult​ needed to construct the response page sent back to the browser. 
This method should convey the following information: 
• The feedback messages should be displayed to the user, if any. 
Determined from the ​feedbackMessages​ and exceptions variables. 
Feedback messages, if any, are displayed before executing any window operations or provided javascript. 
Exception messages are only displayed if status is ​FormProcessingStatus.FAILURE​ or ​FormProcessingStatus.NON_FATAL_ERROR​. 
See the javadoc for the ​FormResult​ and ​FormProcessingStatus​ classes for more information. 
It is also possible for your ​ObjectFormProcessor​'s ​preProcess()​, ​doOperation()​, ​postProcess()​, and ​postTransactionProcess()​ methods to throw 
exceptions. In that case, control will be returned to the ​ActionController​ which will set the variables of the ​FormResult​ as follows: 
• status - FormProcessingStatus.FAILURE 
• exceptions - the thrown Exception 
This will cause the response page to display the exception message in an alert window and then close the wizard 
window. 

How to get selected object(s) in form processor using NmCommandBean 

There are four different form processing scenarios and to get the selected oid in each scenario there is different API on ​NmCommandBean​ that 
can be used. See the picture below for more details. 

Specify the processor class for the wizard on the wizard action 

You specify the ​ObjectFormProcessor​ class in the ​<command>​ subtag of the ​<action>​ tag for the wizard. Your action tag will be contained in a 
*actions.xml​ file. 
Here is an example of how you would specify the ​CreateDocFormProcessor​ class as your processor. 
<action name="create"> 
<command 
class="com.ptc.core.components.forms.CreateObjectFormProcessor" 
 
windowType="popup" /> 
</action> 
Create any necessary ObjectFormProcessorDelegate classes for your wizard 

ObjectFormProcessorDelegates​ may be used to carry out one or more subtasks in your wizard processing. Because the same delegate class 
may be called by multiple ​ObjectFormProcessors​, they are typically used for tasks that are needed by multiple wizards. Here are some examples 
of how ​ObjectFormProcessorDelegates​ are used in the delivered product: 
• Several object creation wizards have a checkbox named "Keep checked out after checkin." The ​ObjectFormProcessors​ for all these wizards 
call the same ​ObjectFormProcessorDelegate​ class to handle this checkbox and checkout the object after it is created if the box is 
checked. 
• Wizards to create objects that are ​ContentHolders​ typically have a Set Attachments step to specific the documents that should be 
attached to the object. All these wizards call the same ​ObjectFormProcessorDelegate​ class to handle the persistence of the attachments. 
• Many object creation wizards have an input field for a Location attribute to specify the object's folder. Because the processing of this input 
field is complex all these wizards call the same ​ObjectFormProcessorDelegate​ to set the folder on the object being created. 
As shown in the examples above, a ​ObjectFormProcessorDelegate​ can handle the processing of one or many input fields. If your wizard does 
not have any HTML elements that are used in multiple wizards you may not need any delegates. However, they can be useful for modularizing 
your processing code also. 
ObjectFormProcessorDelegates​ should extend the class ​DefaultObjectFormProcessorDelegate​. ​ObjectFormProcessorDelegates​ are 
instantiated by the ​DefaultFormProcessorController​ and passed to the ​ObjectFormProcessor​. 
ObjectFormProcessorDelegates​ have ​preProcess()​, ​doOperation()​, ​postProcess()​, and ​postTransactionProcess()​ methods just like 
ObjectFormProcessors​. The delegates registered for the wizard will be called by the ​DefaultObjectFormProcessor​ during the different 
processing phases. 
The outcome of an ​ObjectFormProcessorDelegate​ method is passed back to the ​ObjectFormProcessor​ using a ​FormResult​, just as the 
ObjectFormProcessor​ methods pass back their results to the ​FormProcessorController​. 
Like ​ObjectFormProcessor​ methods, ​ObjectFormProcessorDelegate​ methods can throw exceptions. How these are handled depends on the 
ObjectFormProcessor​ calling it. 

Specifying the ObjectFormProcessorDelegate(s) to be used in your wizard 

The names of the ​ObjectFormProcessorDelegate​ classes to be instantiated by the ​DefaultObjectFormProcessor​, if any, are communicated in 
hidden input fields as follows: 
<input name="FormProcessorDelegate" 
value="com.ptc.core.components.forms.NumberPropertyProcessor" 
type="hidden"> 
These hidden input fields can be created in several ways: 
• If you have a delegate associated with a specific wizard step, the delegate can be specified in the command 
subtag of in the wizard step action. For example: 
<action name="attachmentsWizStep" postloadJS="preAttachmentsStep" 
preloadWizardPage="false" 
<command 
 
class="com.ptc.windchill.enterprise.attachments.forms.Second 
aryAttachmentsSubFormProcessor" windowType="wizard_step"/> 
</action> 
The wizard framework will then generate a hidden ​FormProcessorDelegate​ field in the wizard for any wizard using this step. If you have 
specified an object handle on the wizard step action, the name attribute of the hidden field will include the object handle so that the 
delegate will be called only for the target object associated with the step. 
• If you have a specific input field that requires a delegate you can generate the hidden field in the data utility that creates the GUI 
component for the input field. It is recommended that the data utility return a subclass of ​AbstractGuiComponent​ to take advantage of 
the ​addHiddenField()​ method and the ​AbstractRenderer​. After creating the GUI component in the data utility, call the method 
addHiddenField()​ in the ​AbstractGuiComponent​ class. For example: 
LocationInputGuiComponent guiComponent = new 
LocationInputComponent(…); 
guiComponent.addHiddenField 
(CreateAndEditWizBean.FORM_PROCESSOR_DELEGATE, "com. 
ptc.windchill.enterprise.folder.LocationPropertyProcess 
or"); 
The hidden input field will be generated for you by the ​AbstractRenderer​ automatically. If the field is associated with a step or table row that 
has an object handle, that object handle will be embedded in the HTML name attribute of the hidden field. If you choose to return a GUI 
component that does not extend ​AbstractGuiComponent​, your GUI component and renderer would have to know how to render the 
necessary hidden field. 
• You can include a hidden field for your delegate directly in your jsp file. However, one of the first two methods is preferred because it 
encapsulates the hidden field with its associated HTML input fields. 

Wizards with Multiple Target Objects 

This process consists of the following steps: 


• ​Create your processor class 
• ​Specify the processor class for the wizard on the wizard action 
• ​Create any necessary ObjectFormProcessorDelegate classes for your wizard 
• ​Specifying the ObjectFormProcessorDelegate(s) to be used in your wizard 
• ​Create a FormProcessorController, if needed 
• ​Specify the FormProcessorController to be used in your wizard, if needed 

Create your processor class 

The same as for a wizard with a single target object. See ​Create your processor class​. 

Specify the processor class for the wizard on the wizard action 

The same as for a wizard with a single target object. See ​Specify the processor class for the wizard on the wizard action​. 

Create any necessary ObjectFormProcessorDelegate classes for your wizard 

The same as for a wizard with a single target object. See ​Create any necessary ObjectFormProcessorDelegate classes for your wizard​. 

Specify the ObjectFormProcessorDelegate(s) to be used in your wizard 

The same as for a wizard with a single target object. See ​Specifying the ObjectFormProcessorDelegate(s) to be used in your wizard​. Remember 
that an object handle must be embedded in the name attributes of the hidden fields specifying the delegates if you want the delegate to be 
used only for a given object. 

Create a FormProcessorController, if needed 

If the target objects of your wizard form a structure of related objects, you will need to create your own ​FormProcessorController​ to create a 
structure of the ​ObjectBeans​. You should subclass the ​DefaultFormProcessorController​ to leverage its ability to partition the form data into 
ObjectBeans​ and ​ProcessorBeans​ and call the form processors. Typically, the only method you will need to override is the 
createObjectBeanStructure()​ method. 

Specify the FormProcessorController to be used in your wizard, if needed 

If you have created your own ​FormProcessorController​, you should specify the controller to be used for your wizard on the wizard tag in your 
main JSP file for the wizard. For example: 
<jca:wizard helpSelectorKey="change_createProblemReport" 
buttonList="DefaultWizardButtonsWithSubmitPrompt" 
formProcessorController="com.ptc.windchill.enterprise.change2.forms. 
controllers.ChangeItemFormProcessorController"> 

FormResult / Client-Side Post Form Processing 

Starting with the Windchill 10.1 MR010 release, the form processing and the action response handling have been separated. This means that the 
form processor will still perform the work to update/add/delete the selected/affected objects, but there is no need to specify the next action on 
the ​FormResult​. The ​FormResult​ will contain the OIDs of the objects that were updated/added/deleted. These OIDs will be passed back to the 
client and the client will update the components that are currently displaying those objects. 

Setting up the FormResult 

Starting with the Windchill 10.1 MR010 release there is no need to set the next action (i.e. ​setNextAction(FormResultAction.REFRESH_OPENER)​). 
Only the OIDs the objects added/updated/removed should be added to the ​FormResult​ object. 
Example: ​formResult.addDynamicRefreshInfo new DynamicRefreshInfo(newOid, oldOid, NmCommandBean.DYNAMIC_UPD)) 
The ability to set a URL or Javascript function on the ​FormResult​ is still valid. However, this should only be done when no other options are 
available. Because multiple clients are potentially invoking these actions the URL javascript returned may not be appropriate or may not 
reference valid code. Ideally, you want the components to update themselves and not have individual actions update the components on the 
page. If a URL is set on the ​FormResult​, the ​afteraction​ listener will not be called on the component where the action was launched from. 
However, the ​objectsaffected​listener will be called. 

Refreshing/Updating JCA Components 

There are two listeners that can be attached to components for objects updated via actions. Most of the components (i.e. table, tree, info page, 
etc.) have a default listener for both of the events. 
• ​afteraction​ : This listener is responsible for updating the objects in the component where the action was launched/executed from. For 
example, if the checkout action was executed from the information page, the information page ​afteraction​ listener is responsible for 
refreshing itself. Likewise, if the checkout action was launched from a table, the table ​afteraction​ listener will be responsible for updating 
the row in the table. This listener can be attached to any Ext component by adding the ​afteraction​ event listener. The listener will receive 
the ​FormResult​ JS object as a ​parameter.component.on(‘afteraction’, someAfterActionListener); 
• ​objectsaffected​ : This listener is responsible for updating the objects in a component when the action is executed from a different 
component. For example, if the checkout action was performed from the information page and the search results table contained that 
same object, the ​objectsaffected​ listener for the [search results] table will be responsible for updating the row in the table. This listener is 
attached to the global ​PTC.action​ object: ​component.mon(PTC.action, ‘objectsaffected’,someListener); 

Examples of Custom Action Handling 


Refreshing an entire component when any action occurs inside of it 

me.on('afteraction', me.onAfterAction); 
me.onAfterAction = function(formResult) { 
me.refresh(); 
return true;//Stop further processing 
}; 

Replacement for FormResultAction.FORWARD and LOAD_OPENER_URL 

Different components have different behavior and different client platforms have different URL patterns so it is not supported to generically try 
and specify a URL on the ​FormResult​. Each component can decide how to do it individually and the ​afterAction​ listeners on common 
components such as the ​infopage​ and ​miniNavigator​ already have generic logic to refresh themselves. The ​miniNavigator​ for example does not 
want to forward to the new URL because that would break the user out of the current context and so it refreshes itself instead. 
See the checkout example below for how to have a component specific event listener do something unique. See the 
PTC.miniNavigator.onAfterAction​ and the ​PTC.infoPage.onAfterAction​ code in the js files. If extra URL parameters are needed to be passed down 
to construct the url on the client side then the ​FormResult.extraData​ map can be used to pass down extra information. 
You can change how an object is handled for a specific table. For example add a row to the checkouts table instead of adding a checkout glyph 
to an existing row (from ​wip.jsfrag​): 
PTC.wip = {}; 
/** 
* add/remove rows from the checkouts table for the wip actions 
*/ 
PTC.wip.checkoutsTableObjectsAffectedWrapper = function(original, formResult) { 
 
if (formResult.actionName === 'checkout') { 
var added_new_rows = formResult.getUpdatedOids(); 
   
if(added_new_rows.length > 0) { 
clearActionFormData(); 
 
rowHandler.addRows(added_new_rows, this.id, null, { 
doAjaxUpdate : true, 
addSorted : true 
}); 

return true; 

return original.call(this, formResult); 
}; 
 
PTC.wip.addCheckoutTableListeners = function (table) { 
table.onObjectsAffected = table.onObjectsAffected.wrap 
(PTC.wip.checkoutsTableObjectsAffectedWrapper); 
}; 
 
Ext.ComponentMgr.onAvailable('checkedout.work.table', 
PTC.wip.addCheckoutTableListeners); 

Replacements for FormResultAction.JAVASCRIPT 

In cases where extra javascript was needed to run to update specific components in the page as the result of an action, it is better to add 
component specific ​objectsaffected​ listeners instead that react to the specific action, such as the checkout action example below. Some 
wizards may want to add a listener specific to the wizard because the javascript is often times not needed in other pages. 

Wizard Specific Handler 

Actions that need to perform custom or specific code as the result of a wizard can use a callback function for handling a successful submission. 
This ​successFunc​ configuration parameter is passed to the ​PTC.wizard.submitWizard​ function. This ​successFunc​ is then called when the wizard 
receives the ​FormResult​ from the server and begins to fire the ​afteraction​ and ​objectsaffected​ events. This allows an action owner to control the 
flow and behavior of the wizard more specifically than needing to modify how individual components handle the event name. For Example, the 
checkin​ Button on the edit wizard has a success handler to launch the ​checkin​ wizard once the attributes are successfully saved to the 
database. 
In the sample code below, notice that the function passed to the wizard code: 
<command onClick="onEditSubmit('checkinButton')"/> 
function onEditSubmit(actionName){ 
var params = {successFunc: PTC.wizard.launchCheckinWizard, 
finished: actionName=='saveButton'}; 
PTC.wizard.submitWizard(params); 

Change how an object is handled for a specific table 

For example, you can add a row to the checkouts table instead of adding a checkout glyph to an existing row (from ​wip.jsfrag​): 
PTC.wip = {}; 
/** 
* add/remove rows from the checkouts table for the wip actions 
*/ 
PTC.wip.checkoutsTableObjectsAffectedWrapper = 
function(original, formResult) { 
 
if (formResult.actionName === 'checkout') { 
var added_new_rows = formResult.getUpdatedOids(); 
   
if(added_new_rows.length > 0) { 
clearActionFormData(); 
 
rowHandler.addRows(added_new_rows, this.id, null, { 
doAjaxUpdate : true, 
addSorted : true 
}); 

return true; 

return original.call(this, formResult); 
}; 
 
PTC.wip.addCheckoutTableListeners = function (table) { 
table.onObjectsAffected = table.onObjectsAffected.wrap 
(PTC.wip.checkoutsTableObjectsAffectedWrapper); 
}; 
 
PTC.onAvailable('checkedout.work.table',PTC.wip.addCheckoutTableListeners); 

Replacement for FormResultAction.FORWARD and LOAD_OPENER_URL 

Different components have different behavior and different client platforms have different URL patterns so it’s not supported to generically try 
and specify a URL on the ​FormResult​. Each component can decide how to do it individually and the ​afterAction​ listeners on common 
components such as the ​infopage​ and ​miniNavigator​ already have generic logic to refresh themselves. The ​miniNavigator​ for example does not 
want to forward to the new URL because that would break the user out of the current context and so it refreshes itself instead. 
See the checkout example below for how to have a component specific event listener do something unique. Also see the 
PTC.miniNavigator.onAfterAction​ and the ​PTC.infoPage.onAfterAction​ code in the js files. If extra URL parameters are needed to be passed down 
to construct the url on the client side then the ​FormResult.extraData​ map can be used to pass down extra information. In most cases it is not 
necessary to auto-forward to the info page of an object just created or modified. After create actions, an inline message appears and contains 
a link to allow someone to go to the new objects info page. This is how one might auto-forward after an action. Modify the component’s 
onAfterAction​ method to include this code sequence: 
if (formResult.actionName === 'checkout') { 
PTC.infoPage.goTo(formResult.getUpdatedOids()[0]); 

return true; 
You can add a generic action handler to all possible components that will auto-forward for a specific action. The following code example details 
this. However, this is not necessarily recommended because some pages and components might lose in-progress data. 
PTC.action.on('objectsaffected', function (formResult) { 
if (formResult.actionName === 'checkout') { 
PTC.infoPage.goTo(formResult.getUpdatedOids()[0]); 

return true; 
}); 

Limitations 

• Only one ​ObjectFormProcessor​ class per wizard is supported at this time 


• The framework does not support having data for multiple objects in the same wizard step unless it is in tabular format. It also does not 
support having data common to all the objects on the same step as object-specific data. 

Additional Resources 

Related Package/Class Javadoc 

• com.ptc.core.components.forms.FormDispatcher 
• com.ptc.core.components.forms.FormProcessorController 
• com.ptc.core.components.forms.DefaultFormProcessorController 
• com.ptc.core.components.forms.ObjectFormProcessor 
• com.ptc.core.components.forms.DefaultObjectFormProcessor 
• com.ptc.core.components.forms.CreateObjectFormProcessor 
• com.ptc.core.components.forms.DefaultEditFormProcessor 
• com.ptc.core.components.forms.EditWorkableFormProcessor 
• com.ptc.core.components.forms.ObjectFormProcessorDelegate 
• com.ptc.core.components.forms.DefaultObjectFormProcessorDelegate 
• com.ptc.core.components.forms.FormResult 
• com.ptc.core.components.forms.DynamicRefreshInfo 
• com.ptc.core.components.forms.FormProcessingStatus 
• com.ptc.core.components.util.FeedbackMessage 
• com.ptc.core.ui.resources.FeedbackType 
 
Example Formwizard 
 
Custom-action.xml 
<objecttype name="customdoc" resourceBundle="null"> 
<action name="createdoc" type="customdoc" > 
<command url="/ext/jsp/customwizard/createdoc.jsp" class="ext.cust.myprocessor.SelectedObjectFormProcessor" windowType="popup" /> 
<label>Create Document</label> 
</action> 
<action name="selectObject" type="customdoc" > 
<command url="/ext/jsp/customwizard/selectObject.jsp" windowType="wizard_step" /> 
<label>Select Object</label> 
</action> 
<action name="setAttributes" type="customdoc" > 
<command url="/ext/jsp/customwizard/setAttributes.jsp" windowType="wizard_step" /> 
<label>Set Attributes</label> 
</action> 
</objecttype> 
 
Custom-actionModel.xml 
 
<action name="createdoc" type="customdoc" /> 
 
Createdoc.jsp 
 
<%@ taglib prefix="jca" uri="http://www.ptc.com/windchill/taglib/components"%> 
<%@ taglib uri="http://www.ptc.com/windchill/taglib/fmt" prefix="fmt"%> 
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 
<%@ include file="/netmarkets/jsp/components/beginWizard.jspf" %> 
<%@ include file="/netmarkets/jsp/components/includeWizBean.jspf" %> 
 
<jca:wizard title="Create object"> 
<jca:wizardStep action="selectObject" type="customdoc"/> 
<jca:wizardStep action="setAttributes" type="customdoc"/> 
</jca:wizard> 
 
<%@ include file="/netmarkets/jsp/util/end.jspf"%> 
 
selectObject.jsp 
 
<html> 
<body> 
Select Object Type: <select name="Type"> 
<option>Select a Type</option> 
<option>part</option> 
<option>document</option> 
</select> 
</body> 
</html> 
 
setAttributes.jsp 
 
<%@ include file="/netmarkets/jsp/components/beginWizard.jspf" %> 
<%@ taglib uri="http://www.ptc.com/windchill/taglib/wrappers" prefix="w" %> 
<table> 
<tr> 
<td scope="row" width="200” align="right"> 
*Number: 
</td> 
<td align="left"> 
<input type="textbox" name="null___number___textbox" id="number" /> 
</td> 
</tr> 
<tr> 
<td scope="row" width="200” align="right"> 
*Name: 
</td> 
<td align="left">&nbsp; 
<w:textBox name="name" id="name" maxlength="30" size="10"/> 
</td> 
<! -- Both input type textbox and w:textBox will create a text box for user input. 
One is using normal HTML tag another is from Windchill Tag Library that’s the 
only difference --> 
</tr> 
<tr> 
<td scope="row" width="200” align="right"> 
Location: 
</td> 
<td align="left"> 
<input type="text" name="loc_displayLocation" id="loc_displayLocation"> 
<button onclick="ITC.launchFolderPicker(event)" type="button"> 
Browse 
</button> 
</td> 
</tr> 
</table> 
<input type="hidden" name="null___loc_folder___textbox" id="loc_folder" /> 
<input type="hidden" name="null___loc_container___textbox" id="loc_container" /> 
<script type="text/javascript"> 
<!--//namespace for paste example to avoid colliding with any globally defined functions --> 
ITC = {}; 
ITC.launchFolderPicker = function(event) { 
var url = getBaseHref() + 
'servlet/WindchillAuthGW/wt.enterprise.URLProcessor/invokeAction?action=cadxBrowseLocations&containerVisibilityMask=PDMLink&accessPer
mission=modify&displayHotlinks=false&displayCreateFolder=true'; 
launchBrowseFolders(url, $('loc_displayLocation'), ITC.locationCallback); 
Event.stop(event); 
return false; 

ITC.locationCallback = function(contextValues) { 
var containerOID = contextValues.containerOID; // 'OR:wt.pdmlink.PDMLINKProduct:123' 
var folderOID = contextValues.folderOID; // 'OR:wt.folder.SubFolder:123' 
$('loc_folder').value = folderOID; 
$('loc_container').value = containerOID; 

</script> 
<%@include file="/netmarkets/jsp/util/end.jspf"%> 
 
SelectedObjectFormProcessor.java 
 
package ext.cust.myprocessor; 
 
import java.util.HashMap; 
import java.util.List; 
import javax.servlet.http.HttpServletRequest; 
import com.ptc.core.components.beans.ObjectBean; 
import com.ptc.core.components.forms.DefaultObjectFormProcessor; 
import com.ptc.core.components.forms.FormResult; 
import com.ptc.netmarkets.util.beans.NmCommandBean; 
import wt.doc.WTDocument; 
import wt.fc.ObjectReference; 
import wt.fc.Persistable; 
import wt.fc.PersistenceHelper; 
import wt.fc.PersistenceServerHelper; 
import wt.fc.ReferenceFactory; 
import wt.folder.Folder; 
import wt.folder.FolderEntry; 
import wt.folder.FolderHelper; 
import wt.inf.container.WTContainer; 
import wt.part.WTPart; 
import wt.part.WTPart.*; 
import wt.util.WTException; 
import wt.util.WTPropertyVetoException; 
 
public class SelectedObjectFormProcessor extends DefaultObjectFormProcessor { 
 
@SuppressWarnings("rawtypes") 
@Override 
public FormResult doOperation(NmCommandBean commandbean ,List<ObjectBean> list)throws WTException 

HttpServletRequest request = commandbean.getRequest(); 
String type = request.getParameter("Type"); 
HashMap map = commandbean.getText(); 
 
Object[] keys = map.keySet().toArray(); 
 
String name = null; 
String number = null; 
String containerOid = null; 
String folderOid = null; 
String desCripTion = null; 
String viewName = null; 
 
if (type.toLowerCase().contains("wtpart")) { 
System.out.println("Getting Type:"+type); 
name = ((String) map.get("name")).toUpperCase(); 
number = ((String) map.get("number")).toUpperCase(); 
folderOid = ((String) map.get("loc_folder")); 
containerOid = ((String) map.get("loc_container")); 
viewName = ((String) map.get("view")); 
} else { 
name = ((String) map.get("name")).toUpperCase(); 
number = ((String) map.get("number")).toUpperCase(); 
folderOid = ((String) map.get("loc_folder")); 
containerOid = ((String) map.get("loc_container")); 

HashMap mapArea = commandbean.getTextArea(); 
//desCripTion = ((String) mapArea.get("description")); 
 
Persistable per = null; 
if (type.toLowerCase().contains("part")) { 
 
System.out.println("Getting Name:"+name); 
System.out.println("Getting Number:"+number); 
System.out.println("Getting folder:"+folderOid); 
//System.out.println("Getting description:"+desCripTion); 
per = createPart(name, number, containerOid, folderOid, viewName); 
} else if (type.toLowerCase().contains("document")) { 
per = createDocument(name, number, desCripTion, containerOid, 
folderOid); 
try { 
((WTDocument)per).setDescription("UPDATED FROM METHOD"); 
System.out.println("after persisting the value"); 
PersistenceServerHelper.manager.update(per); 
PersistenceHelper.manager.refresh(per); 
} catch (WTPropertyVetoException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 


ObjectBean ob = ObjectBean.newInstance(); 
ob.setObject(per); 
list.add(ob); 
/* Adding the Persistable object in the List */ 
return super.doOperation(commandbean, list); 

private Persistable createPart(String name, String number,String containerOid, String folderOid, String viewName) throws WTException 

WTPart part = WTPart.newWTPart(); 
try { 
part.setName(name); 
part.setNumber(number); 
part.setContainer((WTContainer)fromReferenceToObject(containerOid)); 
 
part=(WTPart)PersistenceHelper.manager.save(part); 
} catch (wt.util.WTPropertyVetoException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 
}catch(WTException e) 

e.printStackTrace(); 

 
return part; 

private Persistable fromReferenceToObject(String objectReference) 

 
ObjectReference objRef = null; 
try { 
final ReferenceFactory factory=new ReferenceFactory(); 
objRef = (ObjectReference)factory.getReference(objectReference); 
} catch (WTException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 

return (objRef.getObject()); 

private Persistable createDocument(String name, String number,String desCripTion, String containerOid, String folderOid) throws 
WTException 

WTDocument doc = WTDocument.newWTDocument(); 
 
 
try { 
doc.setNumber(number); 
doc.setName(name); 
if(desCripTion!=null) 

  doc.setDescription(desCripTion); 

doc.setContainer((WTContainer) fromReferenceToObject(containerOid)); 
assignFolder(doc,folderOid); 
doc=(WTDocument)PersistenceHelper.manager.save(doc); 
} catch (WTPropertyVetoException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 

return doc; 

private void assignFolder(FolderEntry folderEntry,String folderoid) 

final Folder folder=((Folder)fromReferenceToObject(folderoid)); 
 
try { 
FolderHelper.assignLocation(folderEntry, folder); 
} catch (WTException e) { 
// TODO Auto-generated catch block 
e.printStackTrace(); 


 

 
 
 
Load From Files Utility  

Bulk Load Documents 

Create a CSV in Excel with the following columns: 

General,user,name,title,number,type,description,department,saveIn,teamTemplate,domain,lifecycletemplate,typedef,primarycontenttype,path,fo
rmat,contdesc,version,iteration 

o Example: 

§ General,orgadmin,my name,my title,0000000013,Document,my 


description,ENG,/Default/Documentation,,,Basic,INWORK,com.tristar.Agenda,ApplicationData,C:\my_doc.doc,,,A,1 

o Notes: title does not show up in the UI; department does not show up in the UI; you can leave typedef blank if not a soft type 

· Place the CSV in <Windchill>\src\loadfiles 

· In a Windchill Shell, run the CSV to XML converter: 

o windchill wt.load.util.CSV2XML -input docs_to_load.csv  


· In a Windchill Shell, run the document loader: 

o ​windchill wt.load.LoadFromFile -d <Windchill>\src\loadfiles\docs_to_load.xml -u wcadmin -p wcadmin -CONT_PATH 


/wt.inf.container.OrgContainer=<org>/wt.pdmlink.PDMLinkProduct=”<product>” 

o ​Note: for a Library, use wt.inf.library.WTLibrary=”<library>”. Please check the file csvmapfile.txt for other object type bulk upload 

OIR and Other Customization 


{GEN:wt.enterprise.SequenceGenerator:WTPARTID_seq:10:0} 

Proble Explain {GEN:wt.enterprise.SequenceGenerator:WTPARTID_seq:10:0} in OIR of Windchill.   


m   

Resolut {GEN:wt.enterprise.SequenceGenerator:WTPARTID_seq:10:0} is basically a code snippet in rule.xml which is used to describe the 
ion  sequence to be used for new object creation for a particular object type. 

● wt.enterprise.SequenceGenerator is a class that maps the name of the sequence to the Sequence pool present in 
Oracle database using the NAME_TO_SEQUENCE_MAP. 
● WTPARTID_seq is the name of the sequence specified in Windchill database which generates numbers in a particular 
sequence/pattern. 
● 10 is the length of the number to be generated. 
● 0 is the padding character which will be padded in the beginning of the number if the length of the number is less than 
10. 

 
 
Turn off auto-numbering of object types in Windchill using OIR 

Proble ● Turn off auto-numbering of WTPart. 


m    ● Turn off autonumbering of WTDocument, EPMDocument, etc.   
 

  To turn off auto-numbering of object types in Windchill, edit the OOTB rule.xml file. 

Resolution  If GatherAttributeConstraints is not present in rule.xml for number attribute, then add the below code snippet. 
<AttributeValues​ ​objType=​"ext.part.CustomPart"​>  
​<AttrConstraint​ i​ d=​"number"​ a
​ lgorithm=​"com.ptc.core.rule.server.impl.GatherAttributeConstraints"​> 
​</AttrConstraint> 
</AttributeValues> 

But, if GatherAttributeConstraints is already present in the rule.xml file with some additional constraints, then comment 
out the other constraints within GatherAttributeConstraints as shown in the code snippet below: 
<AttrConstraint​ ​id=​"number"​ ​algorithm=​"com.ptc.core.rule.server.impl.GatherAttributeConstraints"​> 
​<!-- 
<Value algorithm="com.ptc.core.rule.server.impl.GetServerAssignedConstraint"/> 
<Value algorithm="com.ptc.core.rule.server.impl.GetImmutableConstraint"/> 
--> 
​<!-- Other algorithms --> 
</AttrConstraint> 
 
After doing this, upload the rule.xml file for that object type. Now while creating a new object, the user will be asked to enter 
the number manually. 
 
How to create and use custom RuleAlgorithm in Windchil 

Proble ● How to set the number of object type to a number coming from an external number generating system. 
m    ● How to create and implement a custom RuleAlgorithm. 
● How to create a custom NumberGenerator.   

 
Resolution  At first, create a class that either implements RuleAlgorithm or extends NumberGenerator class. 

Override the calculate() method and write the logic for generating random number as shown below: 

package​ ​ext.custom​.​rules​; 
import​ ​java.util.Random​; 
import​ ​wt.inf.container.WTContainerRef​; 
import​ ​wt.rule.algorithm.RuleAlgorithm​; 
import​ ​wt.util.WTException​; 
 
public​ ​class​ ​CustomNumberGenerator​ ​implements​ RuleAlgorithm ​{ 
​@Override 
​public​ Object ​calculate​(​Object​[]​ arg0​,​ WTContainerRef arg1​)​ ​throws​ WTException ​{ 
Random random ​=​ ​new​ Random​(); 
​int​ randomValue ​=​ random​.​nextInt​(99999); 
​return​ randomValue​; 
​} 

Register the class within OIR for generation of customized random numbers as shown below: 

<AttributeValues​ ​objType=​"ext.part.CustomPart"​>  
​<VarDef​ ​id=​"randomNumber"​ a ​ lgorithm=​"ext.custom.rules.CustomNumberGenerator"​> 
​</VarDef> 
​<AttrValue​ ​id=​"number"​ ​algorithm=​"com.ptc.windchill.enterprise.revisionControlled.server.impl.NumberGenerator"​> 
​<VarRef​ ​id=​"randomNumber"​ /​ > 
​</AttrValue> 

</AttributeValues> 

 
 
Change the numbering pattern of an object based on another attribut 

Proble Based on an attribute value, change the numbering pattern of an object type.   
m   
[How to use Switch-Case in OIR] [How to put conditions in OIR]   
 

Resolution  If we want to customize the naming pattern of an object based on business conditions, we can do it by making use of 
CaseBranch, BooleanBranch or EqualsTest. 

CaseBranch algorithm in OIR, provides the functionality similar to Switch-Case used in Java. Similarly, EqualsTest is used 
to compare two values for equality. 

For eg. 
<AttrValue​ ​id=​"number"​ ​algorithm=​"com.ptc.windchill.enterprise.revisionControlled.server.impl.NumberGenerator"​> 
​<!-- CaseBranch starts here --> 
​<Value​ ​algorithm=​"wt.rule.algorithm.CaseBranch"​> 
  
​<!-- If name entered by user equals to One, then number is assigned the value of Source attribute --> 
​<Value​ ​algorithm=​"wt.rule.algorithm.EqualsTest"​> 
​<Attr​ ​id=​"name"​ ​/> 
​<Arg>​One​</Arg> 
​</Value> 
​<Attr​ ​id=​"source"​ /​ > 
  
< ​ !-- If name entered by user equals to Two, then number is assigned the value of Container Name --> 
​<Value​ ​algorithm=​"wt.rule.algorithm.EqualsTest"​> 
​<Attr​ ​id=​"name"​ ​/> 
​<Arg>​Two​</Arg> 
​</Value> 
​<Attr​ ​id=​"containerName"​ /​ > 
  
< ​ !-- If none of the above cases are true then the default value is assigned to the number attribute --> 
​<Arg>​{GEN:wt.enterprise.SequenceGenerator:WTPARTID_seq:10:0}​</Arg> 
​</Value> 

</AttrValue> 

 
Prefix or Postfix an argument to auto-generated number in Windchill 
Proble How to prefix or postfix an argument to the auto-generated number in Windchill?   
m   

Resolution  To prefix or postfix an argument to the number of an object type, we can use <Arg> tag. Below is an example: 
<AttrValue​ ​id=​"number"​ ​algorithm=​"com.ptc.windchill.enterprise.revisionControlled.server.impl.NumberGenerator"​> 
​<Arg>​PREFIX-​</Arg> 
​<Arg>​{GEN:wt.enterprise.SequenceGenerator:WTPARTID_seq:4:0}​</Arg> 
​<Arg>​-POSTFIX​</Arg> 
</AttrValue> 

The auto-generated number will look something like this: 

PREFIX-0123-POSTFIX 

 
Assign an attribute value to the number of an object using OIR in Windchill 

Proble How to assign an attribute value to the number of an object using Object Initialization Rule (OIR) in Windchill? 
m   

 
Resolution  To set the number of an object to any attribute value, we can simply use <Attr> tag along with the internal name of the attribute to which 
you want to map the number. 

For eg. 

We can set the number of an object same as its name which is entered by the user. 

Below is the code snippet: 


<AttrValue​ ​id=​"number"​ ​algorithm=​"com.ptc.windchill.enterprise.revisionControlled.server.impl.NumberGenerator"​> 
​<Attr​ ​id=​"name"​ ​/> 
</AttrValue> 

Just as the name attribute, we can use value of other attributes simply by putting the internal name of the attribute in the <Attr> tag. 

Related    Object Initialization Rules (OIR) in Windchill 

 
Creating a new number sequence in Oracle for OIR in Windchill 

Proble How to create a new sequence in Oracle that can be used to generate number of an object type and how to hook it up in 
m    OIR.   

 
Resolution  Below are the steps to create new sequence: 

1. Create a new file "create_<mysequence>_sequence.sql" in <Windchill_Home>/db/sql directory and <Windchill_Home>/db/sql3 


directory. 

2. Add the below code to the sql file: 

​//​exec​ WTPK.createSequence(​'<Sequence Name>'​, ​<​Initial ​Number​>​, ​<​Increment​>​) 


​For​ eg, 

​exec​ WTPK.createSequence(​'PartSequence_seq'​, ​1​, ​20​) 

3. Execute the script from the windchill shell 


​//<Windchill_Home>/db/execute_sql_script.bat create_<mysequence>_sequence.sql 
For eg​, 

​<​Windchill_Home​>/​db​/​execute_sql_script​.​bat​ create_newseq_sequence​.​sql 

This will create a new sequence with name PartSequence_seq in the Oracle database. After the sequence is created, we can use the 
generated sequence in our Object Initialization Rule (OIR). 

Below are the steps: 

Edit the OOTB rule.xml file and use the newly created sequence. 
<AttrValue​ ​id=​"number"​ ​algorithm=​"com.ptc.windchill.enterprise.revisionControlled.server.impl.NumberGenerator"​> 
​<Arg>​{GEN:wt.enterprise.SequenceGenerator:PartSequence_seq:10:0}​</Arg> 
</AttrValue> 

Upload the OIR to see the changes take effect. 

 
Explain {GEN:wt.enterprise.SequenceGenerator:WTPARTID_seq:10:0} 

Proble Explain {GEN:wt.enterprise.SequenceGenerator:WTPARTID_seq:10:0} in OIR of Windchill.   


m   

 
Resolut {GEN:wt.enterprise.SequenceGenerator:WTPARTID_seq:10:0} is basically a code snippet in rule.xml which is used 
ion  to describe the sequence to be used for new object creation for a particular object type. 

● wt.enterprise.SequenceGenerator is a class that maps the name of the sequence to the Sequence pool present in 
Oracle database using the NAME_TO_SEQUENCE_MAP. 
● WTPARTID_seq is the name of the sequence specified in Windchill database which generates numbers in a particular 
sequence/pattern. 
● 10 is the length of number to be generated. 
● 0 is the padding character which will be padded in the beginning of the number if the length of the number is less 
than 10. 

Difference between Version Reference(VR) and Object Reference(OR) 

Proble Difference between VR and OR.   


m   

 
Resolution  Consider a new WTPart is created, below is the VR and OR for difference revisions and iterations created: 

Activity  Version  Version Reference (VR)  Object Reference (OR) 

After Part Creation  Version A.1  oid=VR:wt.part.WTPart:246003  oid=OR:wt.part.WTPart:246004 

After Iteration Change  Version A.2  oid=VR:wt.part.WTPart:246003  oid=OR:wt.part.WTPart:246018 

After Revision Change  Version B.1  oid=VR:wt.part.WTPart:246041  oid=OR:wt.part.WTPart:246042 

After Iteration Change  Version B.2  oid=VR:wt.part.WTPart:246041  oid=OR:wt.part.WTPart:246053 

From the table, it is clear that Version Reference(VR) refers to a particular revision of a part i.e. VR will be same for all the 
iterations of a particular revision 

The VR number is repeated and shared on a per iteration basis, meaning that all iterations to that mastered/revision 
controlled object will be the same. VR also redirects you to the latest iteration of the latest revision 

On the other hand, Object Reference(OR) refers to the actual object i.e. every iteration will have a different OR 

Finally, if you want to convert VR to OR, below is the code 


public​ ​static​ String ​getORFromVR​(​String vr​)​ ​throws​ WTException  
vr ​=​ vr​.​substring​(0,​ vr​.​lastIndexOf​(​":"​)) 
ReferenceFactory rf ​=​ ​new​ ReferenceFactory​(); 
WTReference ref​; 
ref ​=​ rf​.​getReference​(​vr​); 
WTObject lct ​=​ ​(​WTObject​)​ ref​.​getObject​(); 
ObjectReference objRef ​=​ ObjectReference​.​newObjectReference​(​lct​); 
​return​ objRef​.​getKey​().​toString​(); 


 
Difference between Version Reference(VR) and Object Reference(OR) 

Proble Difference between VR and OR.   


m   

Resolution  Consider a new WTPart is created, below is the VR and OR for difference revisions and iterations created: 

Activity  Version  Version Reference (VR)  Object Reference (OR) 

After Part Creation  Version A.1  oid=VR:wt.part.WTPart:246003  oid=OR:wt.part.WTPart:246004 

After Iteration Change  Version A.2  oid=VR:wt.part.WTPart:246003  oid=OR:wt.part.WTPart:246018 

After Revision Change  Version B.1  oid=VR:wt.part.WTPart:246041  oid=OR:wt.part.WTPart:246042 

After Iteration Change  Version B.2  oid=VR:wt.part.WTPart:246041  oid=OR:wt.part.WTPart:246053 

From the table, it is clear that Version Reference(VR) refers to a particular revision of a part i.e. VR will be the same for all the iterations of a particular 
revision. 

The VR number is repeated and shared on a per iteration basis, meaning that all iterations to that mastered/revision controlled object will be the same. 
VR also redirects you to the latest iteration of the latest revision. 

On the other hand, Object Reference(OR) refers to the actual object i.e. every iteration will have a different OR. 

Finally, if you want to convert VR to OR, below is the code: 


public​ ​static​ String ​getORFromVR​(​String vr​)​ ​throws​ WTException  
vr ​=​ vr​.​substring​(0,​ vr​.​lastIndexOf​(​":"​)) 
ReferenceFactory rf ​=​ ​new​ ReferenceFactory​(); 
WTReference ref​; 
ref ​=​ rf​.​getReference​(​vr​); 
WTObject lct ​=​ ​(​WTObject​)​ ref​.​getObject​(); 
ObjectReference objRef ​=​ ObjectReference​.​newObjectReference​(​lct​); 
​return​ objRef​.​getKey​().​toString​(); 


 
Type of attributes in Windchill 

Proble Type of attributes in Windchill.   


m   

 
Resolution  There are 5 types of attributes used in Windchill: 

● Modeled attributes 
● Local/Standard attributes 
● Global attributes 
● Alias attributes 
● Calculated attributes 

Modeled attributes: 

● These are programmed in Java and cannot be created using Type and Attribute Manager tool. 
● You can create your own modeled attributes by using @GeneratedProperty annotation within the modeled 
type(hard type). 
● PTC doesn’t recommend the usage of modeled attributes because of the development time and effort it takes and it 
also makes the Windchill upgrade more complex. It also requires the purchase of Windchill Information Modeler, an 
optional Windchill component. 
● It is basically used for statistical analysis or validating itself against third-party system. 

Local/Standard attributes: 

● Local attributes are also called Standard attributes. 


● It can be configured without Java code. These are stored in the same table as its parent type. 
● For eg. If we want to create a local attribute for wt.part.WTPart type, then we need to create a column in WTPart 
table in database using AddColumns utility as shown below 
AddColumns wt.part.WTPart String=1 
● This will create a new column in WTPart table as PTC_STR_1TYPEINFOWTPART 
● Since, these are local to the table, they are slightly faster than the global attributes. 
● It is single-valued as it is stored in the same table as its parent type. 

Global attributes: 

● Global attributes are configured without Java code. These are stored in a table separate from its parent type. 
● For eg. We can create a global attribute “height” and can use it for various object types. 
● Global attributes have definition and value tables to store the data in database. 
 
Defnitions​ ​ alues 
V
BooleanDefinition  BooleanValue 
FloatDefinition FloatValue 
IntegerDefinition  IntegerValue 
StringDefinition StringValue 
RatioDefinition RatioValue 
ReferenceDefinition ReferenceValue 
TimestampDefinition TimestampValue 
UnitDefinition UnitValue 
URLDefinition URLValue 
  
● Global attributes can be multi-valued as it can be used by different object types. 
● They are comparatively slower than local attributes. 

Calculated and Alias attributes: 

● Calculated and Alias attributes are not stored in Windchill database directly. Instead, they are derived from other 
attributes. 
● Calculated attributes create new attributes from existing ones, either by concatenating String together or by using 
mathematical formula. 
 
For eg. If a part have “height” and “width” attributes for measurement, then we can create a calculated attribute to 
compute its area having 
Internal Name = area 
Display Name = Area  
Formula = height * width 
● Alias attributes navigate links to other objects.For eg. An alias attribute for a part would be the name of the 
document describing the part.  

 
Add a custom role in Windchill 
Problem    How to add a custom role in Windchill.   

Resolut There are two ways to add a custom role in Windchill; 


ion 
● Via enumCustomize tool 
● By changing the roleRB.rbinfo file 

Method 1: 

1. Open Windchill shell. Type enumCustomize. Hit Enter. 


2. enumCustomize tool will open. Browse <Windchill>\codebase\wt\project\RoleRB.RB.ser and open it. 
3. Add your custom role. 
4. Build the changes by saving the changes. 
5. Stop the Windchill server. 
6. Run the MakeJar command from codebase directory 
 
ant -f MakeJar.xml custUpdate 
7. Restart Windchill to see the changes. 

Method 2: 

1. Open src/wt/project/roleRB.rbinfo file. 


2. Add your custom role manually. 
3. Run the build command. 
ant –f tools.xml bundle_custom –Dbundle.input=registry 
4. Run the MakeJar command from codebase directory. 
ant -f MakeJar.xml 
5. Restart Windchill 

 
Add a custom state in Windchill 

Proble How to add a custom state in Windchill.   


m   

Resolut There are two ways to add a custom state in Windchill; 


ion 
● Via enumCustomize tool 
● By changing the stateRB.rbinfo file 

Method 1: 

1. Open Windchill shell. Type enumCustomize. Hit Enter. 


2. enumCustomize tool will open. Browse <Windchill>\codebase\wt\lifecycle\StateRB.RB.ser and open it. 
3. Add your custom state. 
4. Build the changes by saving the changes. 
5. Stop the Windchill server. 
6. Run the MakeJar command from codebase directory 
 
ant -f MakeJar.xml custUpdate 
7. Restart Windchill to see the changes. 

Method 2: 

1. Open src/wt/lifecycle/stateRB.rbinfo file. 


2. Add your custom state manually. 
3. Run the build command. 
ant –f tools.xml bundle_custom –Dbundle.input=registry 
4. Run the MakeJar command from codebase directory. 
ant -f MakeJar.xml 
5. Restart Windchill 
 
Link Objects in Windchill 
Details  Link between objects in Windchill   

 
Resolution  Below are the link objects in Windchill. 

From object  Link object  To object  

wt.doc.WTDocument  wt.doc.WTDocumentUsageLink  wt.doc.WTDocumentMaster 

wt.doc.WTDocument  wt.doc.WTDocumentDependencyLink  wt.doc.WTDocument 

wt.doc.WTDocument  wt.part.WTPartDescribeLink  wt.part.WTPart 

wt.part.WTPart  wt.part.WTPartUsageLink  wt.part.WTPartMaster 

wt.part.WTPart  wt.part.WTPartReferenceLink  wt.doc.WTDocumentMaster 

wt.part.WTPart  wt.epm.EPMBuildRule  wt.epm.EPMDocument 

wt.part.WTPart  wt.epm.EPMDescribeLink  wt.epm.EPMDocument 

wt.part.WTPart  wt.epm.structure.EPMDescribeLink  wt.epm.EPMDocument 

wt.part.WTPartMaster  wt.part.WTPartAlternateLink  wt.part.WTPartMaster 

wt.part.WTPartUsageLink  wt.part.WTPartSubstituteLink  wt.part.WTPartMaster 

wt.epm.EPMDocument  wt.epm.structure.EPMMemberLink  wt.epm.EPMDocumentMaster 

wt.epm.EPMDocument  wt.epm.structure.EPMReferenceLink  wt.epm.EPMDocumentMaster 

wt.epm.EPMDocument  wt.epm.structure.EPMVariantLink  wt.epm.EPMDocumentMaster 

wt.change2.WTChangeOrder2  wt.change2.AddressedBy2  wt.change2.WTChangeRequest2 

wt.change2.WTChangeActivity2  wt.change2.IncludedIn2  wt.change2.WTChangeOrder2 

 
 

 
How to create Data Utility in Windchi 
Details  Data Utility Example   

 
1. Create a class that extends any OOTB data utility class that falls in your context. Also override the 
Resolut
method getDataValue according to your requirement. 
ion 
 
public class TextColorDataUtility extends TypeDataUtility { 
 
@Override 
public Object getDataValue(String comp_id, Object datum, ModelContext mc) throws WTException { 
 
WTPart part = null; 
TextDisplayComponent tdp = null; 
   
if (datum instanceof WTPart) { 
part = (WTPart) datum; 
tdp = new TextDisplayComponent(part.getName()); 
tdp.setLabel(part.getName()); 
tdp.setValue(part.getName()); 
tdp.addStyleClass("overdueDate"); 

 
return tdp; 

}  
2. Register your data utility in <windchill>/codebase/service.properties.xconf and run xconfmanager -p to propagate 
the changes. 
 
<Service name="com.ptc.core.components.descriptor.DataUtility"> 
<Option serviceClass="ext.custom.datautilities.TextColorDataUtility" requestor="java.lang.Object" 
selector="textColorDataUtility"/> 
</Service> 
3. There are two ways you can call your data utility. Any of the two listed methods can be used to call the data utility. 
First: You can call it for attributes using Type and Attribute Management. 
For eg. 
Go to Type and Attribute Manager -> 
Part(Get into Edit Mode) -> 
Layouts(Choose a layout) -> 
Add Data Utility Id to the desired attribute -> 
Save the changes and exit. 
 
Second: You can call it in Table Builder 
For eg. 
 
@ComponentBuilder("ext.custom.tables.MVCTableBuilder") 
public class MVCTableBuilder extends AbstractComponentBuilder { 
 
@Override 
public Object buildComponentData(ComponentConfig config, ComponentParams params) throws Exception { 
QuerySpec query = new QuerySpec(WTPart.class); 
  
// You can specify the search condition according to you 
query.appendWhere(new SearchCondition(WTPart.class,WTPart.NAME,SearchCondition.LIKE,"%Part%"), null); 
return PersistenceHelper.manager.find(query); 

 
@Override 
public ComponentConfig buildComponentConfig(ComponentParams arg0) throws WTException { 
 
ComponentConfigFactory factory = getComponentConfigFactory(); 
 
TableConfig table = factory.newTableConfig(); 
 
table.setLabel("Selected Parts"); 
table.setSelectable(true); 
 
ColumnConfig column = factory.newColumnConfig("name",true); 
column.setDataUtilityId("textColorDataUtility"); // set the data utility id 
column.setLabel("Name"); 
table.addComponent(column); 
   
table.addComponent(factory.newColumnConfig("number", true)); 
table.addComponent(factory.newColumnConfig("type", true)); 
table.addComponent(factory.newColumnConfig("thePersistInfo.modifyStamp", true)); 
table.addComponent(factory.newColumnConfig("thePersistInfo.createStamp", true)); 
 
return table; 
 

How to specify the folder where an object will be created in Windchill PDMLink 

Description 
● How to specify a folder selected as default where new objects will be created? 
● How to create a​ Part in a specific folder? 
● How to set folder location while Check-In WTPart from Pro/ENGINEER? 
● How to Set up a Default Folder for all New Baselines Created? 
● How to change the save location of Change Request(ECR), Change Notice(ECN) and Problem Report(PR) 
● How to save ECR, ECN and PR to a different folder, not default root folder? 
● How to create OIR which will always create part in a specific folder of a particular library 
● Is it possible to mention the folder location in a different context in Object Initialization Rule? 
● How to force instances saving at the same location as their generic file? 

Resolution 

● Set the default folder location via Object Initialization Rules (OIR) 
1. Navigate to appropriate context such as Site / Organization / Product / Library > Utilities > Object Initialization Rules 
2. Download OIR of the appropriate object type such as Part, Document, Change Notice etc. 
3. Open XML file in a text editor such as Notepad etc. 
4. Modify <AttrValue id="folder.id"> element, add the name of the desired folder after /Default, for example:  

<AttrValue id=​"folder.id"​ algorithm=​"com.ptc.core.foundation.folder.server.impl.FolderPathAttributeAlgorithm"​> 


<Arg>/​Default​/Folder_Name</Arg> 
</AttrValue> 
 
Notes: 
a. ​Where​ ​"Folder_Name"​ specified above refers ​to​ the actual folder name which exists ​in​ Windchill.  
b. ​If​ the folder referred ​in​ OIR does ​not​ exists ​in​ Windchill ​then​ users will receive ​error​ ​while​ creating ​new​ objects ​for​ that specific type. 
c. ​Default​ OIR will have the following details: 
 
<AttrValue id=​"folder.id"​ algorithm=​"com.ptc.core.foundation.folder.server.impl.FolderPathAttributeAlgorithm"​> 
<Arg>/​Default​</Arg> 
</AttrValue>​ 

6. Search for AttrConstraint id="folder.id" element, which is located in the same file to specify how default folder location should display when 
creating or editing new objects. 

<AttrConstraint id=​"folder.id"​ algorithm=​"com.ptc.core.rule.server.impl.GatherAttributeConstraints"​> 


<Value algorithm=​"com.ptc.core.rule.server.impl.GetServerPreGeneratedValue"​/>    
</AttrConstraint> 

Notes: The constraint details specified above such ​as​ algorithms can be changed ​in​ ​order​ ​to​ change the display ​of​ the components ​in​ GUI ​in 
Create ​New​ wizard. 

For example ​check​ the ​following​ AttrConstraint element details.   


 
<AttrConstraint ​id​=​"folder.id"​ algorithm=​"com.ptc.core.rule.server.impl.GatherAttributeConstraints"​> 
<​Value​ algorithm=​"com.ptc.core.rule.server.impl.GetServerPreGeneratedValue"​/> 
<​Value​ algorithm=​"com.ptc.core.rule.server.impl.GetRendererConstraint"​> 
<Arg>SelectGeneratedFolderByDefault</Arg> 
</​Value​> 
</AttrConstraint> 

7. Save the changes and close file. 


8. Use Edit action from Object Initialization Rules table to upload updated XML file for the same object type in step 3. 
● Notes: 
● Error "​A folder name '/Default/Manufacturing' passed to the algorithm 
com.ptc.core.foundation.folder.server.impl.FolderPathAttributeAlgorithm does not exist.​" will be thrown if folder specified in OIR is not 
available in Windchill. 
● Default behaviour of "Check-In" in Windchill will continue to use workspace preferences as Attr id ="docType" isn't propagated/persisted 
until objects are uploaded. 
● For objects created in the Workspace, an alternate folder location can be specified by: 

A. Workspace Preferences: 
Select the Workspace > Edit Preferences action 
1. Change Part Target Folder and CAD Document Target Folder by clicking on Browse button 
2. Click OK 

OR 
 
B. Workspace Action 

1. Select all new files to be checked in from the Active Workspace 


2. Click CheckIn 
3. In CheckIn window again select all files to be checked in 
4. Click on choose Location Folder and set required location 
5. Location will be updated for all files simultaneously 
 

Data Utility Class for Windchill Object Attribute Manipulation 


How to create Data Utility in Windchill 
 
Create a Data Utility 
 
Overview 
What is different? 
• You are rendering a column in a table of changeables (ie. parts, documents) which represents a value on the binary link associating the 
changeable to the change object. 
• The value must be retained between: 
○ Paging beyond the page limit 
○ Sorting the table 
○ Changing table views (which may not even display the column or the row!) 
○ Survive additions and removals to the table. 
• The class ChangeLinkAttributeDataUtility has many helper methods and provides useful customization points to extend to assist with 
handling the above cases. 
• For performance, using the ChangeLinkAttributeDataUtility will cache the links for the method request so multiple attributes can be 
processed efficiently. 
Process 
The following are the key points that are required to make a working change link attribute data utility: 
1. Extend the class ChangeLinkAttributeDataUtility. 
2. Override the method createGuiComponent(String id,Object datum,ChangeLinkAttributeBean linkBean); The 
ChangeLinkAttributeBean is a specialized bean class to support managing the links in the request. 
3. Use linkBean.getObjectReference(datum) to get the ObjectReference of the changeable from the datum (normalizes the reference type for the 
table). 
4. Leverage getGUIComponentId() to generate an ID for components based on the reference object and the component ID. 
5. Set the name and ID of the GuiComponent to the generated value above. 
6. For in-place editable fields, call super.setEventListener(component,linkBean); to setup a listener to copy the edited value onBlur for the 
component 
7. Override retrieveValue(String id, BinaryLinklink); to extract the attribute value from the link (for VIEW and initial EDIT component modes). 
8. To get the current value use retrieveValue( ) 
9. Override ProcessExistingLinks(NmOid oid,String componentId) to load the links into the dataUtility. 
 
Add Support for Multi-Edit Actions 
 
In order to support multi-select actions in either a light-box or popup dialog, upon dismissing the prompting window, it is important to ensure 
the data is properly updated. The change link tables take advantage of a new table data manager store that has been added to support: 
 
PTC.change.linkAttributeHelper.update(item, tableId); Item is a Javascript object made up of an id and value. The id should be of the form: 
rowOid#component_id (... VR:wt.part.WTPart:5556#regulatoryAffected) It is recommended to disable events while manipulating the data stores 
(suspendEvents/resumeEvents) The table may be updated by calling the following if the table’s records are committed in their store: 
table.view.doPartialDataChange(); 
 
Example 
The following code provides an example for the operations needed on completing the editing multiple rows and updating the table.  
 
function updateRecords(isChecked, componentId, tableId) { 
var table = tableUtils.getTable(tableId); 
var store = table.getStore(); 
store.suspendEvents(); 
var selections = table.getSelectionModel().getSelections(); 
for(var i=0,l=selections.length; i<l; i++) { 
var record = selections[i]; 
record.data[componentId] = isChecked; 
record.commit(); 
var oid = record.get('oid'); 
if(oid) { 
var item = { 
id : oid+SEP+componentId, 
value : isChecked 
}; 
PTC.change.linkAttributeHelper.update(item, tableId); 


store.resumeEvents(); 
// re-draw displayed rows where data has changed. 
table.view.doPartialDataChange(); 

 
Key to code sample: 
 
Line 04 – Suspend Events 
Line 08 – Updates text in table cell 
Line 13 – Generate ID readable by change data utilities and form delegates 
Line 16 – Store the data item in the Table Data Manager store 
Line 19 – Resume Events 
Line 21 – Visually update the table with just what changed Process 
In order to use a lightbox or popup dialog from the tables, the launch action must be added to the actions list of the table. 
1. Create a custom actions file based on ChangeManagement-actions.xml, and register this file at the end of the property 
“com.ptc.netmarkets.util.misc.defaultActions“ in wt.properties. 
2. Create a custom action models file based on ChangeManagement-actionsModel.xml and register this file at the end of the property 
com.ptc.netmarkets.util.misc.defaultActionmodels in wt.properties. 
3. Copy the change objecttype and only the desired actions to be overridden to the custom actions.xml file. For this customization example use 
“regulatoryAffected” and action “setRegulatoryAffected” with selectRequired=“true”. 
4. Set the command to onClick a JS function to launch your lightbox/popup dialog. 
5. Add the action “regulatoryAffected.setRegulatoryAffected” in the custom actionModels.xml file. 
 
Note 
By overwriting the action model in a custom actionModels.xml, it is not necessary to update the MVC table builder because it is already pointing 
to this named action model. 
Details  Data Utility Example 
 
Resolution  1. Create a class that extends any OOTB data utility class that falls in your context. Also override the method 
getDataValue according to your requirement. 
public class TextColorDataUtility extends TypeDataUtility { 
  
@Override 
public Object getDataValue(String comp_id, Object datum, ModelContext mc) throws WTException { 
  
WTPart part = null; 
TextDisplayComponent tdp = null; 
   
if (datum instanceof WTPart) { 
part = (WTPart) datum; 
tdp = new TextDisplayComponent(part.getName()); 
tdp.setLabel(part.getName()); 
tdp.setValue(part.getName()); 
tdp.addStyleClass("overdueDate"); 

  
return tdp; 
}}  
 
 
2. Register your data utility in <windchill>/codebase/service.properties.xconf and run xconfmanager -p to propagate the changes. 
<Service name="com.ptc.core.components.descriptor.DataUtility"> 
<Option serviceClass="ext.custom.datautilities.TextColorDataUtility" requestor="java.lang.Object" 
selector="textColorDataUtility"/></Service> 
 
 
3. There are two ways you can call your data utility. Any of the two listed methods can be used to call the data utility. 
 
First: You can call it for attributes using Type and Attribute Management. 
For eg. 
 
Go to Type and Attribute Manager -> 
Part(Get into Edit Mode) -> 
Layouts(Choose a layout) -> 
Add Data Utility Id to the desired attribute -> 
Save the changes and exit. 
 
Second: You can call it in Table Builder 
For eg. 
@ComponentBuilder("ext.custom.tables.MVCTableBuilder")public class MVCTableBuilder extends AbstractComponentBuilder { 
  
@Override 
public Object buildComponentData(ComponentConfig config, ComponentParams params) throws Exception { 
QuerySpec query = new QuerySpec(WTPart.class); 
  
// You can specify the search condition according to you 
query.appendWhere(new SearchCondition(WTPart.class,WTPart.NAME,SearchCondition.LIKE,"%Part%"), null); 
return PersistenceHelper.manager.find(query); 

  
@Override 
public ComponentConfig buildComponentConfig(ComponentParams arg0) throws WTException { 
  
ComponentConfigFactory factory = getComponentConfigFactory(); 
  
TableConfig table = factory.newTableConfig(); 
  
table.setLabel("Selected Parts"); 
table.setSelectable(true); 
  
ColumnConfig column = factory.newColumnConfig("name",true); 
column.setDataUtilityId("textColorDataUtility"); // set the data utility id 
column.setLabel("Name"); 
table.addComponent(column); 
   
table.addComponent(factory.newColumnConfig("number", true)); 
table.addComponent(factory.newColumnConfig("type", true)); 
table.addComponent(factory.newColumnConfig("thePersistInfo.modifyStamp", true)); 
table.addComponent(factory.newColumnConfig("thePersistInfo.createStamp", true)); 
  
return table; 
  
}} 
 
 
 
 
Another Example Program 
 
package ext.datautility.attributes; 
 
import java.sql.Timestamp; 
import java.util.*; 
import com.ptc.core.components.descriptor.ModelContext; 
import com.ptc.core.components.rendering.guicomponents.TextDisplayComponent; 
import com.ptc.windchill.enterprise.object.dataUtilities.TypeDataUtility; 
import wt.part.WTPart; 
import wt.util.WTException; 
 
public class DaysBetween extends TypeDataUtility { 
 
public static void main (String args[]){ 
System.out.println("test"); 

 
@Override 
public Object getDataValue(String comp_id, Object datum, ModelContext mc) throws WTException{ 
 
WTPart part = null; 
TextDisplayComponent tdc = null; 
 
if (datum instanceof WTPart){ 
 
part = (WTPart) datum; 
int days = getDaysBetween(part.getCreateTimestamp(), part.getModifyTimestamp()); 
String str = Integer.toString(days); 
tdc = new TextDisplayComponent(str); 
tdc.setLabel(str); 
tdc.setValue(str); 
tdc.addStyleClass("overdueDate"); 

 
return tdc; 

private int getDaysBetween (Timestamp start, Timestamp end) { 
boolean negative = false; 
if (end.before(start)) { 
negative = true; 
Timestamp temp = start; 
start = end; 
end = temp; 

GregorianCalendar cal = new GregorianCalendar(); 
cal.setTime(start); 
cal.set(Calendar.HOUR_OF_DAY, 0); 
cal.set(Calendar.MINUTE, 0); 
cal.set(Calendar.SECOND, 0); 
cal.set(Calendar.MILLISECOND, 0); 
GregorianCalendar calEnd = new GregorianCalendar(); 
calEnd.setTime(end); 
calEnd.set(Calendar.HOUR_OF_DAY, 0); 
calEnd.set(Calendar.MINUTE, 0); 
calEnd.set(Calendar.SECOND, 0); 
calEnd.set(Calendar.MILLISECOND, 0); 
if (cal.get(Calendar.YEAR) == calEnd.get(Calendar.YEAR)) { 
if (negative) 
return (calEnd.get(Calendar.DAY_OF_YEAR) - cal.get(Calendar.DAY_OF_YEAR)) * -1; 
return calEnd.get(Calendar.DAY_OF_YEAR) - cal.get(Calendar.DAY_OF_YEAR); 

int days = 0; 
while (calEnd.after(cal)) { 
cal.add (Calendar.DAY_OF_YEAR, 1); 
days++; 

if (negative) 
return days * -1; 
return days; 


Register your data utility in <windchill>/codebase/service.properties.xconf and run xconfmanager -p to propagate 
the changes. 
 
<Service name="com.ptc.core.components.descriptor.DataUtility"> 
<Option serviceClass="ext.custom.datautilities.TextColorDataUtility" requestor="java.lang.Object" selector="DaysBetween"/> 
</Service> 
 
First: You can call it for attributes using Type and Attribute Management. 
For eg. 
  
Go to Type and Attribute Manager -> 
Part(Get into Edit Mode) -> 
Layouts(Choose a layout) -> 
Add Data Utility Id to the desired attribute -> 
Save the changes and exit. 
 
 

 
UI Validation 

Objective 

You want to hide an action or attribute in the UI based on some context information. 

● You want to determine whether or not an action selected in the UI should be allowed to proceed based on some context 
information. 
● You want to determine whether or not a user can proceed to the next step in a wizard or whether the entire wizard may be 
submitted, based on the data entered by the user in that wizard. 

Background 

UI  Validation  is  intended  to  simplify  the  experience  of  the  Windchill  end-user.  There  are  three  categories  of  UI  Validation  that will each 
be discussed in further detail in this document. 

● Pre-Validation 
● Post-Select-Validation 
● Post-Submit Validation 
Pre-Validation 

The  first  category  of  UI  Validation  is  referred  to  as  Pre-Validation.  This  is  the  category  of validation that most people will first associate 
with  UI  Validation.  Pre-Validation  is  a  term  that  describes  the  process  of  determining  whether  or  not  a  UI  Component  should  be 
available  in  the  UI.  An  example  of  Pre-Validation  would  be  disabling  the  “check-in”  action  for  an  object  that  is  not  checked-out. 
Pre-Validation  can  be  applied  to  both  actions  and  attributes  in  the  UI.  Of  the  three  types  of  UI  Validation,  this  type  is  the  most 
often-used. 

Post-Select Validation 

A  second  category  of  UI  Validation  is  Post-Select  Validation.  Post-Select  Validation  is  the  process  of  determining  whether  or  not  an 
action  should  be  allowed  to  proceed  once  it  is  selected  in  the  UI.  An  example  of  post-select  validation  would  be  displaying  an  error 
message  and  not  allowing  the  checkout  to  proceed  if  a  user  tries  to  perform  a  checkout  on  an  object  that  is  already  checkedout. 
Post-Select Validation applies only to actions. 

Post-Submit Validation 

The  final  category  of  UI  Validation  is  Post-Submit  Validation.  This  type  of  validation  is  used  exclusively  in  wizards  or  other  forms  where 
users  enter  data.  An  example  of  Post-Submit  Validation  would  be  stopping  a  user  from  moving  to  the  Presenting  Information  in  the  UI 
next  step  in  a  wizard  because  the  data  they’ve  entered  in  the  current  step is invalid. Post-Submit Validation applies only to wizard steps 
and wizard submissions. 

Scope/Applicability/Assumptions 

• Pre-Validation - Suppose you want to hide an action in the UI from users who are not members of the current container’s team. 

•  Post-Select  Validation  -  After  a  user  selects  an  action,  you  want  to  determine  whether  or  not  the  target  object  is  in  a  certain  lifecycle 
state before allowing the action to proceed. 

•  Post-Submit  Validation  -  After  a  user  enters  data  in  the  first  step  of  a  wizard  and  tries  to  navigate  to  the  next  step,  you  want  to 
determine whether or not the information entered on the first step is valid before allowing them to proceed. 

Intended Outcome 
•  Pre-Validation  -  Before  the  page  is  rendered,  you  are  able  to determine whether or not the user is a member of the current container’s 
team. If not, the action is not displayed on the page. 

•  Post-Select  Validation  -  After  the  user  invokes  the  action,  you  are  able  to  check  the  target  object’s lifecycle state. If the state is not the 
state you require, you can display a message to the user, and the action is not performed. 

•  Post-Submit  Validation  -  When  the  user  clicks  “next”  on  the  wizard,  you  get  the  data  entered  in  the  current  step  and  are  able  to 
determine  whether  or  not it is adequate to allow the user to proceed to the next step. If the data is inadequate or invalid, you can display 
a message to the user and not allow them to proceed to the next step. 

Solutions 

•  Pre-Validation  -  Determine  whether  the  business  logic  is  specific  to  a  single  action  or  attribute,  or  if  the  same  logic  could  apply  to 
multiple  actions  or  attributes.  If  the  logic  is  specific  to  a  single  UI  Component  (action  or  attribute),  add  the  logic  to  a  Validator.  If  the 
logic could apply to multiple UI 

Components,  add  the  logic  to  a Filter. Finally, associate the Validator or Filter with the UI Component to ensure that the business logic is 


applied to that component. 

○  Pre-Validation  in  a  Validator  -  Implement  the  performFullPreValidation()  and  performLimitedPreValidation() methods in a Validator to 


contain the desired business logic. 

○ Pre-Validation in a Filter - Implement the preValidateAction method in a Filter to contain the desired business logic. 

•  Post-Select  Validation  -  Implement  the  validateSelectedAction()  and  validateSelectedMultiSelectAction()  methods  in  a  Validator  to 
define the desired business logic, and associate the Validator with the action. 

•  Post-Submit  Validation  -  Implement  the  validateFormSubmission()  method  in  a  Validator  to  define  the  desired  business  logic,  and 
associate the Validator with the wizard step or the entire wizard. 

Prerequisite Knowledge 

To achieve this objective, you need to have an understanding of the following: 

● The actions framework in the Windchill client architecture. The Application Context or service.properties mechanism for 
registering delegates. 
● A basic familiarity with the NmCommandBean and its methods will be helpful. 
● The validation service is very generic in nature. It does not provide the APIs you’ll need in order to 
determine whether or not something is valid. The service will provide your validators or filters with the context data and form data 
you need to determine whether or not a UI Component is valid. But you will need to know what to do with that data in order to 
apply your validation business logic. 

 
Preference Framework 
The Preferences Framework is based on the principle that a unique preference consists of the following attributes: 
● Parent Node (or root node if at the top of the hierarchy) 

● Preference Node (usually associated as a group of similar preferences) 


● Preference Key 

Together these attributes form a unique key structure of parent/node/key. This unique key structure will be referred to as the fully qualified 
preference key. To separate individual user and group preferences for the same fully qualified preference key, a context is applied to the 
preference. 
The context consists of the following elements: 
● Macro — a constant defining the type of context (see below) (optionally) Descriptor — text defining the name of the context. 

● These elements are placed together with a ’:’ to form the Preference Context. 

The fully qualified preference key when placed together with a context will form a unique row in the database table, allowing users, and other 
divisions to have individual preferences. 
Preference Macros 
The wt.prefs.WTPreferences class defines the following types of Preference Context Macros: 
● USER_CONTEXT - the context for individual users 

● DEFAULT_CONTEXT - the context for the system default (shipping) values 


● CONTAINER_CONTEXT - a context used in the container hierarchy 
● CONTAINER_POLICY_CONTEXT - a container context that is enforced as a policy 
● DIVISION_CONTEXT - the context used for any scopes defined in addition to the default, container, and user scopes 
● DIVISION_POLICY_CONTEXT - a division context that is enforced as a policy 

Generic UI Customizations 
Setting the Hierarchy 
The delegates.properties value wt.prefs.delegates.DelegateOrder controls the hierarchy in which 
delegates are called. For each level in the hierarchy there should be an entry in this property. The customized entries should appear as 
DIVISION_CONTEXT. For example, in the out-of-the-box hierarchy, there is a division scope called Windchill Enterprise, and the out-of-the-box 
wt.prefs.delegates.DelegateOrder property value is: 
$DEFAULT,$CONTAINER,$DIVISION:WindchillEnterprise,$USER 
In this value, there is no DIVISION_POLICY_CONTEXT defined since 
DIVISION_POLICY_CONTEXT and DIVISION_CONTEXT are related and are at the same level in the preference hierarchy. Similarly, the 
CONTAINER_POLICY_CONTEXT need not be included. Entries are designated differently only when storing and retrieving preferences internally. 
For more details on correctly naming delegates, see the delegates.properties file. If wt.prefs.delegates.DelegateOrder has been removed from 
the delegates.properties file, Windchill uses the following: 
$DEFAULT,$CONTAINER,$USERSetting Preferences Edit the file Windchill/loadFiles/preferences.txt. This file is used to put the system values 
into the database. Note that you don’t put quotes around the strings unless you actually want quotes persisted as part of the preference. 
Syntax: 
PrefEntry~keyName~default value~fullyQualifiedNodePath 
Example: 
PrefEntry~fileOperationType~ASK~/wt/content 
Getting Preferences 
You can get a preference by first navigating the preferences tree to the proper node, then setting the context for that particular user, then 
getting the value for that key. 
Example: 
// returns an instance of the top node in the Windchill preference "tree" 
Preferences root = WTPreferences.root(); 
// returns the preference node at that path 
Preferences myPrefs = root.node( "/wt/content" ); 
((WTPreferences)myPrefs).setContextMask 
(PreferenceHelper.createContextMask() ); 
// get( ), gets the value for that 
// preference key 
String prefValue = myPrefs.get( "fileOperationType", "SAVE" ); 
Clearing a Preference 
There is a big difference between "clear" and "remove". Assuming there are no division-level defaults or policies, if you "clear" a user preference 
by setting the value to be the empty string "", then the value returned will be ""; but if you "remove" the user-level preference, then the value 
returned would be system default value. In most cases you will want to remove the user-level preference and not clear it, giving the user the 
upper hierarchical preference as their default. 
Example: 
Preferences root = WTPreferences.root(); 
Preferences myPrefs = root.node( "/wt/content" ); 
((WTPreferences)myPrefs).setEditContext 
(PreferenceHelper.createEditMask()); 
((WTPreferences)myPrefs).setContextMask 
(PreferenceHelper.createContextMask()); 
String prevValue = myPrefs.remove("fileOperationType"); 
 
Preference Registry 
The preference registry is a way to take a cryptic name like a preference and 
provide a localized series of data about it. This registry is in the form of rbInfo 
files. Anyone adding preferences to the system will have the option of adding this 
localized information to the Preference Registry. Adding a preference to the preference registry 
To add a Preference to the Preference Registry, you need to edit the file <Windchill>src/wt/prefs/registry/prefRegistry.rbInfo and for the 
Preference you want to add, add at least: 
DISPLAY_NAME 
DESCRIPTION 
DEFAULT 
The format is as follows: 
/node-name/key-name% [ ]tag.value= 
Where /node-name is the name of the node (for example /wt/workflow), /key-name is the name of the key under the node (SortOrder) and % [ ]tag 
is one of the tags mentioned above (% [ ]DISPLAY_NAME). 
 
Generic UI Customizations 
Creating a Preference 
 
The creation of a preference is done through an XML load file. When creating a preference the following pieces of information need to be 
determined: 
● Unique preference name 

● Visibility: if the preference will appear in the preference manager UI and visible at what contexts: SITE, ORGANIZATION, CONTAINER or 
USER . 

● Preference category: The category the new preference will appear under in the Preference Management utility. 

● Display name: This is the Name column in the Preference Management utility – string in the form <RBINFO>:< RBINFO key> 
● Description: This is the Description column in the Preference Management utility – string in the form: <RBINFO>:<RBINFO key> 
● Long Description: This description is displayed in the Edit Preference UI, gives a more detailed description including the expected values. 

string in the form <RBINFO>:<RBINFO key> 

Default value 

Value header 

In the example below, we will create a new preference named /com/mycompany/MyNewPreference along with an associated preference 
category to appear in the Preference Manager UI. 

1. Create a resource bundle for labels used for your new preference. Labels are needed for display name and description of preference 
category which the new preference will be visible under in Preference Management UI. Labels are also needed for the display name, 
description and long description of your preference. Create the file mycompanyPreferenceResource.rbInfo 
in package com.mycompany.pref. In this example, this file would be added to the <Windchill>/src/mycompany/pref directory. 

ResourceInfo.class=wt.tools.resource.StringResourceInfo 
ResourceInfo.customizable=true 
ResourceInfo.deprecated=false 
# Preference Category labels 
MyNewPreferenceCategory.displayName.value=My Preference Category 
MyNewPreferenceCategory.description.value=Preference Category for my custom preferences. 
# Preference Definition labels 
MyNewPreference.displayName.value=Display name of preference /com/mycompany/MyNewPreference 
 
MyNewPreference.description.value=Description of preference /com/mycompany/ 
MyNewPreference. 
MyNewPreference.longDescription.value=Long description of preference 
/com/mycompany/MyNewPreference. 
 
2. Build the resource bundle by executing the following command from a 
windchill shell: ResourceBuild com.mycompany.pref.mycompanyPreferenceResource 
 
3. Restart the servlet engine and the MethodServer. 
 
4. Create Preference load file: createMyNewPreference.xml. It will contain a definition for the new preference category and new preference 
definition. 
<?xml version="1.0"?><!DOCTYPE NmLoader SYSTEM "standardX10.dtd"> 
<NmLoader> 
<csvPreferenceCategory handler="wt.preference.LoadPreference.createPreferenceCategory"> 
<csvname>CUSTOM_PREFERENCE_CATEGORY</csvname> 
<csvparentName></csvparentName> 
<csvdisplayName>com.mycompany.pref.mycompanyPreferenceResource: MyNewPreferenceCategory.displayName</csvdisplayName> 
<csvdescription>com.mycompany.pref.mycompanyPreferenceResource:MyNewPreferenceCategory.description</csvdescription> 
</csvPreferenceCategory><csvPreferenceDefinitionhandler="wt.preference.LoadPreference.createPreferenceDefinition"><csvname>/com/mycom
pany/MyNewPreference</csvname> 
<csvvisibility>USER</csvvisibility> 
<csvcategoryName>CUSTOM_PREFERENCE_CATEGORY</csvcategoryName> 
<csvdisplayName>com.mycompany.pref.mycompanyPreferenceResource:MyNew 
Preference.displayName</csvdisplayName> 
<csvdescription>com.mycompany.pref.mycompanyPreferenceResource:MyNew 
Preference.description</csvdescription> 
<csvlongDescription>com.mycompany.pref.mycompanyPreferenceResource:MyNewPreference.longDescription</csvlongDescription> 
<csvdefaultValue>Default Value</csvdefaultValue> 
<csvhandler>com.ptc.windchill.enterprise.preference.handler.StringPreferenceValueHandler:4000</csvhandler></csvPreferenceDefinition> 
<csvLinkPreferenceClientDefinition handler="wt.preference.LoadPreference.set 
ClientDefinitionLink"> 
<csvname>/com/mycompany/MyNewPreference</csvname> 
<csvclientName>WINDCHILL</csvclientName> 
</csvLinkPreferenceClientDefinition> 
</NmLoader> 
 
5. Load the preference category and preference definition using the following 
command: 
windchill wt.load.LoadFromFile -d <fullpath>/createMyNewPreference.xml 
 
Deleting a Preference 
The deletion of a preference is also done through the use of an XML load file. we will delete the preference 
/com/mycompany/MyNewPreference. The deletion of the preference will also remove any preference instances which may have been set for this 
preference in the UI. 
 
1. Create an XML file, deleteMyNewPreference.xml, containing the following definition to specify the deletion of the preference. 
<?xml version="1.0"?><!DOCTYPE NmLoader SYSTEM "standardX10.dtd"> 
<NmLoader> 
<csvDeletePreferenceDefinitionhandler="wt.preference.LoadPreference.deletePreferenceDefinition"> 
<csvname>/com/mycompany/MyNewPreference</csvname> 
</csvDeletePreferenceDefinition> 
</NmLoader> 
 
2. Delete the preference definition using the following command: 
windchill wt.load.LoadFromFile -d <fullpath>/deleteMyNewPreference.xml 
 
 

 
Customizing Column Lengths 

A  column  length  for  a  modeled  attribute  can  be  customized.  These  column  lengths  are  obtained  through  the  ​wt.introspection  package.  The 
value  of  this  property  can  be  overridden  by  placing  entries  in  the  customizations  property  file  for  modeled  packages.  To  change  the  column 
length for a modeled attribute, perform the following steps:  

1. Determine customization property for the attribute 


2. Override attribute length in appropriate customizations property file 
3. Generate the class info objects 
4. Verify the customization. 
5. Alter the database Tables 
6. Restart the method servers if they were running during the procedure. 
The  following  example  sets  the  column  length  for  the  name  attribute  of  the  ​wt.doc.WTDocumentMaster  class  to  hold  350  characters.  The 
following  steps  describe  how  to  determine  which  customizations  property  file  entry  contains  the  new  column  length,  and  how  to  set  the  value. 
The  customization  is  made  in  a  location  parallel  with  the  originally  modeled  attribute.  The  default  location  for  these  customizations  is 
$(wt.home)\wtCustom​, as defined by the ​wt.generation.custom.dir​ entry in the tools properties. Create this directory if it does not already exist. 

1. Determine which customization property entry must be added: 


a. Obtain an info report for the class by executing the following command from a Windchill shell: 
infoReport wt.doc.WTDocumentMaster 
The info report file is created in ​<Windchill>/temp​ and is named ​doc.WTDocumentMaster.out​ for this class. 
b. Inspect the value of the ​WTIntrospector.UPPER_LIMIT​ property (the value being customized) of the name PropertyDescriptor 
getValue( WTIntrospector.UPPER_LIMIT ) : 60 
c. Inspect the value of the ​WTIntrospector.DEFINED_AS​ property of the name ​PropertyDescriptor​: 
getValue( WTIntrospector.DEFINED_AS ) : wt.doc.WTDocumentMaster.name 
d. Based on this information, use the following values: 
▪ The customization property file is ​<Windchill>\wtCustom\wt\doc\docModel.properties​. 
▪ The customization property entry is ​WTDocumentMaster.name.UpperLimit​. 
2.  Add  the  customization  property  entry  to  the  appropriate  customizations  property  file.  In  this  example,  add  the  following  entry  to 
<Windchill>\wtCustom\wt\doc\docModel.properties​ (create this file if it does not exist): 
WTDocumentMaster.name.UpperLimit=350 
 
# Ignore multi-byte database character sets when setting value. 
# UpperLimit is constrained by database VARCHAR limit : max value is 
4000/wt.db.maxBytesPerChar 
3.  Generate  the  class  info  objects.  Update  the  serialized  info  object  by  entering  the  following command from a 
Windchill shell: 
ant -f bin\tools.xml custom_column -Dgen.input=wt.doc 
This  updates  introspection  information  for  descendant  classes  that  are  concrete,  for  example  ​wt.federation.ProxyDocumentMaster  in  the 
case of ​ wt.doc.WTDocumentMaster​. 
4.  Verify  the  customization.  Obtain  an  info  report  for  the  class  and  inspect  the ​UPPER_LIMIT value as described in the preceding steps. The 
value  should  reflect  the customization.  If the info report value is unchanged, verify that the generate step actually updated the following 
serialized info file: 
<Windchill>\codebase\wt\doc\WTDocumentMaster.ClassInfo.ser 
5. Adjust the length of the customized column by taking the following steps: 
a. Execute the Windchill Upgrade Manager to run the Compare Schema step only. 
Start the Windchill Upgrade Manager within a Windchill shell as follows: UpgradeManager -cs 
b. Inspect the generated output to find the SQL script(s) related to this customization in: 
<Windchill>\Upgrade\UpgradeReports\CompareSchema\CompareSchema-​<timestamp>​.html#AlteredTables  -  section  'Tables  to  be 
Altered'​. 
Tables corresponding to descendant classes must also be altered (e.g. ​PROXYDOCUMENTMASTER​). 
Review  the  output  carefully  to  ensure  the  results  are  what  you  expect.  You  should  never  execute  any  SQL  that  you  do not 
understand, or that does not seem related to your intended customizations  

c. Execute the relevant SQL scripts as Windchill database schema owner. 


In  this  example,  ​WTDocumentMaster.name  is  also  the  source  for  the  derived  attribute  ​WTDocument.name​.  This  derived  attribute, 
WTDocument.name​,  gets  its  ​UpperLimit  from  the  source  attribute,  ​WTDocumentMaster.name​.  A  derived  attribute  cannot  set  the  ​UpperLimit 
property in the annotation. Therefore, the derived attribute cannot be customized in this manner. 

 
 

You might also like