Advanced Customization > Services and Infrastructure Customization > Developing Server Logic > Implementing Business Data Types
  
Implementing Business Data Types
A business data type can be characterized as an entity object (that is, a knower) that deals basically with abstracting some crisp and clearly definable piece of information in the problem domain. Therefore, business data types are primarily defined by data, not behavior, as in control objects (that is, doers). Because most of the specification of a business data type is by its attributes, implementation of this business information typically focuses on the classes’ attributes.
In addition, because business data types are entity objects and are typically lightweight compared to control objects, they efficiently transport information between an application and the server in the three-tier (client, server, and database) architecture.
Initializing Business Attributes
Initialization of business data type attributes is done by implicitly code generated "initialize" methods. No constructors are generated for modeled business data types. Instead, factories are created for each modeled constructor and, within the factory, an instance is made. Then the initialize method with a signature that matches the factory is invoked.
These initialize methods are empty at the first code generation and must be hand- implemented because the code generator currently cannot make any assumptions on what attributes should or should not be initialized. By default, once these methods are implemented, their code is preserved for subsequent use. To mimic Java constructor chaining, each initialize method should always invoke its parent initialize method before doing anything else.
Business Attribute Accessors
Attributes are always generated as private fields with two accessors: a getter and setter method, like JavaBean properties. The getter is generated as a no-arg method that returns the value of the attribute.
The setter is generated as a single-arg method which sets the attribute to the value of the arg. Depending on whether the attribute is constrained, the setter is generated with or without a wt.util.WTPropertyVetoException throws clause. This exception extends java.beans.PropertyVetoException and allows for a customized message.
Note that the body of the methods are flagged as "preserve=no." This instructs the code generator to overwrite the code within a method. Getters and setters can be preserved by setting this flag to "yes", but in general this is not recommended. On the other hand, the code generator can be instructed to not generate a getter and setter for an attribute with the "GenerateAccessors" property on the Windchill tab set to "False."
Overriding Accessor Methods
Typically, overridden accessors do the following:
Attribute validation (as shown in the example)
Lazy attribute initialization
Computed attribute access
The following is an example of overriding accessor methods:
Validating Business Attributes
Validating business data type attributes is handled at two distinct levels. The simplest and completely code-generated level is implemented as a validation method for a constrained attribute that is invoked by its setter. The mechanism used to veto the setting of an attribute is a wt.util.WTPropertyVetoException capable of being thrown from the setter. If an attribute is not constrained, the setter is generated without the capability of an exception being thrown.
The properties for specifying limits on a String or numeric attribute are LowerLimit and UpperLimit. In both cases, the value supplied is treated as a literal or constant. This means that the validation method is code generated with "if" statements that use Java less than or greater than operators to test against the specified value in the LowerLimit or UpperLimit property, respectively. The following examples illustrate the use of these two properties and the validation code that is generated automatically:
The other more general level of validating one or more attributes is to override and implement the "checkAttributes" method inherited from wt.fc.WTObject. This method is invoked before the object is stored in the database initially and every time it is modified. In this case the exception thrown is wt.fc.InvalidAttributeException, not wt.util.WTPropertyVetoException.
Implementing the checkAttribute Method
The checkAttributes method shown in the following example is an alternative to the setStatus method shown in the example in Overriding Accessor Methods earlier in this section. It ensures that closure comments are provided if the status is to be set to 2.
The checkAttributes method is called by the PersistenceManager to ensure the state of the object is correct before storing it in the database. The following is an example of implementing the checkAttribute method:
Business Attribute Aggregation
Structured attributes (that is, non-first-class) at all levels of associations are placed in the same database table as the first-class object that stores the structure attributes. The aggregated attributes must be object mappable and, regardless of cardinality, will always exist in the database table as a flat structure. The flatness of the tables is due to the fact that the database tier in the three-tier architecture is assumed to be a relational database, not an object-oriented database.
However, if the top-level structured attribute’s cardinality is 0..1 or 0..* and it is not flagged as being required, it can be nullified in the database. Care must be taken if this top-level structured attribute aggregates any other structured attribute because the nested structured attributes will not be nullified.
Business Attribute Persistence
A business attribute is persisted as a column in a first-class object’s database table if it is flagged as being "Persistent". Otherwise, it is treated as an in-memory attribute only and is not mapped to a column in the database table.
Business Attribute Derivation
Derived attributes are strictly generated as getters and setters without any such named field for the modeled derived attribute. Typically, the purpose of modeling a derived attribute is to make it act as a computed value when accessed. One side effect of derived attributes is they are neither serializable nor externalizable. They could be an implementation detail in the body of a class, but Windchill’s generated externalization methods will not recognize the attribute as existing. That is, the externalization methods to read and write an object’s data are generated based on what is known in the object model, not the implementation.
If an object with a derived attribute is either being transmitted across-the-wire, or is being stored/retrieved externally, the derived attribute within it is non-existent. Any assumption made in implementation where a derived attribute is required for processing after the object has been serialized or externalized may produce erroneous results unless the accessor is called to re-derive the attribute.
Implementing Business Services
A business service can be characterized as a set of abstractions — based on the business service design pattern — that collectively provide essential behavior and processing for a problem domain. The main kinds of classes in a business service act as control objects (that is, doers) that carry out processing on business objects. Additionally, cookies are maintained by the business service that hold state and key information on a per object basis.
Cookie classes are aggregated to interfaces that are managed by business services. For example, PersistInfo is a cookie class that is aggregated to the Persistable interface. Any class that implements Persistable will also aggregate PersistInfo, and the PersistenceManager business service will manage the attributes of PersistInfo such as createStamp and modifyStamp.
Initializing Business Services
Business services are designed to execute as singletons in the server only. When the server is launched, the wt.properties file is read to determine what services are to be started automatically. These specified services are constructed and statically initialized, service events are registered, and the services are then started. The ordering specified by wt.services.service entries in the wt.properties file controls the order in which services are started. If administrative access is required on service startup, it is necessary to create a new SessionContext and set the current user to the Administrator. It is equally important to reset the session context at the end of a service’s startup processing. To guarantee that a session context is always reset, it should be done in a "finally" clause.
Business Service Operations
Business service operations can be invoked either locally or remotely. Only remote invocations are made by a client. Local invocations can be either from a client or server.
The business service’s Helper abstraction contains only methods that can be invoked locally. These methods are generally used for the accessing of the service’s cookie information. Other types of methods that aid in processing may also be implemented in a Helper. The Service abstraction contains methods that can be invoked locally on the server and invoked remotely from the client if the Service is stereotyped as being a "RemoteInterface." With this stereotyping and aggregation of the "service" in the helper, all public methods are available for use. The business service should ensure that all externally available operations are exposed either through the Helper or Service class. All other operations should only be available internally with the possible exception of methods on utility classes that are used by other services.
Often business service operations executing on the server must perform database actions. To maintain database integrity, service independence, and loose coupling between other services, database changes should be protected by the use of transaction blocks. The following section of sample code serves as a guideline on how to implement transaction blocks:
Vetoing Business Service Events
When a business service is notified of an event occurring within the system, it has the option of vetoing the event. This is done by subscribing as a listener and implementing the notifyVetoableEvent method. The listener must process the event’s data and determine if a veto is applicable. If so, the listener throws an appropriate exception. Otherwise, the listener does whatever processing is required and allows the event to continue.
Business Service Rules
If applicable, a business service should implement rules to enforce consistent and correct functionality of its operations. The two basic forms of business rules are access control and problem domain integrity.
Access control establishes who can act upon what and in what manner. The enforcement of access control in a business service is explicitly implemented. In the locking service, for example, when an object is requested to be locked, the service guarantees that the principal attempting to place the lock has modify access to the object such that the lock for it can be persisted in the database as shown below:
Enforcement of problem domain integrity is somewhat different than access control. Maintaining correct problem domain behavior within the business service should always be a goal, but in particular there may be some special requirements that must be satisfied. As in version control, for example, when an object is checked in, it is then once again available to be checked out. However, if the object is already checked out, it can not be checked out again unless some sharing and merging rules are in place.
Business Service Communication
Business services can communicate with one another either directly through synchronous Java invocations or by events. Direct peer-to-peer communication to another service is how access control is implemented in all of Windchill’s services. This is the most rudimentary means of communication.
Event-based communication is a more sophisticated means of communicating between services. Services can register their events and subscribe listeners to another service’s events when they are started. When a service event is emitted, all subscribed listeners are notified of the occurrence of the event. The event notification and responses are synchronous. Therefore, it is equivalent to method invocations to the services, except that services which cause events do not know which other services will be notified. If one of the notified services vetoes the event, the other services not yet notified are not informed.