Upload
subhodeep1988
View
388
Download
6
Embed Size (px)
Citation preview
Java Application Development
Oracle Utilities Framework
2
Day 1 Agenda
• Logical Architectural Overview• OLTP• Batch
• History• Version V1.x• Version V2.x
• Development, Extension and Customization Overview• The Artifact Generator• Entities
• Entity Classes• Entity CRUD• Querying Entities
Logical Architectural Overview
4
Logical Web Architecture
• Information is presented in HTML and JavaScript (no Java applets) through Internet Explorer 6.0
• The browser communicates with the Web App Server via HTTP
HTML
JavaScript/XSLT
Session State
HT
TP
Web Browser
5
Logical Web Architecture
• The Web App Server is divided into logical tiers:• Presentation services• Business logic• Data access
HTML
JavaScript/XSLT
Session State
Presentation
Servlets/XSLT
Caches
Data Access
Persistent Entities
Hibernate
JDBC
Business
Services
Data Service Objects (Java/COBOL)
HT
TP
Web Browser Web Application Server
6
Logical Web Architecture
• In the Presentation Layer …• Java servlets handle the inbound HTTP requests from the browser
• Various static data – e.g. control tables, language-specific messages and labels – are cached
• XSL/T technology is used to create the HTML
• The servlets invoke the data service objects in the business layer as required
HTML
JavaScript/XSLT
Session State
Presentation
Servlets/XSLT
Caches
Data Access
Persistent Entities
Hibernate
JDBC
Business
Services
Data Service Objects (Java/COBOL)
HT
TP
Web Browser Web Application Server
7
Logical Web Architecture
• In the Business Layer …• Data service objects are invoked from the presentation layer• These services ultimately perform the business functions• The service objects themselves are Java, but the business logic
could be implemented in Java or COBOL, or a combination• The data access objects are used to communicate with the
database
HTML
JavaScript/XSLT
Session State
Presentation
Servlets/XSLT
Caches
Data Access
Persistent Entities
Hibernate
JDBC
Business
Services
Data Service Objects (Java/COBOL)
HT
TP
Web Browser Web Application Server
8
Logical Web Architecture
• In the Data Access Layer …• The Hibernate Object Relational Model (ORM) framework is used for
database access and persistence
• The Java Database Connectivity API (JDBC) is used directly (i.e. Hibernate is bypassed) for SQL statements from COBOL
• All database calls are routed through the framework’s data access layer – application programs do not issue direct calls to database
HTML
JavaScript/XSLT
Session State
Presentation
Servlets/XSLT
Caches
Data Access
Persistent Entities
Hibernate
JDBC
Web Browser Web Application Server
Business
Services
Data Service Objects (Java/COBOL)
HT
TP
9
Logical Web Architecture
• Database• Framework supports …
• Oracle
• DB2
• Microsoft SQL Server.
HTML
JavaScript/XSLT
Session State
Presentation
Servlets/XSLT
Caches
Data Access
Persistent Entities
Hibernate
JDBC
Web Browser Web Application Server
Business
Services
Data Service Objects (Java/COBOL)
HT
TP
Oracle
DB2
MSSQL
Database
10
Logical Batch Architecture
• Batch Processing• A scheduler or the command-line is used to invoke the splbatch script
(the scheduler can be a 3rd party OUF’s “cdxcronbatch” scheduler)
• splbatch starts a batch instance of the framework
• The framework controls all batch execution
• Once initialized, splbatch prompts for the batch code of the batch process to execute
splbatch.sh / cmd(command line)
Scheduler
Etc.
splbatch.sh / cmd(command line)
Scheduler
Etc.
Submission
11
Logical Batch Architecture
• Batch Processing• The Standalone Executor invokes the Java/COBOL batch
process, based on the supplied batch control code• The business layer components perform the business functions
(same as online)• The data access layer is used to communicate with the database
(same as online)
splbatch.sh / cmd(command line)
Scheduler
Etc.
splbatch.sh / cmd(command line)
Scheduler
Etc.
Batch Control
Standalone Executor
Batch Process
Data Access
Persistent Entities
Hibernate
JDBC
Submission Batch Server
Business
Data Service Objects (Java/COBOL)
Oracle
DB2
MSSQL
Database
12
Embedded Batch Architecture (option)
• Starting with FW 2.1.0 batch processing can optionally take place on the Web Application Server:
• A scheduling daemon polls for jobs submitted by the online• The local business tier executes the work
Batch Control
Standalone Executor
Batch Process
Data Access
Persistent Entities
Hibernate
JDBC
Web Application Server
Business
Data Service Objects (Java/COBOL)
Oracle
DB2
MSSQL
Database
13
Servers<BizTalk><Body> <AccountInfo> <Request docTynfo"> <Account> <company>99ny> <accountId>5ntId> <area>001</area> </Account> </Request> </AccountInfo> </Body></BizTalk>
XML: Web Services Integration
Real timeinterfaces:
CRM, MS Office,Web Self Service, etc.
DBMS
UI
…etc…
Mass recordIntegration
Outside Agents
Meter data
Billable Charges
ERP / GL
<BizTalk><Body> <AccountInfo> <Request docTynfo"> <Account> <company>99ny> <accountId>5ntId> <area>001</area> </Account> </Request> </AccountInfo> </Body></BizTalk>
<BizTalk><Body> <AccountInfo> <Request docTynfo"> <Account> <company>99ny> <accountId>5ntId> <area>001</area> </Account> </Request> </AccountInfo> </Body></BizTalk>
<BizTalk><Body> <AccountInfo> <Request docTynfo"> <Account> <company>99ny> <accountId>5ntId> <area>001</area> </Account> </Request> </AccountInfo> </Body></BizTalk>
Staging
<BizTalk><Body> <AccountInfo> <Request docTynfo"> <Account> <company>99ny> <accountId>5ntId> <area>001</area> </Account> </Request> </AccountInfo> </Body></BizTalk>
<BizTalk><Body> <AccountInfo> <Request docTynfo"> <Account> <company>99ny> <accountId>5ntId> <area>001</area> </Account> </Request> </AccountInfo> </Body></BizTalk>
<BizTalk><Body> <AccountInfo> <Request docTynfo"> <Account> <company>99ny> <accountId>5ntId> <area>001</area> </Account> </Request> </AccountInfo> </Body></BizTalk>
Mass XML Integration
XML Staging
XAIMPL
Integration Architecture
History
Version 1.x
16
Page Server(Servlet/JSP)
V1.x Technical Architecture Web Server to App Server
Handles service
distribution
WebApplication
Server
BusinessApplication
Server
DatabaseServer
Data Page Conduit
(EJB)
XML
Jolt
WebLogic
TuxedoBusiness Object Conduit
Contains• Tuxedo commun.• Flattener / expander
Business Object
(COBOL)
Tuxedo
17
PersonCI_PER
NameCI_PER_NAME
IDCI_PER_ID
PhoneCI_PER_PHONE
CharCI_PER_CHAR
The entities that hold person information
3 Types Of Programs Involved With The Maintenance Of Objects
• Let’s use an example of the programs used to maintain the person object:
PersonPage
PersonRow
NameList
IDList
PhoneList
Char.List
NameRow
IDRow
PhoneRow
Char.Row
Page programs orchestrate the calls to the various row and list programs
List programs access and updates the rows in a collection. Note, we only update rows that have been changed. You will also find inter-row validation here
Row programs perform validation and updates on a specific row
Page programs perform inter-record validation after row validation / update, but prior to commit
18
PersonPage
PersonRow
NameList
IDList
PhoneList
Char.List
NameRow
IDRow
PhoneRow
Char.Row
PersonWrapper
Issues the rollback and commit so that other programs can call the page program
Where’s The Commit / Rollback
• Tuxedo doesn’t call the page program, it calls the page program’s wrapper• The commit / rollback is issued by the page’s wrapper (the program that
Tuxedo invokes)
Tuxedo
Version 2.x
Transition to Java
20
Transition Strategies
• Persistence• Java persistence layer based on Hibernate that interoperates with
existing COBOL Row program persistence.
• Services• Java service implementation as an alternative to COBOL Page,
List, Search services• A service dispatcher that dispatches service requests
implemented in either Java or COBOL
• Business Interoperability• Business logic implemented in Java, COBOL or scripts can call
each other, usually without knowing the implementation technology of the called code.
21
Business Interoperability
• Expand COBOL• Preprocesses COBOL code to make JNI calls to Java hosting it’s
execution
• AbstractCobolProgram• A Java program that wraps a COBOL program so it can be called
from Java. Typically, COBOL copybooks are processed and a Java API is generated to make programming calls to COBOL easy.
• JavaToCobolGateway/CIPZCTJN• Generalized facility to call arbitrary static Java methods from
COBOL by passing COBOL copybook structures
• Database Session• One consistent session is shared among Java, COBOL and
scripting participants in a business transaction
22
Native Execution Hosting
Native Business
Objects
J2EE Web Application Server
Java Business
Objects
Primary ChildJVM
Non-TCP Communication
Protocol
Failover ChildJVM(s)
Development, Extension and Customization
24
Resources
• Wiki• http://sf-morpheus.splwg.com/ccb• The “CookBook” is the best place to start for most things• Walk-through: look at the wiki
• Product documentation• //documentation/User Doc (Multi Product)
• JavaDocs• Generally, the code is well commented and JavaDocs show up in Eclipse
quite easily.
• Use cases• How are classes used by other classes• Junit tests
• A fantastic resource for discovering how a developer intended a class to be used
• A fantastic resource for discovering that the developer misunderstood the requirements or didn’t test his or her code
25
The Application Stack
• The running system is really a composite of stacked applications configured at runtime• Example: base, ccb, mtm, cm
• Applications initialize the lower application in the stack and then overlay higher applications
• Applications are developed by independent teams:• May not change applications lower on the stack• May not depend on applications higher on the stack
26
Perforce Locations
• Each application has it’s own root node in depot• Example: FW, C1, O2
• Within each version of the application (2.1.0, 2.2.0, etc) there are three important folders: modules, projects, tools
• Modules• Code that makes up the deliverable application• “lib” includes jar dependencies from third parties or lower
applications on the stack• “source” includes application source and tests• “dist” includes other resources/executables from third parties or
lower applications on the stack (I.e. COBOL object code)
27
Perforce Locations (cont)
• Projects• Represents files configuring the development process• Eclipse projects are configured here• Wiring for the Maven build• Configuration of the shared development servers
• Tools• Contains software needed to develop• Eclipse• Tomcat• Ant• Java
28
Introduction to Eclipse
• Walk-through• See project structure• Typical sync
• Perforce sync
• Artifact generator build
• Refresh
• Launch Tomcat
• Look at Perforce integration
29
Package Structures
• com.splwg.base.api.*• Framework APIs that applications may code against
• com.splwg.shared.*• Classes that are shared between the runtime application and the
build-time code (I.e. the Artifact Generator)• Includes some very useful classes:
• Logger
• StringUtilities
• ApplicationException
• com.splwg.mtm.domain.*• Domain logic for the “mtm” application• Typically a new package for each Maintenance Object
The Artifact Generator
An Introduction
31
What is the Artifact Generator
• We generate both Java source and XML documents via a stand-along Java program called the “Artifact Generator”
• The Artifact generator:• Parsed Java source files to extract
• Knowledge of public methods and variables
• Comments so that they can helpfully be added to generated business interfaces
• Annotations embedded in comments that define metadata coupled to the code
• COBOL source to build up Java representations of the COBOL programs and copybooks for COBOL-Java interoperability
• Database Metadata to understand the structure of Tables, Fields and Constraints
32
Why do we generate artifacts?
• To create classes that Humans can code to• Business Interfaces with proper “getters” and “setters”• Helpful methods on abstract superclasses specific to the class
that someone is coding
• To capture details only available from the source code at build time because the source code is not available at runtime• Example: the structure of COBOL copybooks
Entities
34
What are Entities?
• Entity Classes • Classes wired into the Hibernate Object-Relational Mapping
framework to provide persistence
• Entity Objects • Instances of Entity Classes representing rows on the database
• Entity Layer• The layer logically between the database and the service layer• Contains families of classes that facilitate the basic CRUD of rows
on the database
35
Entity Layer Classes
• Walk-through: Take a look at “Currency” in FW• Hand-coded persistence classes
• Entity “Impl” (Currency_Impl) defines basic persistence• Change Handler (Currency_CHandler) defines validation and
cascading changes• Message Repository (MessageRepository) defines new messages
used by this package
• Hand-coded maintenance (service layer) classes• List Maintenance (CurrencyListMaintenance) defines a service
callable via the online• Entity Code Description Query (CurrencyCodeDescriptionQuery)
defines a service that returns descriptions of the rows, ostensibly for caching
36
Generated Entity Layer Classes
• Business Interface (Currency) • Defines the interface that client objects should talk to
• ID (Currency_ID) • Defines a prime key class
• Data Transfer Object (Currency_DTO) • Defines a transient data structure holding the properties of the entity
• Entity “Gen” (Currency_Gen) • Defines the abstract superclass of the “Impl”. Mostly this give
convenient accessors to program business methods on the “Impl”.
• Language classes (Currency_Language and Currency_LanguageId) • Provides language support
• POJO (Currency_POJO and others)• Provides support for the HQL query tool. Not used by the running
system.
37
Generated Entity Configuration Files
• Hibernate Mappings (*.hbm.xml)• Loaded at runtime to wire up Hiberate
• Info Data (*.info.xml)• Provides runtime metadata about the entity for the framework
• packageMetaInfo.xml• Bootstrapping metadata used by the framework to discover and
wire up the package
38
Prerequisite Database Metadata
• Some metadata data needs to be defined in framework tables before the artifact generator is run• Table, Field, TableField and Contraint defines the structure of the
tables that the entities must map to• Lookups need to be defined because classes are generated which
allow Java code to directly reference lookup values• Maintenance Objects values drive
• Validations in the artifact generator that prevent incorrect parent-child relationships from being defined and other issues
• Maintenance generation using the “MO Wizard” which we’ll discuss later.
Working with Entities
40
Inside Business Entities• In general, a Business Entity class exists for each table in
the system• The example here illustrates the Person and Person Name
tables and their related classes
+createDTO()
AbstractBusinessEntity
+getId()+getAddress()
-id-address1
Person_Gen
Person_Impl
+getId()+getEntityName()
-id-entityName
PersonName_Gen
PersonName_Impl
Person InterfacePersonName
Interface
CI_PER
PK PER_ID
ADDRESS1
CI_PER_NAME
PK,FK1 PER_IDPK SEQ
ENTITY_NAME
41
Business Entity related classes are divided into 3 categories:1. Framework
2. Generated
3. Hand-coded
Business Entity and Related Classes
+createDTO()
AbstractBusinessEntity
+getId()+getAddress()
-id-address1
Person_Gen
+getInfo()
Person_Impl+getId()+getAddress1()+getInfo()+getDTO()()+setDTO()()
«interface»Person
AbstractDataTransferObject
Person_DTO
StringId
Person_Id
AbstractEntityList
+add()+remove()
PersonNames_Impl
«interface»SimpleEntityList
«interface»PersonNames
42
• The goal of the framework was to keep hand-coded logic to a minimum
• When creating a new Business Entity, only the “Impl” class is hand coded; others are framework or generated by the Artifact Generator
Business Entity and Related Classes
+createDTO()
AbstractBusinessEntity
+getId()+getAddress()
-id-address1
Person_Gen
+getInfo()
Person_Impl+getId()+getAddress1()+getInfo()+getDTO()()+setDTO()()
«interface»Person
AbstractDataTransferObject
Person_DTO
StringId
Person_Id
AbstractEntityList
+add()+remove()
PersonNames_Impl
«interface»SimpleEntityList
«interface»PersonNames
43
• Let’s look at how the classes below are used• Business Entities• Data Transfer Objects (DTOs)• String Ids• Entity Lists
Business Entity and Related Classes
+createDTO()
AbstractBusinessEntity
+getId()+getAddress()
-id-address1
Person_Gen
+getInfo()
Person_Impl+getId()+getAddress1()+getInfo()+getDTO()()+setDTO()()
«interface»Person
AbstractDataTransferObject
Person_DTO
StringId
Person_Id
AbstractEntityList
+add()+remove()
PersonNames_Impl
«interface»SimpleEntityList
«interface»PersonNames
44
• The Java programs typically do not call the database directly; they use the Business Entities where possible
• For example, to fetch a Person from the database
Person_Id perId = new Person_Id("1185477091");Person perEntity = perId.getEntity();
Working with Business Entities
AbstractBusinessEntity
+getId()+getAddress()
-id-address1
Person_Gen
+getInfo()
Person_Impl+getId()+getAddress1()+getInfo()+getDTO()()+setDTO()()
«interface»Person
45
Query query = createQuery("from Person");Person perEntity = null;QueryIterator qIter = query.iterate();while (qIter.hasNext()) {
perEntity = (Person)qIter.next();...
}
• To fetch multiple persons from the databaseWorking with Business Entities
AbstractBusinessEntity
+getId()+getAddress()
-id-address1
Person_Gen
+getInfo()
Person_Impl+getId()+getAddress1()+getInfo()+getDTO()()+setDTO()()
«interface»Person
This is an early introduction to our Java Persistence Framework (JPF) & Hibernate. JPF & Hibernate are discussed in detail later, but we will see more references to them over the following slides
46
• The framework creates instances of Business Entities when fetching data – for examplePerson perEntity = perId.getEntity();
or
Person perEntity = (Person)qIter.next();
will create a new instance of Person• Therefore: An instance (object) of a Business Entity class
represents a row in a database table
Working with Business Entities
PER_ID ADDRESS1 …
1550000001 Easy Street …
7804500002 Penny Lane …
…
id = 1550000001address1 = Easy Street
Person : Person_Implid = 1550000001address1 = Easy Street
Person : Person_Impl
id = 7804500002address1 = Penny Lane
Person : Person_Implid = 7804500002address1 = Penny Lane
Person : Person_Impl
47
Working with Business Entities• Creating a new instance of a Business Entity creates a new
database row (when the transaction commits)• Likewise, when changing or deleting an instance, the
database row is updated or deleted (when the transaction commits)
• The database commit happens at the framework’s discretion – the application code has no direct control over it
PER_ID ADDRESS1 …
1550000001 Easy Street …
7804500002 Penny Lane …
…
id = 1550000001address1 = Easy Street
Person : Person_Implid = 1550000001address1 = Easy Street
Person : Person_Impl
id = 7804500002address1 = Penny Lane
Person : Person_Implid = 7804500002address1 = Penny Lane
Person : Person_Impl
48
• A Business Entity has “getters” (get methods) for retrieval of its propertiesExample
Person perEntity = (Person)query.firstRow();String emailAddress = perEntity.getEmailAddress();
Will retrieve a person’s email address
• Most getters are specific to an object (e.g. emailAddress on Person), but there are common ones• getId() retrieves identifier (Account Id,
Person Id, Premise Id, etc.) depending on the object• getDTO() retrieves an instance of a Data Transfer
Object (DTO) for the Business Entity
Getting Entity Properties
AbstractBusinessEntity
+getId()+getAddress()
-id-address1
Person_Gen
+getInfo()
Person_Impl+getId()+getAddress1()+getInfo()+getDTO()()+setDTO()()
«interface»PersonMore about Ids and DTOs in a
minute…
49
• A Business Entity does not have “setters” for its properties
• Properties can only be updated via a DTO• A Business Entity has a setDTO() method to register a
modified DTO and thus update the database
Setting Entity Properties
AbstractBusinessEntity
+getId()+getAddress()
-id-address1
Person_Gen
+getInfo()
Person_Impl+getId()+getAddress1()+getInfo()+getDTO()()+setDTO()()
«interface»Person
More about Ids and DTOs in a minute…
50
String emailAddress = perDTO.getEmailAddress();...perDTO.setEmailAddress(emailAddress);
Person perEntity = (Person)query.firstRow();Person_DTO perDTO = perEntity.getDTO();
AbstractDataTransferObject
Person_DTO
• Data Transfers Objects (DTOs) are transient (temporary / non-persistent) holders of Business Entity properties
• They are acquired via the Business Entity getDTO() method, for example
• Use the DTO “getters” and “setters” (get/set methods) to retrieve or update properties, for example:
Data Transfer Objects
51
• Updating a DTO property (via “setter” method) does not update the entity (database object) automatically
• A Business Entity has the setDTO method to update a modified DTO, for example
This effectively updates the database, but the database update & commit happen at the discretion of the framework
Data Transfer Objects
AbstractDataTransferObject
Person_DTO
Person perEntity = (Person)query.firstRow();Person_DTO perDTO = perEntity.getDTO();perDTO.setEmailAddress(“[email protected]”);perEntity.setDTO(perDTO);
52
String Ids
• Simple string keys (e.g. Person Id) are defined as objects in the framework
• An ID is retrieved from a DTO or Business Entity, e.g.Person_Id perId = perDTO.getId();or
Person_Id perId = perEntity.getId();
• Use the getIdValue() method to get the value of the IdString perIdString = perId.getIdValue(); StringId
Person_Id
53
String Ids
• An Id object can also be used to retrieve a Business Entity, for example
Person_Id perId = new Person_Id("6785704944");Person perEntity = perId.getEntity();Person_DTO perDTO = perEntity.getDTO();...
StringId
Person_Id
54
Entity Lists
AbstractEntityList
+add()+remove()
PersonNames_Impl
«interface»SimpleEntityList
«interface»PersonNames
• Entity lists reference collections of entities, such as person names, phone numbers, characteristics, etc.
• They are retrieved from a Business Entity, e.g.
Person_Id perId = new Person_Id("1185477091"); Person perEntity = perId.getEntity();PersonNames perNames = perEntity.getNames();
• Elements in an entity list can be:• Sequentially retrieved• Removed• Added
55
Entity Lists
• For example, to retrieve all person names for a person:Person_Id perId = new Person_Id("1185477091"); Person perEntity = perId.getEntity();PersonNames perNames = perEntity.getNames();Iterator iterator = perNames.iterator();while (iterator.hasNext()) {
PersonName perName = (PersonName)iterator.next();
String entityName = perName.getEntityName();
...}
AbstractEntityList
+add()+remove()
PersonNames_Impl
«interface»SimpleEntityList
«interface»PersonNames
56
Creating an Entity
• To create a new Business Entity, you create the DTO first
… or you create an empty DTO from an “Id” class …
… and then create the Business Entity from the DTO
Person_DTO perDTO = (Person_DTO) createDTO(Person.class);
Person_Id perId = new Person_Id("1185477091");Person_DTO perDTO = perId.newDTO();
Note: createDTO() is inherited from the parent GenericBusinessObject class for all Business Entity or Business Component classes (e.g. Change Handlers)
perDTO.setId(new Person_Id(“1185477091”));perDTO.set...Person person = perDTO.newEntity();
57
Deleting an Entity
To delete a Business Entity, you use the delete method on the entity …
… or you can delete the results of a query using the delete method on Query
person.delete();
Query query = createQuery("from Person");long rowsDeleted = query.delete();
58
Hibernate for SPL Framework - HQL• HQL queries must also go through the SPL Framework• The framework Query class implements a subset of pure
HQL• The framework HQL queries return Business Entities• It is mostly the same as the native HQL language, except
for a few differences …• The SELECT clause is not allowed in the framework’s HQL text• The ORDER BY clause is not allowed in the framework’s HQL text• UNIONs are catered for in the framework’s HQL (native HQL does
not allow it)• The framework caters for raw SQL statements
• The following slides describe the framework’s implementation of HQL
Querying Entities
60
Selecting Entities
• The SELECT clause is not used in a framework Query call• Similar to Hibernate, the following query is valid
• In this example, the List will contain the algorithm objects selected for the algorithm type (i.e. Business Entities are returned)
AlgorithmType algorithmType = ... ;Query query = createQuery("from Algorithm algorithm “+ “where algorithm.algorithmType = :algorithmType");query.bindEntity("algorithmType", algorithmType);List algorithms = query.list();
61
Specifying Results
• To select individual columns, you specify the “result” properties programmatically, giving each a unique name
• The addResult method on the Query object specifies a result property (column) to select
• Now the List will contain QueryResultRow objects• Each QueryResultRow will have two values, keyed by
“algorithm” and “algorithmId”
Query query = createQuery("from Algorithm algorithm”+ “where algorithm.algorithmType = :algorithmType");
query.bindEntity("algorithmType", algorithmType);query.addResult("algorithm", "algorithm");query.addResult("algorithmId", "algorithm.id");List queryResults = query.list();
62
Ordering Results
• The ORDER BY clause in HQL is not allowed by the framework
• Instead, the order must be programmatically specified …
• The List will contain QueryResultRow objects, ordered by “algorithmId”
Query query = createQuery("from Algorithm algorithm”+ “where algorithm.algorithmType = :algorithmType");
query.bindEntity("algorithmType", algorithmType);query.addResult("algorithm", "algorithm");query.addResult("algorithmId", "algorithm.id");query.orderBy(“algorithmId”, Query.ASCENDING);List queryResults = query.list();
63
Using Query Iterators
• For a large volume query, instead of using a List, better performance can be gained using the iterate method on Query
• This is more efficient because the iterator loads objects on demand
• It also performs better if the objects have already been loaded and cached by the session
Query query = createQuery("from Person");Person perEntity = null;QueryIterator qIter = query.iterate();while (qIter.hasNext()) {
perEntity = (Person)qIter.next();...
}
64
Union Queries
• Hibernate does not provide for unions (it does not fit in with the object-oriented principles of HQL)
• However, unions can be useful so the framework allows you to programmatically code a union
• Unioned queries must have identical …• Result columns• Order by clauses• Max results settings
• The unionWith() method of the Query class performs the initial union
• To add a third (or later) query, you add it directly to the union
UnionQuery union = query.unionWith(query2);
union.addQueryToUnion(query3);
65
Please note that this is not a recommended or preferred means of accessing the database
Raw SQL• It may be necessary to code SQL instead of HQL• Potential reasons are …
• To specify performance hints• A table is not mapped to a Java entity
• Instead of createQuery(), you use the JDBC-like createPreparedStatement() method on the session object
• The text is similar to the HQL Query text, but you use the table and column names instead of the Java entity and property names
• This feature is only mentioned here for information – we do not cover it in the exercises
66
Extending Business Entities
• Entities can be extended by applications “higher” on the application stack. For example, the “cm” application can extend “ccb” entities.
• The BusinessEntity annotation has a special parameter to define this: extendedInterfaceName
• Only new business methods can be added. Existing ones may not be overridden or removed.
• Walk-through: User_Impl in CCB
67
COBOL-Backed Entities
• There are complex COBOL row programs in the CCB application that maintain table data
• So not to duplicate this logic between COBOL and Java, these row programs are “wrapped” by BusinessEntity classes.
• These classes can be interacted with just like any other entity: queried, changed, etc
• The persistence mechanism has been changed though so that rather than Hibernate persisting changes, the COBOL programs are called instead
• Walk-through: ServiceAgreement in CCB
End of Day 1
69
Day 2 Agenda
• Change Handlers-Standard Validations
Change Handlers
Standard Validations
71
Change Handlers• The way to change an entity’s response to changes in it’s
state is by creating Change Handlers• Validations disallow inappropriate modification• Cascading changes allow one change to cause another
• One or more Change Handlers may exist for a Business Entity – the framework will call all of them
• The Business Entity is specified in the annotations – it tells the framework which entity to attach the Change Handler to at runtime
• A Change Handler extends the AbstractChangeHandler framework class
• A Change Handler class name MUST end in “_CHandler” for the artifact generator to recognize it as such
72
Example Change Handler
/** * @ChangeHandler (entityName = characteristicType) */public class CmCharacteristicType_CHandler extends AbstractChangeHandler {
...}
• In this example …• The annotation specifies a Change Handler for Business Entity
“Characteristic Type”• It extends AbstractChangeHandler• The class name ends in “_CHandler”
73
Change Handlers
• Change Handlers add event-driven logic to entities (similar to COBOL row user exits)
• They can perform 2 things:1. Validate data changes
2. Cause cascading changes
• We’ll take a look at validation first
74
Change Handlers – Validation Rules• The framework uses Validation Rules to determine how to
validate a Business Entity at runtime• The Validation Rules for a Business Entity are declared in
a Change Handler• The declarative style means the programmer specifies
WHAT to validate, not HOW or WHEN• A number of standard rules exist that require minimal
programming, but custom rules can be coded if needed• Custom rules require programming – these contain the
step-by-step logic of HOW to validate• Validation rules are “side-effect free”, which means …
• They cannot change the persistent state of the system (can’t update)
• It insures all validations are performed on a complete set of changes
75
Change Handlers – Validation Rules• The following standard rules exist
• AllowAndRequireRule – both allows and requires a property to be populated
• AllowRule – allows a value to be populated• CustomRule – creates a rule out of a Custom Validation class (to
implement logic not handled by any of the above)• NotAllowRule – one or more properties are not allowed• PlaceHolderRule – this is a “place holder” when a Change
Handler is developed – they are replaced with the actual rules during development
• ProtectRule – prevents a property from being changed• RequireRule – requires a property to be populated• RestrictRule – restricts a property to a set of values
• Refer to the SDK documentation for more details on these rules
76
Change Handlers – Validation Rules
• The framework invokes the getValidationRules method on a Change Handler for all the validation rules for a Business Entity
• The getValidationRules method must return an array of validation rule objects
• To use a standard rule, simply use the static “factory” method on the rule itself – for example• RequireRule.alwaysRequire() returns an instance of the
RequireRule class that insures an entity property has a value• RestrictRule.validatedMustEqualReference() returns an instance
of the RestrictRule class that checks if a property is equal to a specific value/reference
77
Change Handlers – Validation Rules
• A Validation Rule requires a Property on the Business Entity (i.e. the “field”) to validate
• A property is a reference to an entity field• Properties exist as constants on the business interfaces –
for example• Person.properties.emailAddress refers to email address field on
Person Business Entity• Account.properties.setUpDate is the setup date field on the
Account entity
• Properties are passed to the Validation Rules as parameters to tell a rule which property to validate
78
Change Handlers – Validation Rules
Here we see …• Static method emailRequiredRule to instantiate a RequiredRule object
using factory method alwaysRequire• Person.properties.email to tell the rule to validate the Email Address
property (i.e. “field”) on Person• Public method getValidationRules to provide a list of the rules to the
framework (only one in this case)
public static ValidationRule emailRequiredRule() { return RequireRule.alwaysRequire("Person:Email is required", "The person's email address must be specified", Person.properties.emailAddress); } public ValidationRule[] getValidationRules() { return new ValidationRule[] {emailRequiredRule()}; }
Example – make Email Address field on Person required
79
Exercise 1 – Change Handler Validation 1All Students• Create a Person change handler in the java folder of your project …
• Place it in package com.splwg.cm.domain.customerinfo.person
• Name it CmPerson_CHandler
• Add validation rule address1RequiredRule to make address line 1 required
OptionalNote: this exercise is very similar to the above, but will lead to a more challenging exercise 3
• Create a Premise change handler …• Place it in package com.splwg.cm.domain.customerinfo.premise
• Name it CmPremise_CHandler
• Add a validation rule to make city required
Test• Use the online system to test your change handler
80
When do Rules Fire?• Before we delve into the topic of Testing, it helps to
understand how validation is triggered by the framework• To reiterate, validation rules are “declarative”; declared in
a change handler, but invoked at the discretion of the framework
• By default, validation rules “fire” when a Business Entity is updated, deleted, created, etc.
• For example, using setDTO to update an entity will trigger the validation for it, as seen here …
• This is called “eager” validation because it fires immediately
Person_DTO perDTO = perEntity.getDTO();perDTO.setEmailAddress(“[email protected]”);perEntity.setDTO(perDTO);
81
When do Rules Fire?• The triggering of validation can be controlled
programmatically if needed• It may be necessary where multiple entities require
validation as a “coherent” set of changes• To illustrate this, consider the example of validating a new
Person and Person Name, but only once the entities have been created – i.e. to defer validation until both entities are created
To defer validation until later, use framework method startChanges() before the update and saveChanges() after the update
• Let’s take a look at the code for this example …
NOTE: saveChanges() does not issue commit to database!
82
Example: Defer Validation
1. Invokes startChanges to tell the framework to suspend validation
2. Creates a new Person Business Entity
3. Creates a PersonName Business Entity for the new Person
4. Adds PersonName to Person
5. Invokes saveChanges to allow for validation on the “coherent” set of changes
startChanges();
Person_DTO perDTO = (Person_DTO)this.createDTO(Person.class);Person_Id perId = new Person_Id(“1234567890”);perDTO.setId(perId);Person person = perDTO.newEntity();
PersonName_DTO perNameDTO = (PersonName_DTO) createDTO(PersonName.class);PersonName_Id perNameId = new PersonName_Id(person, BigInteger.ONE);perNameDTO.setId(perNameId);perNameDTO.setEntityName(“Blog,Joe");perNameDTO.setNameType(NameTypeLookup.constants.PRIMARY);PersonName perName = perNameDTO.newEntity();
person.getNames().add(perName);
saveChanges();
1
2
3
45
JUnit Testing
84
JUnit Testing• To ensure quality code, an automated unit testing
procedure is required• The reasons for automated testing are …
• One small change can unexpectedly break other parts of the system – automated tests provide early notification of these bugs
• Studies show that to fix bugs costs more the longer they are undiscovered – automated tests prevent this
• Automated tests are useful (albeit technical) forms of functional documentation – they describe to the programmers the expected inputs/outputs
• We use JUnit for automated testing• The Eclipse IDE is integrated with JUnit, making it easy to
create and run tests• JUnit test are part of our continuous build process.
85
JUnit Testing
• The framework contains many abstract “tester” classes, one for each type of test – for example• AbstractEntityTestCase tests a Business Entity and its related
Change Handlers. It also ensures all the Validation Rules in the Change Handler have fired
• AlgorithmImplementationTestCase tests an algorithm function
• When coding your own test class, you must extend the appropriate abstract “tester” class
• The root of many tester class is ContextTestCase which can be used to test most things that require general framework services
86
JUnit Tests are Safe
… or should be…• The transaction logic in the tester classes is “hotwired”.
Whenever you use the session provided within the tester class:• All your changes get rolled back at the end of the test, pass or fail• If you do data setup, it gets rolled back
• Other resource cleanup (like temporary files) should be cleaned up by overriding “tearDown()” method
• Let’s see how a test is created …
87
Creating a JUnit Test Class
First step is to create a JUnit test class
This class is to test CmPerson_CHandler validation rules1. The class name ends in “_Test”
2. It extends AbstractEntityTestCase – to test a Business Entity/Change Handler
3. The getChangeHandlerClass method returns the class definition of the Change Handler being tested – the framework invokes this method to get the name of the class to test
public class CmPerson_CHandler_Test extends AbstractEntityTestCase {
protected Class getChangeHandlerClass() { return CmPerson_CHandler.class; } }
88
Creating a JUnit Test CasesNow we add a method for each test case …
Remember:1. The method name must start with “test” – the framework will
execute all classes that start with “test”
2. ALL the rules must be violated in the test - if a validation rule exists in the change handler but is not violated in the test class, the framework will fail the test
3. The objective is to have all test cases complete successfully (no failures)
public void testEmailRequiredRule() {
}
Note Our change handler only has one rule – “Email Required” – so we only have one test case method
89
Fleshing out a JUnit Test CaseLet’s add some code to test our “email required” rule …
What this code does is …1. Fetches a person from the database and instantiates a Person
object2. Acquires a Data Transfer Object (DTO) from Person3. Updates the email address on the DTO to a non-space value4. Modifies the DTO on Person, which triggers validation
This tests for a positive outcome (which is necessary), but we MUST also violate the condition to properly test the validation rule …
public void testEmailRequiredRule() { Person_Id perId = new Person_Id("5775933103");
Person person = perId.getEntity();
Person_DTO goodDTO = person.getDTO();
goodDTO.setEmailAddress("[email protected]");
person.setDTO(goodDTO);}
1
2
3
4
90
Fleshing out a JUnit Test Case (cont)So let’s add some more code …
What this code does is …1. Instantiates a “bad” DTO and clears the email address to violate the rule
2. Updates the Person’s DTO to trigger validation
3. Invokes JUnit fail method if setDTO does NOT cause expected rule violation
4. Exception catch (what we expect), verifies that our rule – not some other one - caused the exception
public void testEmailRequiredRule() { ... try { Person_DTO badDTO = person.getDTO(); badDTO.setEmailAddress(null);
person.setDTO(badDTO);
fail("a validation error should have been thrown");
} catch (ApplicationException exception) {
// Make sure the correct rule was violated. verifyViolatedRule(CmPerson_CHandler.emailRequiredRule(), exception); }}
1
2
3
4
91
The Complete Test Case
Here’s the complete test method …
With this method, the JUnit test should run with no errors or failures – an error or failure would mean a problem in the test class or application code
public void testEmailRequiredRule() { Person_Id perId = new Person_Id("1648461517"); Person person = perId.getEntity(); Person_DTO goodDTO = person.getDTO(); goodDTO.setPersonOrBusiness( PersonOrBusinessLookup.constants.PERSON); goodDTO.setEmailAddress("[email protected]"); person.setDTO(goodDTO); try { Person_DTO badDTO = person.getDTO(); badDTO.setEmailAddress(null); person.setDTO(badDTO); fail("a validation error should have been thrown"); } catch (ApplicationException exception) { // Make sure the correct rule was violated. verifyViolatedRule(CmPerson_CHandler.emailRequiredRule(), exception); }}
92
Exercise 2 – JUnit Test 1All Students• Create a JUnit test class for your Person validation rule from exercise 1 in the
test folder of your project …• Place it in package com.splwg.cm.domain.customerinfo.person• Name your class CmPerson_CHandler_Test• Add a method to test your validation rule
Optional• Create a JUnit test class for your Premise validation rule from exercise 1
Test• From Eclipse, right-click on your test class and select Run As -> JUnit Test
The Continuous Build
94
The Continuous Build
• Servers have been dedicated to continuously build and test our applications• Every 10 minutes or so, the build server looks for committed
changes in the appropriate Perforce location. If none are found, the build waits another 10 minutes and polls again.
• When changes are found it does the following: • Downloads all changes
• Removes all artifacts, class files and other remnants from the last build
• Generates all artifacts
• Compiles all code
• Runs all tests
• If the build was unsuccessful it sends emails to everyone who committed changes since the last successful build.
95
When you “break the build”
• If a developer thinks the problem is his/her fault, it is customary to: • Send a note to other developers on the list so they don't waste
time on the problem • Fix the problem, test it locally, commit changes to Perforce and
hope the build is appeased.
• If no developer confesses to the problem, some collaboration may be required. It may also require some discussion with the "build master“.
96
How not to break the build
• Make sure everything compiles• Make sure your changelists are complete• Run as many tests as you can. Run all appearing related
to the changes made.• When you need to commit code you know is broken or
untested, disable the test as follows:
protected void testSendingEmails() { disableTest( "This test needs to be reworked so it doesn't hit the real oracle
email server and spam real people. This should be more like the other email tests that hit an embedded server.",
"Brian Conlon", new Date(2008, 04, 29));
Conditional Validation
98
Conditional Validation• In the last exercise we used the alwaysRequire method to
UNCONDITIONALLY require a value• Rules would not be very useful if only unconditional
validation was allowed• Validation is usually based on some condition, for
example1. Prevent a “dependent” property from being changed if the
“primary” property matches a certain value – e.g. FT freeze date/time cannot be changed when the status is frozen (ProtectRule)
2. The life support description on Person is required if the life support flag is set (RequiredRule)
• Rules have various constructs (and factory methods) to handle this, for example• RequireRule.ifReferenceEqualsValue() requires a property if
another property equals a certain value
99
Conditional Validation (cont)Example – email address required if life support flag is setpublic static ValidationRule emailRequiredIfLifeSupportIsYesRule() { return RequireRule.ifReferenceEqualsValue( "Person:Email address is required if life support is active", "Email address is required if life support is active",
Person.properties.emailAddress,
Person.properties.lifeSupportSensitiveLoad,
LifeSupportSensitiveLoadLookup.constants .LIFE_SUPPORT_SENSITIVE_LOAD, com.splwg.base.domain.StandardMessages.fieldMissing());}
1
2
Here we introduce a few new things …1. Using the ifReferenceEqualsValue factory method on RequireRule
2. The property that is required (“primary” property) - emailAddress
3. The property to compare for a value (“dependent” property) – lifeSupportSensitiveLoad
3
100
Conditional Validation (cont)Example – email address required if life support flag is setpublic static ValidationRule emailRequiredIfLifeSupportIsYesRule() { return RequireRule.ifReferenceEqualsValue( "Person:Email address is required if life support is active", "Email address is required if life support is active", Person.properties.emailAddress, Person.properties.lifeSupportSensitiveLoad,
LifeSupportSensitiveLoadLookup.constants .LIFE_SUPPORT_SENSITIVE_LOAD, com.splwg.base.domain.StandardMessages.fieldMissing());}
Here we introduce a few new things …4. The value to compare the dependent property (no. 3) to –
LIFE_SUPPORT_SENSITIVE_LOAD is the lookup value for constant “Y” (all valid lookup values are generated onto “lookup” classes)
5. The message to display when the condition is true - com.splwg.base.domain.StandardMessages.fieldMissing
Note: Here we use a standard message – we will create a custom message later
4
5
3
101
Exercise 3 – Conditional ValidationAll Students• Modify your Person change handler from exercise 1 …
• Change the validation rule to make address line 1 required only if the person type is “Business”
• Rename the validation rule to indicate that it’s conditional – e.g. address1RequiredIfBusinessRule
Optional• Modify your Premise change handler from exercise 1 …
• Change the validation rule to make city required only if the county is not equal to constant value “San Francisco”
• Rename the validation rule to indicate that it’s conditional – e.g. cityRequiredIfCountyNotSFRule
Test• Update your JUnit test class from exercise 2 to test the conditional
validation
Base Messages
103
Message Categories• ALL application messages are stored in the framework’s
message repository (the message text is not embedded in the source code)
• Messages are grouped into categories
• Use menu Admin -> M -> Message to view/update the message categories
• The list here shows the standard message categories
104
• Within each category, the message text is numbered
• This example shows messages for “Standard” category 11001
• Note message number 101 (“ %1 field missing”); we used it in the previous exercise
• “%1” is a substitution variable to be supplied when the Java class displays the message (field name in this case)
• A message may contain up to 9 substitution variables
Message Categories – Message Text
105
• Message text may contain substitution variables• Variables are denoted by % followed by a number from 1
to 9 – e.g. %1, %2, etc.• For example, the message text
%1 field missing
will be displayed as
Bill Cycle field missing
%1
• The application code must pass the values for the substitution variables when the message is displayed
Message Categories – Substitution Variables
addError(StandardMessages.FieldMissing(fieldName))
106
• Base message text may not be modified (it is prevented by the system) - changes would be overwritten at the next upgrade anyway
• However, you can ADD customized message text for a base message on the Details tab
Base Messages – Modifying …
107
• The Customer Specific Message Text must use the same substitution variables as the base one
Base Messages – Modifying …
The framework will ALWAYS display the customer specific message if present – it is not possible to conditionally display the base Message Text if custom text for it exists
108
Base Messages – Java Definitions• Each category has its own class to define message variables and
methods
• For example, the StandardMessages class contains …1. The category number definition for the standard messages
2. All the standard message numbers
1
2
109
Base Messages – Runtime Definition• A message class also contains methods …
• The application code calls these methods to display messages at runtime – for example
addError(StandardMessages.fieldMissing("EMAILID"));
110
Base Messages – Runtime Definition
Note: in this case, the language-dependent description of EMAILID would be displayed in the message – in English, “Email Address field missing”
• As can be seen here, runtime message methods return objects of type ServerMessage
111
Base Messages – Declarative Definition• The previous slide shows a RUNTIME message definition• Remember, validation rules are declarative• We therefore also need a declarative version of the
message to use in the validation rules
• This methods returns a ServerMessageTemplate object to use in the validation rule declaration
112
Base Messages – Declarative Definition• In the last exercise we used the declarative version of
StandardMessages.fieldMissing
• fieldMissing has a runtime AND declarative message method, but not all messages require both versions• Those used in validation rules require declarative ones; those
displayed from other “runtime” code (e.g. algorithms) require runtime versions
• We’ll see in a moment how to use a runtime message …
public static ValidationRule emailRequiredIfLifeSupportIsYesRule() { return RequireRule.ifReferenceEqualsValue( "Person:Email address is required if life support is active", "Email address is required if life support is active", Person.properties.emailAddress, Person.properties.lifeSupportSensitiveLoad, LifeSupportSensitiveLoadLookup.constants .LIFE_SUPPORT_SENSITIVE_LOAD, com.splwg.base.domain.StandardMessages.fieldMissing());}
Custom Messages
114
Custom Messages – Creating …
• Over the following slides, we’ll look at creating and using a custom message in Java
• The custom message text we will work with is
“%1 required if %2 is activated”
The validation that references this messages will therefore be a conditional validation
Remember this text – you could even write it down to refer to as we step through the slides
• First, let’s look at creating the message online …
115
This is all that is required in the online to create a new message
Custom Messages – Creating …• ONLY category 90000, Implementer’s
messages, may contain new custom messages
• To create a new message, you select category 90000 and insert a new message as shown below
% variables here must be supplied in the classes that display the message
116
Custom Messages – Java Definition• On the Java side, these are the basic initial setup rules for
creating custom messages …1. Create a CustomMessages class (supplied with this course) to
contain all your custom message definitions
2. CustomMessages must extend AbstractMessageRepository
3. In CustomMessages, specify 90000 as the message category
4. In CustomMessages, define all your custom message numbers
5. Place CustomMessages in the com.splwg.cm.domain package
6. In each individual sub-package under package cm, you create a MessageRepository class for the message methods specific to that sub-package – Note: common message methods are placed in the CustomMessages class
7. Each MessageRepository extends CustomMessages and contains the runtime and declarative methods for that sub-package
• Let’s take a closer look …
117
Custom Messages – Java Definition
• These are the important class characteristics …1. It is in package com.splwg.cm.domain2. It imports our custom MessageRepository (to be created next)3. Class CustomMessages extends AbstractMessageRepository4. It defines MESSAGE_CATEGORY 900005. It defines EVERY custom message (for now, we only have one)
package com.splwg.cm.domain;
import com.splwg.base.domain.common.message.AbstractMessageRepository; import com.splwg.cm.domain.templates.MessageRepository;
public class CustomMessages extends AbstractMessageRepository {
public static final int MESSAGE_CATEGORY = 90000;
public static final int REQ_IF_CONDITION = 10006;
private static MessageRepository instance; public CustomMessages() {
super(MESSAGE_CATEGORY); } static MessageRepository getInstance() { if (instance == null) instance = new MessageRepository(); return instance; } }
1
23
4
5
118
Custom Messages – Java Definition
• These are the important class characteristics …6. It defines a MessageRepository instance variable (see point 8)7. It invokes the superclass to establish the message category at runtime8. The getInstance method is a private method that provides the
MessageRepository instance to the to the message methods in the class
6
7
8
package com.splwg.cm.domain;
import com.splwg.base.domain.common.message.AbstractMessageRepository; import com.splwg.cm.domain.templates.MessageRepository;
public class CustomMessages extends AbstractMessageRepository {
public static final int MESSAGE_CATEGORY = 90000;
public static final int REQ_IF_CONDITION = 10006;
private static MessageRepository instance;
public CustomMessages() { super(MESSAGE_CATEGORY); } private static MessageRepository getInstance() { if (instance == null) instance = new MessageRepository(); return instance; } }
119
Custom Messages – MessageRepository• Next we create the MessageRepository class in the sub-
package under package cm• It must extend the CustomMessages class• It only contains the methods for the messages relevant to
the sub-package• All static definitions for the message numbers should be in
CustomMessages• Let’s look at the example …
120
Custom Messages – MessageRepository
• These are the important class characteristics …1. It is in the sub-package to which it applies - customerinfo.person in
this example2. It extends the parent CustomMessages class3. It defines a MessageRepository instance variable4. It has a getInstance method to provide the MessageRepository
instance – i.e. an instance of itself - to the message methods that follow
package com.splwg.cm.domain.customerinfo.person; import com.splwg.cm.domain.CustomMessages;
public class MessageRepository extends CustomMessages {
private static MessageRepository instance;
private static MessageRepository getInstance() { if (instance == null) instance = new MessageRepository(); return instance; } }
1
2
3
4
MessageRepository class
121
Custom Messages – Runtime Method
• The important method characteristics are …1. a) The method is static
b) It returns a ServerMessage object type for a runtime messagec) It has a descriptive method name based on the message it handlesd) The parameters correspond with the % variables in the message
public static ServerMessage requiredIfCondition(String fieldName1, String fieldName2) {
MessageParameters parms = new MessageParameters();
parms.addField(fieldName1);
parms.addField(fieldName2);
return getInstance().getMessage(REQ_IF_CONDITION, parms); }
Let’s add a runtime method for our message in the MessageRepository class
1
122
Custom Messages – Runtime Method
• The important method characteristics are …2. It defines a MessageParameters object to carry the substitution
values for the message3. It adds the substitution values (the parameters) to the
MessageParameters object4. It returns a ServerMessage object as instantiated by the getMessage
method, based on the supplied message number and parameters –the message number is defined in class CustomMessages
Let’s add a runtime method for our message in the MessageRepository class
2
4
3
public static ServerMessage requiredIfCondition(String fieldName1, String fieldName2) {
MessageParameters parms = new MessageParameters();
parms.addField(fieldName1);
parms.addField(fieldName2);
return getInstance().getMessage(REQ_IF_CONDITION, parms); }
123
Custom Messages – Using at Runtime
• As seen on the previous slide, the requiredIfCondition method uses the addField method to set the parameters …
• The MessageParameters.addField method uses the language-dependent descriptions of the fields to replace the % markers in the message
Person_Id perId = new Person_Id("1234567890"); Person person = perId.getEntity(); Person_DTO perDTO = person.getDTO(); if ((perDTO.getLifeSupportSensitiveLoad().equals( LifeSupportSensitiveLoadLookup.constants. LIFE_SUPPORT_SENSITIVE_LOAD)) & perDTO.getEmailAddress().equals("")) { addError(MessageRepository.requiredIfCondition("EMAILID", "LS_SL_FLG")); }
Here we see how to set a message at runtime
parms.addField(fieldName1); parms.addField(fieldName2);
124
Custom Messages – Declarative Method
• The important method characteristics are …1. a) The method is static
b) It returns a ServerMessageTemplate object type for a declarative messagec) It has a descriptive method name based on the message it handlesd) It has a reference to the parameters if the message contains % substitution variables
public static ServerMessageTemplate requiredIfCondition( LookupProperty prop) { ServerMessageParameter[] messageParms = { ServerMessageParameter.OFFENDING_PROPERTY_NAME, ServerMessageParameter.createDisplayingName(prop) };
return getInstance().getDeclarativeMessage(REQ_IF_CONDITION, messageParms); }
Let’s also add a declarative method for our validation rule
1
125
Custom Messages – Declarative Method
• The important method characteristics are …2. a) It has a message parameter array for the substitutions in the text
messageb) OFFENDING_PROPERTY_NAME is a static reference to the field being validated in the validation rule – we substitute this name into
the message’s 1st parameterc) createDisplayingName gets the name of the LookupProperty value which we substitute into the message’s 2nd parameter
Let’s also add a declarative method for our validation rule
public static ServerMessageTemplate requiredIfCondition( LookupProperty prop) { ServerMessageParameter[] messageParms = { ServerMessageParameter.OFFENDING_PROPERTY_NAME, ServerMessageParameter.createDisplayingName(prop) };
return getInstance().getDeclarativeMessage(REQ_IF_CONDITION, messageParms); }
2
126
Custom Messages – Declarative Method
• The important method characteristics are …3. It returns a declarative message object based on the message
number and substitution parameters
Let’s also add a declarative method for our validation rule
3
public static ServerMessageTemplate requiredIfCondition( LookupProperty prop) { ServerMessageParameter[] messageParms = { ServerMessageParameter.OFFENDING_PROPERTY_NAME, ServerMessageParameter.createDisplayingName(prop) };
return getInstance().getDeclarativeMessage(REQ_IF_CONDITION, messageParms); }
127
Custom Messages – Using DeclarativeUsing the custom declarative message in a validation rule
public static ValidationRule emailRequiredIfLifeSupportIsYesRule() {
return RequireRule.ifReferenceEqualsValue( "Person:Email address is required if life support is active", "Email address is required if life support is active",
Person.properties.emailAddress, Person.properties.lifeSupportSensitiveLoad, LifeSupportSensitiveLoadLookup.constants .LIFE_SUPPORT_SENSITIVE_LOAD, MessageRepository.requiredIfCondition( Person.properties.lifeSupportSensitiveLoad));}
1. Person.properties.emailAddress is automatically set as the OFFENDING_PROPERTY_NAME, so it does not need to be passed to the declarative method as a parameter – as seen on the previous slide, we use this name to substitute the 1st message parameter
2. We now reference our custom requiredIfCondition method in the validation rule
3. The parameter we pass in is the property of the field to substitute the message’s 2nd parameter
1
2
3
128
Exercise 4 – Custom Declarative MessageAll Students• Use the online system to create a descriptive message for the Person
validation rule in exercise 3• Each student should use a unique message number when working on a
shared database – the instructor can assign message numbers, starting at message number 10001
• Use your initials in the message to distinguish it
• Create the message with 2 parameters – the offending field name and the field name used in the comparison
• For example: “%1 is required if %2 is BUSINESS (XX)” – where XX are your initials
• Update the CustomMessages class and add a constant for your new message number – e.g. REQUIRED_IF_ BUSINESS
• Copy the supplied MessageRepository template to your person sub-package and add a method for use in the validation rule – e.g. requiredIfBusiness(…)
129
Exercise 4 – Custom Declarative MessageAll Students (continued) • Update your validation rule from exercise 3 to reference the new
message
Optional• Perform the above steps to create a descriptive message for your
Premise validation rule …• Start at message number 20001
• For added complexity, create a 3rd message parameter in which you specify the string value (constant) that the county is compared to
For example: “%1 required if %2 not %3 (XX)” – where XX are your initials
(see attached note)
Test• Use the online system to validate that the new message is displayed
Condition Objects
131
Change Handlers – Conditions• Rules can take as input one or more Conditions• The Condition interface currently has the following
implementations (i.e. these are the supported conditions)• BetweenValues• Equals• Not• And• Or• GreaterThan / GreaterThanOrEquals• LessThan / LessThanOrEquals• Contains
(Descriptions of these conditions are documented)• You use conditions to conditionally perform validation
132
• Conditions can be instantiated from factory methods on certain properties
The isTrue() and isGreaterThan() methods instantiate a Condition object1. PersonName is being checked for a “primary” attribute2. PersonName sequence is being checked for > 0
• Conditions can also be instantiated using a constructor
This checks if the PersonOrBusiness property is set to “Person”
• A Condition object such as this can be used in a Validation Rule to cause conditional validation
Condition isPrimaryName = PersonName.properties.isPrimaryName.isTrue(); Condition greaterThan = PersonName.properties.sequence.isGreaterThan(BigInteger.ZERO);
12
Condition isPerson = new Equals(Person.properties.personOrBusiness, PersonOrBusinessLookup.constants.PERSON);
Change Handlers – Conditions
133
Example – using a Condition to make email address required if life support flag is set public static ValidationRule emailRequiredIfLifeSupportIsYesRule() {
Condition hasLifeSupport = Person.properties.lifeSupportSensitiveLoad. isEqualTo(LifeSupportSensitiveLoadLookup.constants. LIFE_SUPPORT_SENSITIVE_LOAD);
return RequireRule.ifConditionTrue( "Person:Email address is required if life support is active", "Email address is required if life support is active", Person.properties.emailAddress, hasLifeSupport, com.splwg.base.domain.StandardMessages.fieldMissing()); }
1. This is the Condition to check if life support is on
2. We use the ifConditionTrue method of the RequireRule class
3. The ifConditionTrue method requires the Condition as a parameter, which in this case is the condition created in point 1
1
3
2
Change Handlers – Conditions
134
Exercise 5 – Change Handler ConditionsAll Students• Modify your Person change handler from exercise 3 …
• Introduce a Condition in your validation rule to make address line 1 required if the person type is “Business” (same validation as in exercise 3)
• Change the validation rule to use the Condition
Optional• Modify your Premise change handler from exercise 3 …
• Introduce a Condition in your validation rule to make city required if the county is not equal to “San Francisco” (same validation as in exercise 3)
• Change the validation rule to use the Condition
Test• Run the JUnit test class from exercise 3 to test• Startup the online system and test that your validation rule works the
same as in exercise 4
Custom Rules
136
Change Handlers – Custom Rules• If validation is too complex for a declarative rule, a custom
rule may be coded• A custom rule contains code to perform the specific
validation – in other words, “if invalid condition, display error message”
• A custom rule class may implement one or more abstract methods
• The methods correspond with “events” that may occur with respect to the underlying business entity
• The methods are divided into 2 categories …1. Eager Validations
2. Lazy Validations
137
Custom Rule – Eager Validation Methods• Eager validations fire immediately when an entity is
changed by way of a delete(), setDTO() or createEntity())• The “event” methods that can be implemented by a
custom rule class are …• validateAdd() fires when a business entity is added• validateChangeOnly() fires when a business entity is changed• validateAddOrChange() fires when a business entity is added or
changed (in addition to an add or change event)• validateDelete() fires when a business entity is deleted• validateRegisteredChange() fires when a change is registered to
another object (e.g. a child) - this can be any business entity that feels like notifying you of a change
A parameter passed to this method, RegisteredChangeDetail, can be queried (via its getChangeToList() method) to determine if the change affects you
138
Custom Rule – Lazy Validation Methods• Lazy validations fire after a “coherent set of changes”
have been made – this is in contrast to eager validations that fire immediately when an entity is modified
• Only one lazy validation method exists …
validateSave()It fires at the “end of” a set of changes
Validation of child entities, for example, can therefore be placed in this method
All types of changes (add, change, delete or register change) will trigger a validateSave()
139
Custom Rule Class• These are the basic steps to create and use a custom rule
…1. Create a class that extends AbstractCustomValidation – this class
could be an inner class of your Change Handler if the custom validation rule is specific to the Business Entity being validated
2. In your new class, provide the framework with an array of entity properties associated with the custom rule – the framework uses this to avoid additional validations on properties already known to be in error
3. In you new class, implement the appropriate eager and/or lazy methods to perform the validation
4. Reference the custom rule in your static validation rule method, as you would with a standard declarative rule
• Let’s take a look at an example …
140
Custom Rule MapFieldNameRequired Example
1. This class is an “inner class” of the ScriptFieldMap_CHandler class – this is not required, but it makes sense if the custom rule is specific to a Business Entity
2. The custom rule class extends AbstractCustomValidation
3. It passes back to the framework the property(s) being validated – in this case mapFieldName
1
3
2
141
Custom Rule MapFieldNameRequired Example
1. It overrides abstract method validateAddOrChange for immediate (eager) validation – this validation code will get fired on either an add or a change event
2. It casts the generic Business Entity object in the parameter to the type of Business Entity being validated – in this case ScriptFieldMap
3. If a validation error is found, the addValidationError method registers the error message with the framework
1
2
3
142
Custom Rule MapFieldNameRequired Example
1. In the Change Handler class, it declares a static method mapFieldNameRequired – as a standard, the name ties in with the custom rule class name of MapFieldNameRequired
2. The Change Handler instantiates and returns a new CustomRule object for custom rule class MapFieldNameRequired
3. The getValidationRules() method provides the framework the list of ALL the rules in this Change Handler – custom and standard ones
1
3
Change Handler class – ScriptFieldMap_CHandler
2
143
Exercise 6 – Custom Validation RuleAll Students• Modify (or copy) your Person change handler from exercise 5 and
make the Short Comment characteristic (characteristic type “COMMENT”) required …• Create a new message “Short Comment characteristic is required (XX)”
(where XX are your initials) – the instructor can assign a unique message number to each student
• Create a runtime message method in your MessageRepository class
• Override the validateSave method – remember, you need to do “lazy validation” when checking child entities
• Characteristics are effective dated – use an iterator on PersonCharacteristic to scan for the “COMMENT” type
Test• Create cm.jar and startup the online system to test …
• On a shared database, each student should test using a different person
144
Exercise 6 – Custom Validation RuleOptional• Create a JUnit test class for your custom validation rule and in your
testShortCommentRequiredRule() method …• Create a PersonCharacteristic_Id object for the “COMMENT”
characteristic for your Person_Id• Because this is for an existing Person on the database, remove any
existing Short Comment characteristic before continuing with the steps below to test the validationNote: This remove could trigger validation on the Person, which at this stage you want to avoid. See if you can come up with the solution.
• Create a PersonCharacteristic_DTO object from the PersonCharacteristic_Id
• Set the adhoc characteristic value to something – e.g. “Test comment”• Create a new PersonCharacteristic object from the
PersonCharacteristic_DTO• Add the new PersonCharacteristic object to your collection of
characteristics All the steps up to this point perform the “positive” validation – i.e. they test that your validation rule does NOT get violated
145
Exercise 6 – Custom Validation RuleOptional (continued)
• Test that the rule gets violated by removing the newly created PersonCharacteristic object from the collection of characteristics – this should cause an application exception to be thrown
Cascading Changes
147
Change Handlers – Cascading Changes• Up to now we’ve seen change handlers performing
validation on a Business Entity• Change handlers can also implement business logic based
on inserts, updates & deletes to a Business Entity – i.e. to cause “cascading changes” to occur
For example, if a phone number for a Person changes, update matching phone numbers for related Persons automatically
• Change handlers provide for a number of “handle” methods
• Handle methods are invoked when specific events occur• Unlike validation, this logic fires immediately – it cannot be
deferred by the startChanges or saveChanges method calls
148
The following methods can be implemented to provide customized functionality …
• For an Add or Copy Event• prepareToAdd()• handleAddOrChange()• handleAdd()• handleRegisteredChange()
• For an Update Event• prepareToChange()• handleAddOrChange()• handleChange()
• For a Delete Event• handleDelete()
Change Handlers – Cascading Changes
149
Events and MethodsLet’s take a closer look at the methods …• prepareToAdd(DataTransferObject newDTO)
• This method fires before a new Business Entity is added• It allows you to default values and perform other changes on a new
DTO before the add
• handleAddOrChange(BusinessEntity businessEntity,
DataTransferObject oldDTO)• This fires before validation when a entity is added OR changed• The oldDTO object contains a null on an add event
• handleAdd(BusinessEntity newBusinessEntity)• This fires before validation when an entity is added• It allows for cascading updates on other objects or external
systems
150
Events and Methods• prepareToChange(BusinessEntity unchangedEntity,
DataTransferObject newDTO)• This fires before a change to a DTO is submitted
• You can use this to default values and perform other changes on a DTO before the change
• handleChange(BusinessEntity changedBusinessEntity, DataTransferObject oldDTO)• This fires before validation when an entity is changed
• It allows for cascading updates on other objects or external systems
151
Events and Methods• handleRegisteredChange(BusinessEntity changedBusnessEnty,
RegisteredChangeDetail changeDetl)• This fires when a change is “registered” to the
changedBusnessEnty object via the business entity’s registerChange method
• For example, it can be fired when a child list associated with the Business Entity has been changed
• The RegisterChangeDetail object specifies the details of the change
• handleDelete(DataTransferObject oldDTO)• This fires before validation when an entity is deleted
Let’s now take a look at an example …
Important … these are not preferred places for validation – validation should be done using validation rules
152
handleAdd example• Create an alert when a new account is added /** * @ChangeHandler (entityName = account) */ public class CmAccount_CHandler extends AbstractChangeHandler {
public void handleAdd(BusinessEntity newBusinessEntity) { Account account = (Account) newBusinessEntity; AccountAlerts acctAlerts = account.getAlerts(); AccountAlert_DTO acctAlertDTO = acctAlerts.newChildDTO(); acctAlerts.add(acctAlertDTO, new AlertType_Id("PP EXEMPT"), getSystemDateTime().getDate()); } }
12
3
1. It specifies the entity type in annotations (don’t forget to do this!)2. The class name ends in “_CHandler” and extends
AbstractChangeHandler3. It implements the handleAdd method; the newBusinessEntity object is
a reference to the entity being added
153
handleAdd example (cont)• Create an alert when a new account is added /** * @ChangeHandler (entityName = account) */ public class CmAccount_CHandler extends AbstractChangeHandler {
public void handleAdd(BusinessEntity newBusinessEntity) { Account account = (Account) newBusinessEntity;
AccountAlerts acctAlerts = account.getAlerts();
AccountAlert_DTO acctAlertDTO = acctAlerts.newChildDTO();
acctAlerts.add(acctAlertDTO, new AlertType_Id("PP EXEMPT"), getSystemDateTime().getDate()); } }
4
5
6
7
4. It casts newBusinessEntity to an Account object5. It gets the alerts entity list (AccountAlerts) for the account – this will be empty since it’s a
new account6. It creates a new alert DTO by using the entity list’s newChildDTO method7. It adds the partially completed DTO to the account’s alert entity list – the additional
parameters are required for the entity listThe framework will add the row when the transaction commits
154
Change Handlers – Cascading Changes
• The previous example shows a “cascading change” to a child entity (Alerts) of a Business Entity (Account)
• The basic rules when working with child entities are …• Child entity lists are acquired from a Business Entity via the
appropriate getter methods (e.g. getAlerts())• To add a new child entity, use the entity list’s add methods• To remove a child entity, use the entity list’s remove methods
155
Change Handlers – Cascading Changes
• When inserting and deleting entities that are NOT child entities of the business entity being customized, more explicit code is required
• In this case the getDTO and setDTO method calls need to be used
Let’s look at an example …
156
Change Handlers V – handleChange Example
1. It implements the handleChange method – the changedEntity object is a reference to the account entity being changed; oldDTO is the DTO before the change was made
2. It casts changedEntity to an Account object
3. It checks if the mailing premise Id is populated on the account – mailing premise Id is an optional foreign key reference (Note, on the CI_ACCT table the mailing premise Id is a space, but on the Business Entity object it is a null)
public void handleChange(BusinessEntity changedEntity, DataTransferObject oldDTO) { Account account = (Account) changedEntity; if (account.getMailingPremiseId() != null) { Premise premise = account.getMailingPremiseId().getEntity(); Premise_DTO premiseDTO = premise.getDTO(); premiseDTO.setAddress4(account.getAlertInformation()); premise.setDTO(premiseDTO); } }
• Update the related premise when an account is changed
1
234567
157
Change Handlers V – handleChange Example
4. It gets the Premise object referenced by the mailing premise Id field
5. It gets DTO for the Premise to modify the row
6. It updates the value on the Premise DTO – address line 4 in this case is set to the account’s alert information (i.e. the “Comment” field on the account page)
7. It updates the Premise Business Entity using the setDTO() method, which ultimately causes the row to be updated
public void handleChange(BusinessEntity changedEntity, DataTransferObject oldDTO) { Account account = (Account) changedEntity; if (account.getMailingPremiseId() != null) { Premise premise = account.getMailingPremiseId().getEntity(); Premise_DTO premiseDTO = premise.getDTO(); premiseDTO.setAddress4(account.getAlertInformation()); premise.setDTO(premiseDTO); } }
• Update the related premise when an account is changed
1
234567
158
Exercise 7 – Cascading Change HandlerAll Students• Change your Person Change Handler from the previous exercise
automatically add a Short Comment characteristic (characteristic type “COMMENT”) whenever a new Person is added
Test• Create cm.jar and startup the online system to test• Add a new person and verify that the Short Comment characteristic
was added by your Change Handler
Optional• Create a JUnit test class that adds a new person and verifies that the
Short Comment characteristic was automatically added. In this exercise, a completely new person, with all its required collections of names and Id numbers, must be created. In your test method …• Suspend validation until all the data has been added
• Create a new Person_Id object
159
Exercise 7 – Cascading Change HandlerOptional (continued)
• Create a new Person_DTO object
• Set the following required fields on the Person_DTO …• PersonOrBusiness• LifeSupportSensitiveLoad• LanguageId
• Create a new Person object from the DTO
• Create a PersonName_DTO object (use the getNames() method on the Person object)
• Set the following required fields on the PersonName_DTO …• EntityName• IsPrimaryName• NameType
• Add the name to the Names collection on the Person object (use sequence 1)
160
Exercise 7 – Cascading Change HandlerOptional (continued)
• Create a PersonId_DTO object (use the getIds() method on the Person object)• Set the following required fields on the PersonId_DTO …
• IsPrimaryId
• PersonIdNumber
• Add the Id to the Ids collection on the Person object for Id Type “SSN” (Social Security Number)
• Enable validation at this point (i.e. remove the suspension)• Verify that the short comment characteristic was added by getting a
PersonCharacteristic object for the expected “COMMENT” characteristic – if a null object is returned, fail the test as follows …
assertNotNull("Short Comment characteristic does not exist",perChar);
Services and Maintenance Classes
162
Services• Services expose business functionality via predefined
request and response data structures• Services may be invoked
• from outside the application JVM (I.e. a browser)• from from within the application (I.e. by a service script).
• These are the data access and update services• Many services are implemented by either Java classes or
COBOL programs. The implementations are called “maintenances”.
163
Services• Each service has an XML metainfo document describing
its structure• For integration, the framework automates the mapping of
string-based browser data to a service’s data structure using this metainfo XML (the XAI class covers this)
• The Service Dispatcher routes the service requests to the Java or COBOL service …
164
Services - Service Dispatcher• The Service Dispatcher acts as conduit to the business
objects from the presentation layer• A service invocation represents a database transaction • It invokes a Java or COBOL service
PresentationPresentation ServiceDispatcher
ServiceDispatcher
DBDBHibernateHibernate
JDBCJDBC
JDBCJDBC
COBOL SvcWrapper
COBOL SvcWrapper
COBOL AppCOBOL App
Java Maintenance
Business Objects Data Access
165
Service Dispatching• The Service Dispatcher is commonly invoked from a Web
servlet• It invokes the appropriate service for the Web request,
which could be one of the following1. Page service2. List service3. Search service
Service DispatcherService Dispatcher Dispatched ServiceDispatched Service
Page ServicePage Service List ServiceList Service Search ServiceSearch Service1 2 3
166
Page Services• This is a “top level” application service• It orchestrates the display and update of all data for a root object and
all its child objects – e.g. Person, Person Name, Person Phone, Person Id, etc.
• The data is displayed on a single tab menu – across one or more child tab pages
• Page service names end with “P” – e.g. the Person service is CILCPERP, Account service is CILCACCP, etc.
167
Page Service Dispatching• A Page service invoked in one of seven modes (actions)• Each service class determines if the maintenance service
is implemented in COBOL or Java and uses the appropriate calling mechanism to invoke it
Page ServicePage Service
ChangeChange CopyCopy DefaultDefault DeleteDelete ReadReadAddAdd
Java PageAdd Service
Java PageAdd Service
Cobol Page Add Service
Cobol Page Add Service
… same for all modes
Cobol SvcWrapper
Cobol SvcWrapper
ValidateValidate
168
List Services • This defines a list of objects• It could contain nested lists• It is used for list-oriented data – e.g. Customer Contacts
• List service names end in “L” – e.g. CILCPCCL for Customer Contact list
• They do not support database updates
169
List Service Dispatching• A List service class is invoked in single mode (“List”) from
the Service Dispatcher• It determines if the underlying list service is implemented
in COBOL or Java and uses the appropriate calling mechanism to invoke it
List ServiceList Service
Java ListService
Java ListService
Cobol ListService
Cobol ListService
Cobol SvcWrapper
Cobol SvcWrapper
170
Search Services• These are used to support ad-hoc user searches• The results are similar to list services• The input is set of criteria and a search mode
• Search service names end in “S” – e.g. CILCPERS for Person search
171
Search Service Dispatching• A Search service class is invoked in single mode
(“Search”) from the Service Dispatcher• It determines if underlying search service implemented in
COBOL or Java and uses the appropriate calling mechanism to invoke it
Search ServiceSearch Service
Java SearchService
Java SearchService
Cobol SearchService
Cobol SearchService
Cobol SvcWrapper
Cobol SvcWrapper
Java Maintenance Classes
173
Java Maintenance Classes
• Important forms of maintenances:• PageMaintenances are general purpose and can conceivably do
anything. However, whatever that is must be coded by the implementer.
• EntityPageMaintenance performs the CRUD for an MO• EntityListPageMaintenance performs CRUD on a collection of
entities without a single common maintained parent• SearchServices can perform searches based on varying input
fields
174
PageMaintenances
• Remember, these are the generic ones• Use the @PageMaintenance annotation
• Define the “body” based on the needs for request and response• Declare actions that make sense
• Supported actions are then implemented by overriding action methods, like “add” in the case of a service that adds data to the system or “read” when it just returns existing data.
• Walk-through: EmailService
175
EntityPageMaintenances
• Provide CRUD for MOs • The actions interact with the Entities in the MO• Use the @EntityPageMaintenance annotation
• For new MOs there is a wizard that creates a skeletal structure based on metadata.
• This should be a single root object. That is we are maintaining a single entity instance that my have children. To maintain lists, the @EntityListPageMaintenance is more appropriate
176
EntityPageMaintenances
• @EntityPageMaintenance values• Important root values:
• “service” defines the name of the service• “entity” defines the entity being maintained• “body” is always a data element containing an array of “contents”.
• @RowField should be used for the entity being maintained• @ListFields reference lists• @DataFields can be used to specify “loose fields”• @FieldGroup can be used to group fields together
• Lists• An array of lists is kept that may be referenced by name by @ListField
annotations in the root node of the annotation or other lists• The individual lists have bodies like in the root element discussed above.• Filters may be applied using HQL snippets which bind values in the
implmentation class
• Walk-through: FactMaintenance
177
EntityListPageMaintenances
• Use the @EntityListPageMaintenance annotation• These are typically very simple implementations• A @ListField will be used to define the maintained list in the root
body of the annotation• The reference list will refer to a @RowField corresponding to the
entity being maintained• orderBy” will specify ordering
• Walk-through: CurrencyMaintenance, LanguageListMaintenance
178
Search Services
• Use the @SearchService annotation• Provide some number of different search criteria that can be used
via the @SearchCriteria annotation. Importantly,• “hql” defines how the rows are retrieved
• “orderBy” specifies the order
• “fields” specifies the values that the search is based on
• “returnValus” specifies the values return be a search result row and includes HQL snippets to extract these values from the query result object
• Walk-through: BatchControlSearch
The Eclipse MO Wizard
180
The Eclipse MO Wizard
• An Eclipse plug-in was written to create default implementation classes, with annotations based on MO metadata definitions. This “wizard” will create:• Entity “Impl” classes for each entity in the MO• An EntityPageMaintenance class
• The user chooses the names for the class files, the package and file location and the wizard does the rest
Running the Wizard From Eclipse
End of Day 2
Business Components
184
Business Components
• Business Components are classes that provide two important features• A place to put reusable business logic without the other
responsibilites that come with other types of application classes (like entities, change handers, etc).
• Provide a lightweight replacement strategy that allows customers to fully replace some system logic without all of the formality and configuration of new plugin spots.
185
Adding a Business Component
• Use the @BusinessComponent annotation• With this simple anotation, the artifact generator will process the
business component and create business interface class.• customizationReplaceable
• Specify “customizationReplaceable” to indicate if a customer may replace this component. Setting this value to “true” also has the affect of treating the component as a customization whether or not it was actually provided by the base package or not.
• customizationCallable• Specify if the component is “customizationCallable”. This can be set
to true to allow customers to call methods on the business component but not replace the component
…more…
186
Adding a Business Component
• Use the @BusinessComponent annotation• replacementComponent
• In the case where the component is replacing a component defined in an application lower on the application stack, setting the “replacementComponent” attribute to “true” will cause the component to be the replacement component for any replaceable components where this component implements the same business interface as the component being repaced.
• Extend GenericBusinessComponet
Algorithms
188
Algorithms - Overview• Where the system requires a customization, we provide for
customizable algorithms (a.k.a plug-ins)• Base algorithms exist, but can be cloned and modified• They are like “user exits”, but …
• Unlike Change Handlers, they are more related to the business functions and events
• Also, unlike Change Handlers, they use configurable (“soft”) parameters
• For example …• In the CC&B world, if a CSR requests a customers recommended
deposit amount, the system calls a “deposit recommendation algorithm” to calculate the amount
• If the base version of the algorithm is not appropriate for your business, a new algorithm can be coded to replace or supplement the base one
189
Algorithms - Overview• At upgrades, custom algorithms will not be overwritten• Here are a few more examples where algorithms are used
• Validating the format of a phone number entered by the user• Validating the format of a latitude/longitude geographic code
entered by the user.• Calculating late payment charges in CC&B• Calculating the recommended deposit amount in CC&B• In CC&B, constructing your GL account during the interface of a
financial transaction to your GL• Etc.
• Let’s take a look at how algorithms work internally …
190
Algorithms - OverviewAlgorithms are defined in 2 places …
Database tables• The online Admin menu is used to define the database components,
which are …• Algorithm Types
• Algorithms
• The event or activity to which the algorithm applies (e.g. FT freeze, phone number validation, etc.)
The OU Framework• The framework requires the “implementation” class – the program
that contains the logic – and various generated artifacts (we will only be looking at Java, not COBOL)
Let’s take a look at these definitions more closely, database components first and then the framework classes …
191
CI_ALG_TYPE
PK ALG_TYPE_CD
PGM_NAMEALG_ENTITY_FLGPGM_TYPE_FLG
CI_ALG
PK ALG_CD
FK1 ALG_TYPE_CDVERSION
CI_ALG_TYPE_PRM
PK,FK1 ALG_TYPE_CDPK SEQNO
PARM_REQ_SW
CI_ALG_PARM
PK,FK2 ALG_CDPK EFFDTPK,FK1 SEQNO
ALG_PARM_VALFK1 ALG_TYPE_CD
Customized Entity
FK1 ALG_CD
Algorithm Database Model
• These are the tables that define an algorithm1. Algorithm Type defines …
• The entity – e.g. Char Type – Adhoc Value Validation, SA Type – SA Creation, etc.
• The program name
• The program type – JAVA or COBOL
1
192
Algorithm Database Model
• These are the tables that define an algorithm2. Algorithm Type Parameter defines …
• The parameters expected by the algorithm program
• For each parameter, whether it is required or optional
CI_ALG_TYPE
PK ALG_TYPE_CD
PGM_NAMEALG_ENTITY_FLGPGM_TYPE_FLG
CI_ALG
PK ALG_CD
FK1 ALG_TYPE_CDVERSION
CI_ALG_TYPE_PRM
PK,FK1 ALG_TYPE_CDPK SEQNO
PARM_REQ_SW
CI_ALG_PARM
PK,FK2 ALG_CDPK EFFDTPK,FK1 SEQNO
ALG_PARM_VALFK1 ALG_TYPE_CD
Customized Entity
FK1 ALG_CD
1
2
193
CI_ALG_TYPE
PK ALG_TYPE_CD
PGM_NAMEALG_ENTITY_FLGPGM_TYPE_FLG
CI_ALG
PK ALG_CD
FK1 ALG_TYPE_CDVERSION
CI_ALG_TYPE_PRM
PK,FK1 ALG_TYPE_CDPK SEQNO
PARM_REQ_SW
CI_ALG_PARM
PK,FK2 ALG_CDPK EFFDTPK,FK1 SEQNO
ALG_PARM_VALFK1 ALG_TYPE_CD
Customized Entity
FK1 ALG_CD
Algorithm Database Model
• These are the tables that define an algorithm3. Algorithm defines an “instance” of the algorithm type – many
algorithms may exist for one algorithm type
4. Algorithm Parameter defines the parameter values for the instance
4
1
2
3
194
CI_ALG_TYPE
PK ALG_TYPE_CD
PGM_NAMEALG_ENTITY_FLGPGM_TYPE_FLG
CI_ALG
PK ALG_CD
FK1 ALG_TYPE_CDVERSION
CI_ALG_TYPE_PRM
PK,FK1 ALG_TYPE_CDPK SEQNO
PARM_REQ_SW
CI_ALG_PARM
PK,FK2 ALG_CDPK EFFDTPK,FK1 SEQNO
ALG_PARM_VALFK1 ALG_TYPE_CD
Customized Entity
FK1 ALG_CD
Algorithm Database Model
• These are the tables that define an algorithm5. The algorithm code is specified on the customized entity’s
configuration table – the algorithm will be invoked based on this specification
• Let’s look at an example …
5
Examples
Characteristic TypeSA Type
Etc.
4
1
2
3
195
Algorithm Phone Format ExamplePhone format - algorithm definition diagram
Algorithm Type:PHN-FMT
CI_ALG_TYPE
PK ALG_TYPE_CD
PGM_NAMEALG_ENTITY_FLGPGM_TYPE_FLG
CI_ALG
PK ALG_CD
FK1 ALG_TYPE_CDVERSION
CI_ALG_TYPE_PRM
PK,FK1 ALG_TYPE_CDPK SEQNO
PARM_REQ_SW
CI_ALG_PARM
PK,FK2 ALG_CDPK EFFDTPK,FK1 SEQNO
ALG_PARM_VALFK1 ALG_TYPE_CD
Phone Type
FK1 ALG_CD
Format 1 – requiredFormat 2 – optionalFormat 3 – optional
Algorithm:PHN-FMT-US(999) 999-9999
Format 4 thru 9 – optional Algorithm:PHN-FMT-UK999 9999-999999999 999999
9999 999-9999
Phone Type: BUSN
Phone Type: BUSN_UK
196
Algorithm Phone Format ExampleAdmin -> Algorithm Type – Phone format
1. Algorithm Type name – 12 characters
2. The Algorithm Entity determines where the system will allow this algorithm to be specified – in this case, the PHN-FMT algorithm type may only be specified on the Phone Type entity
3. The Program Type here is JAVA (it can also be COBOL)
2
3
4
1
5
197
Algorithm Phone Format ExampleAdmin -> Algorithm Type – Phone format
4. This is the algorithm component interface to format and validate the phone number
Note: for the JAVA class to be specified, the class must already exist as a framework component
5. This specifies that at least one phone format is required
2
3
4
1
5
198
Algorithm Phone Format ExampleAdmin -> Algorithm – Phone format US
1. This is the algorithm name – i.e. the 1st “instance” of the algorithm type
2. The Algorithm Type that this algorithm corresponds to
3. The effective date
4. The phone format for this instance – only one for the U.S.
3
1
4
2
199
Algorithm Phone Format ExampleAdmin -> Algorithm – Phone format UK
1. This is the algorithm name - i.e. the 2nd “instance” of the algorithm type
2. It has the same algorithm type as for the U.S. (i.e. it is the SAME PROGRAM that formats U.S. and U.K. phone numbers)
1
3
2
200
Algorithm Phone Format ExampleAdmin -> Algorithm – Phone format UK
3. The U.K. has multiple phone formats – the algorithm will validate a U.K. phone number against all of these formats
1
3
2
201
Algorithm Phone Format ExampleAdmin -> Phone Type – Algorithm usage
1
2
• This defines 2 types of business phones1. U.S. phone numbers are to be validated and formatted by the
“North American phone format” algorithm, as defined on the previous slides
2. U.K. phone numbers are to be validated and formatted by the “United Kingdom phone format” algorithm, as defined on the previous slides
202
Algorithm Phone Format ExampleMain -> Person – Algorithm activation
1
2
• On the Person page, Phone Type now determines the algorithm to invoke for the phone number formatting and validation
In this topic we have so far looked at defining the database entries required for an algorithm definition – next we’ll see how the framework components are built to support these definitions …
203
Algorithm Spots• The “call out” places in the system (e.g. from Person to validate phone
number) are known as algorithm spots• Each algorithm spot has an interface class• Communication with an algorithm takes place through the interface• An interface provides abstraction between the base and the
customization
Business Component
Algorithm ComponentAlgorithm Spot
Interface
Person Phone Person Phone PhoneTypeFormatValidationAlgComp
PhoneTypeFormatValidationAlgComp
PhoneTypeFormatValidationAlgorithmSpot
PhoneTypeFormatValidationAlgorithmSpot
204
Algorithm Spots• The attributes of an algorithm spot interface class are …
• The API to the algorithm component (from the base application)• It is specific to the algorithm entity type (or system “event”)• It defines the hard input parameters for an algorithm – these are the
parameters associated with a specific event• It defines the output parameters that can be retrieved after the
algorithm has been invoke• Algorithm spots also specify the schema defined for a plug-in script
205
Algorithm Spots• These classes form part of the base code• They are invoked from the base code at appropriate times (events)• The methods on the interface are related to the algorithm type – for
example, setPhoneValue() is only relevant to PhoneTypeFormatValidationAlgorithmSpot
+invoke()
«interface»AlgoritmSpot
+setFormatOnly() : void+setCharacteristicType() : void+setAdhocValue() : void+getReformattedValue() : String+isValidAdhoc() : Boolean
«interface»AdhocCharacteristicValueValidationAlgorithmSpot
+setPhoneValue() : void+setPhoneType() : void+isValidPhoneValue() : Boolean+getPreferredFormatString() : String+getReformattedPhoneValue() : String
«interface»PhoneTypeFormatValidationAlgorithmSpot
206
Adding Algorithm Spots
• Algorithm spots reference an AlgorithmEntityLookup value so a new lookup value must be added to correspond to the new spot
• Add an interface that defines the spot using the @AlgorithmSpot annotation. Properties include• algorithmEntity—One or more AlgorithmEntity values corresponding
to the lookup value described above• calledFromCobol—A boolean attribute that lets the framework know if
inbound call support is to be supported from COBOL• implementableInCobol—A boolean attribute that lets the framework
know if it must be able to call an algorithm implemented in COBOL• Extend the AlgorithmSpot interface• Wire up the call to the spot by accessing the AlgorithmComponent via
Algorithm.getAlgorithmComponent(…)
Example: See TimelineZoneAlgorithmSpot in FW
207
Supporting Plug-in Scripts
• Algorithms may also be implemented in Plug-in Scripts• In these cases, the Java signature gets interpreted by the
framework at runtime to generate the “parm” schema defining the parameters passed in and out of the plug-in script when invoked
• The framework needs to be able to figure out how to make XML out of the input parameters and make output parameter objects out of the output parameters. This cannot be done with every possible interface. The spot developer must help!
• Simple JavaBean compliant getters an setters should be used where• “set” methods represent values sent into the algorithm• “get” methods represent values returned
208
Supporting Plug-in Scripts
• Collections on the signature should have generic type information so the framework knows the class of the elements that it may contain.
• The ArtifactGenerator checks spot compatability with scripting and will issue an error if the spot cannot be used by the framework for plug-in scripts
• For unusable spots, there are two approaches• For new spots, the spots should be reworked to comply with
framework requirements• Some spots have been shipped before the plug-in spot technology
was created and were unknowingly non-complient. These spots cannot be reworked because they have been deployed to customers. Therefore a “BeanAdapter” class is necessary.• Please see the Wiki for more information on BeanAdapters
209
Algorithm Components• An algorithm requires a programmatic implementation• The Algorithm Type definition carries the program name,
for examplecom.splwg.base.domain.common.phoneType. PhoneTypeFormatValidationAlgComp
This name in fact specifies another interface which is generated from the implementation class
• The implementation class name = the interface name + “Impl”, for examplecom.splwg.base.domain.common.phoneType. PhoneTypeFormatValidationAlgComp_Impl
• The following diagram describes the full Phone Type Validation algorithm component …
210
Algorithm Components – Phone Type Validation
• Remember …• An interface is “empty” – it requires an implementation to perform
appropriate tasks• The implementation for an algorithm spot is an Algorithm
Component – i.e. Business Component
+setPhoneValue() : void+setPhoneType() : void+isValidPhoneValue() : Boolean+getPreferredFormatString() : String+getReformattedPhoneValue() : String
«interface»PhoneTypeFormatValidationAlgorithmSpot
+getPhoneFormat1() : String+getPhoneFormat2() : String+getPhoneFormat3() : String+getPhoneFormat4() : String+getPhoneFormat5() : String+getPhoneFormat6() : String+getPhoneFormat7() : String+getPhoneFormat8() : String+getPhoneFormat9() : String
PhoneTypeFormatValidationAlgComp_Gen
+invoke() : void+setPhoneValue() : void+setPhoneType() : void+isValidPhoneValue() : Boolean+getPreferredFormatString() : String+getReformattedPhoneValue() : String
PhoneTypeFormatValidationAlgComp_Impl
+invoke()
«interface»AlgoritmSpot
+setPhoneValue() : void+setPhoneType() : void+isValidPhoneValue() : Boolean+getPreferredFormatString() : String+getReformattedPhoneValue() : String
«interface»PhoneTypeFormatValidationAlgComp
211
+setPhoneValue() : void+setPhoneType() : void+isValidPhoneValue() : Boolean+getPreferredFormatString() : String+getReformattedPhoneValue() : String
«interface»PhoneTypeFormatValidationAlgorithmSpot
+getPhoneFormat1() : String+getPhoneFormat2() : String+getPhoneFormat3() : String+getPhoneFormat4() : String+getPhoneFormat5() : String+getPhoneFormat6() : String+getPhoneFormat7() : String+getPhoneFormat8() : String+getPhoneFormat9() : String
PhoneTypeFormatValidationAlgComp_Gen
+invoke() : void+setPhoneValue() : void+setPhoneType() : void+isValidPhoneValue() : Boolean+getPreferredFormatString() : String+getReformattedPhoneValue() : String
PhoneTypeFormatValidationAlgComp_Impl
+invoke()
«interface»AlgoritmSpot
+setPhoneValue() : void+setPhoneType() : void+isValidPhoneValue() : Boolean+getPreferredFormatString() : String+getReformattedPhoneValue() : String
«interface»PhoneTypeFormatValidationAlgComp
Algorithm Components – Phone Type Validation
12
3
1. The implementation class (…_Impl) is hand-coded – it can be customized
2. The component interface is generated by the artifact generator – a customized version will be generated for a custom “impl” class
3. An algorithm is invoked via its component interface
212
1. The implementation class contains the hand-coded logic
2. The _Gen class has the methods for the “soft” parameters (as specified on the Algorithm Type definition)Note: These are generated from the annotations in the “_Impl” class
+setPhoneValue() : void+setPhoneType() : void+isValidPhoneValue() : Boolean+getPreferredFormatString() : String+getReformattedPhoneValue() : String
«interface»PhoneTypeFormatValidationAlgorithmSpot
+getPhoneFormat1() : String+getPhoneFormat2() : String+getPhoneFormat3() : String+getPhoneFormat4() : String+getPhoneFormat5() : String+getPhoneFormat6() : String+getPhoneFormat7() : String+getPhoneFormat8() : String+getPhoneFormat9() : String
PhoneTypeFormatValidationAlgComp_Gen
+invoke() : void+setPhoneValue() : void+setPhoneType() : void+isValidPhoneValue() : Boolean+getPreferredFormatString() : String+getReformattedPhoneValue() : String
PhoneTypeFormatValidationAlgComp_Impl
+invoke()
«interface»AlgoritmSpot
+setPhoneValue() : void+setPhoneType() : void+isValidPhoneValue() : Boolean+getPreferredFormatString() : String+getReformattedPhoneValue() : String
«interface»PhoneTypeFormatValidationAlgComp
Algorithm Components – Phone Type Validation
12
213
3. The generated component interface class also contains a “factory” class to create an instance of an algorithm component – we’ll see an example of this shortly
+setPhoneValue() : void+setPhoneType() : void+isValidPhoneValue() : Boolean+getPreferredFormatString() : String+getReformattedPhoneValue() : String
«interface»PhoneTypeFormatValidationAlgorithmSpot
+getPhoneFormat1() : String+getPhoneFormat2() : String+getPhoneFormat3() : String+getPhoneFormat4() : String+getPhoneFormat5() : String+getPhoneFormat6() : String+getPhoneFormat7() : String+getPhoneFormat8() : String+getPhoneFormat9() : String
PhoneTypeFormatValidationAlgComp_Gen
+invoke() : void+setPhoneValue() : void+setPhoneType() : void+isValidPhoneValue() : Boolean+getPreferredFormatString() : String+getReformattedPhoneValue() : String
PhoneTypeFormatValidationAlgComp_Impl
+invoke()
«interface»AlgoritmSpot
+setPhoneValue() : void+setPhoneType() : void+isValidPhoneValue() : Boolean+getPreferredFormatString() : String+getReformattedPhoneValue() : String
«interface»PhoneTypeFormatValidationAlgComp
Algorithm Components – Phone Type Validation
3
214
Algorithm Implementation Class• The base versions of all algorithms are provided• To create a new one, it is easiest to duplicate the
appropriate base one if it exists and modify it• The basic Java elements of a new algorithm are:
• An “_Impl” class, the hand-coded implementation class that contains the logic
• A “_Gen” class, the implementation class for the “soft” parameters, generated by the artifact generator
• A component interface class, generated by the AG• A message method, if required
215
Algorithm Phone Format Example
• This Impl class’s annotation specifies …1. The soft parameters expected by the algorithm – these correspond with
the Algorithm Type parameter definitions
2. Has an Algorithm Component name (as specified on the Algorithm Type) + “_Impl”
1
234
216
Algorithm Phone Format Example
• The class name …3. Extends the “_Gen” class – the “_Gen” class is generated by the
Artifact Generator
4. Implements the base Algorithm Spot class for the algorithm type
1
234
217
Algorithm Phone Format Example
• These methods are invoked by the business component to set the hard parameters1. This sets the phone number to validate – it is stored here for use later
2. This sets the phone type – this method here is empty because it is ignored in this algorithm
Algorithm Spot interface methods that are implemented in _Impl class …
1
2
218
Algorithm Phone Format Example
• The invoke() method is called to validate and format the phone number1. This sets a Boolean based on the validity of the phone number
Algorithm Spot interface methods that are implemented in _Impl class …
1
219
Algorithm Phone Format ExampleAlgorithm Spot interface methods that are implemented in _Impl class …
• These methods are called from the business component after the invoke() method1. This returns a true/false to indicate the validity of the phone number
(you may have noted that this Boolean is the one set in the invoke method)
2. This returns the reformatted value, also formatted in invoke()
1
2
3
220
Algorithm Phone Format ExampleAlgorithm Spot interface methods that are implemented in _Impl class …
• These methods are called from the business component after the invoke() method3. This returns the default phone format – in this case it is always the first
one (e.g. 1st format for U.K. – “999 9999-9999”)
1
2
3
221
Algorithm Phone Format ExampleGenerated artifacts that are based on the _Impl class annotation …
• The _Gen class has the methods for the soft parameters
• The _Impl class calls these methods to get the soft parameter values, as set on the Algorithm definition
222
Algorithm Phone Format ExampleGenerated artifacts that are based on the _Impl class annotation …
• The component interface defines the required methods for the _Impl class as viewed from the application (the business component)
223
Algorithm Phone Format ExampleGenerated artifacts that are based on the _Impl class annotation …
1. The Artifact Generator also generates a static Factory class in the component interface class file
2. It has the newInstance method to create an instance of the algorithm component at runtime
12
224
Algorithm Implementation Class Review• The steps to create a new algorithm class are …
1. Determine the Algorithm Spot interface name – the Javadocs could be used for this
2. Create the “_Impl” class, implementing the appropriate Algorithm Spot interface
3. Add default implementations for all the Algorithm Spot methods (e.g. using the Eclipse Source, Override/implement Methods… menu item)
4. Code the annotation
5. Run the Artifact Generator to create the “_Gen” and component interface classes
In Eclipse, you MUST refresh the project after this!
225
Algorithm Implementation Class Review• The steps to create a new algorithm class are …
6. Edit the “_Impl” class and …• Extend the “_Gen” class on the class definition• Store the required instances variables in the appropriate set… methods (these
instance variables will be referenced in the invoke() method)• Code the invoke() method – this contains the main logic• Return the value(s) in the get… methods as required by the Algorithm spot
7. Create the cm.jar file and shutdown and restart the application server – this is necessary for the Java class to be visible when creating the Algorithm Type definition (next step)
8. Create the Algorithm Type definition – the Java class name will now be in the Program Name drop-down list AND will automatically insert the Algorithm Type parameters from your annotation specification
NOTE: This is the reason for creating the Java impl class BEFORE adding the Algorithm Type and Algorithm definitions in the UI
226
Algorithm Implementation Class Review• The steps to create a new algorithm class are …
9. Create the Algorithm definition
10. Attach the algorithm to the business component
11. Test
227
Exercise 8 – Geographic Type AlgorithmAll Students• In this exercise you will create a Geographic Type algorithm to
validate geographic type “REGION” on Premise• Consider the following scenario …
1. Premises may be in North America or Europe
2. Within North America, the country may be U.S.A. or Canada
3. Within Europe, the country may be United Kingdom or France
4. A geographic type of “region” must be created to allow for the region code to be specified on Premise
5. The format of region code is W-CC, where …• W = World Region – “N” (North America); “E” (Europe)• CC = Country Code – “US” (USA); “CN” (Canada); “UK” (UK); “FR” (France)
The algorithm must validate that the country code falls within its region – for example E-FR is valid; E-US is invalid
228
Exercise 8 – Geographic Type AlgorithmAll Students (continued)
6. The valid region code values must be specified as soft parameters – i.e. no hardcoding of values in Java are allowed
• Java Requirements• The Algorithm Spot to implement is GeoTypeFormatValidationAlgorithmSpot• Name your class CmGeoTypeRegionAlgComp_Impl in package
com.splwg.cm.domain.common.geographicType• Create 2 parameters …
1. worldRegion, string, required
This will contain the 1-digit world region code
2. countryCodes, string, required
This will contain a comma-delimited string of valid country codes for their associated world region code
229
Exercise 8 – Geographic Type Algorithm• Online Configuration
• Create Algorithm Type XXGEOREGION (where XX are your initials) …• The Algorithm Entity is “Geographic Type – Value Format Rule”• Program Type is “Java”• Program Name should be CmGeoTypeRegionAlgComp
• Create two Algorithm definitions …1. XXGEOREG-E (where XX are your initials)
Specifies Europe and its valid countries
2. XXGEOREG-N (where XX are your initials)
Specifies North America and its valid countries
• Create two new Geographic Type definitions, using the following values for Type, Description and Format Algorithm respectively …1. XXREG-E, “Region Europe (XX)”, XXGEOREG-E (where XX are your initials)
2. XXREG-N, “Region North America (XX)”, XXGEOREG-N (where XX are your initials)
230
Exercise 8 – Geographic Type Algorithm• Online Configuration (continued)
• From your browser, use URL http://localhost:6800/flushDropdownCache.jsp to delete the drop-down cache on the server, and then clear the browser cache by deleting your “temporary internet files” to make the new Geographic Types known to the Premise Geographic Data page(Note: “localhost” and “6800” above refer to the server name and port of your web application server – it may be named differently in your specific case)
Test• When adding or changing a Geographic Type on Premise, the
algorithm should validate the format and content of geo type and display the generic message “Premise Geographic Value format incorrect” if invalid
OptionalThe JUnit test class for this exercise should check all possible combinations of invalid world regions and country codes, but we will only check two conditions – one valid and one invalid
231
Exercise 8 – Geographic Type AlgorithmOptional (continued)• Code a JUnit test class for your algorithm (the class must extend
AlgorithmImplementationTestCase) and in the test method – e.g. testGeoTypeRegion …• Similar to previous exercises, create a Premise object from an existing
premise on the database
• Create a new GeographicType_Id object for one of the geographic types you defined (e.g. “XXREG-E” or “XXREG-N”)
• Create a PremiseGeographicLocation_Id object for the Premise_Id (as instantiated above) and GeographicType_Id
• Suspend validation for the following steps
• Create a new PremiseGeographicLocation_DTO from the Id above
• On the DTO, set the value of GeographicValue (e.g. “E-UK”)
• Add the new GeographicLocation (via the DTO) to the collection of GeographicLocations for the Premise (note: the add method can also accept the DTO with the GeographicType_Id (e.g. “XXREG-E”)
232
Exercise 8 – Geographic Type AlgorithmOptional (continued)
• Remove the suspension to validate that the data currently entered is valid – an application error is not expected at this point
• Remove the existing PremiseGeographicLocation from the collection of GeographicLocations
• Set the GeographicValue to an invalid value (e.g. “E-US”)• Re-add the PremiseGeographicLocation object to the GeographicLocations
location which should trigger the validation• Catch the ApplicationError and use the following statement to verify that the correct
error was thrown …assertServerMessage(e, 3, 33901);
Creating Algorithm Spots
234
Creating Algorithms Spots
Batch
236
SPL Batch I• In this section we will look at …
• A brief overview of the SPL batch environment• An overview of batch controls• Batch programming in Java
237
Batch Environment• Batch programs run outside of the web app server, but still
within the context of the framework• The framework provides access to the business entities and
other objects• Therefore, ALL programs run under Java – even COBOL• For COBOL, all SQL calls are handled by the framework –
there are no direct calls from COBOL to Oracle/DB2/SQL Server (e.g. via PRO*Cobol)
238
Batch Threading• The system uses threading to achieve maximum
performance• What does a threaded process do?
• It determines the full workload (e.g. how many accounts?)• It divides the workload into n number of threads• It dispatches each thread to perform the workload – the workloads
are executed concurrently
• For example, instead of one task billing 50000 accounts, 5 tasks can each bill 10000 accounts simultaneously – this is naturally much faster
• The Artifact Generator creates the necessary components for multi-threading and the batch framework facilitates its execution
239
Batch Control• Batch controls are used in batch processing to control
restartibility, program identification, parameters, etc.• Each batch process has its own batch control code• A batch control code defines …
• The program name• The program type (COBOL / Java)• The batch parameters
• For example …
240
Batch Control – Java Example
1. The Batch Control code
2. The Program Type
3. The Program Name
1
23
4
6
5
241
Batch Control – Java Example
4. The batch run number (for this batch control) of the next submission5. Check this to accumulate the “records processed” counts for all instances
of one job – if the job fails and restarts, it will add the “failed” + “restarted” instances for the total records processed – should be unchecked for programs that work with flat files
1
23
4
6
5
242
Batch Control – Java Example
6. These are the non-standard parameters for the batch process – these parameters are prompted for after the standard parametersThey are supplied at runtime using name=value pairs, for exampleFILE-PATH=/spl/data
1
23
4
6
5
243
Batch Process Overview• A background process consists of 2 types of classes …
1. A BatchJob
2. A ThreadWorker
• The BatchJob class determines the work to be processed and splits it into chunks based on a thread count
• Each “chunk” of work is processed by an instance of ThreadWorker
• Let’s take a look at these two components …
244
Batch Process Overview
• BatchJob determines the work and divides it into manageable chunks (ThreadWork)
• Each ThreadWork consists of WorkUnits• WorkUnits represent fine-grained units of work – e.g. one bill
245
Batch Process Overview
• The ThreadWorker is responsible for processing a single ThreadWork instance
• Each ThreadWorker corresponds to one batch instance row on the database, which is used for restart after failure
246
The BatchJob Class• The BatchJob class consists of an annotation and a number
of standard methods that the framework uses to invoke and communicate with a batch process
• Let’s take a look at these components of a BatchJob class …
247
BatchJob Class Annotation
1. rerunnable indicates if the job can be re-run – e.g. bill print extract can be re-run
2. multiThreaded tells the framework if job can be submitted in multiple threads – i.e. if it will accept a thread count > 1
3. modules contains the modules the program belongs to – this should be empty for customizations
@BatchJob (rerunnable = false,
multiThreaded = true,
modules={todo},
softParameters = { @BatchJobSoftParameter (name=OUTPUT-DIR, type=string) },
toDoCreation = @ToDoCreation ( drillKeyEntity = user, sortKeys = {lastName, firstName}, messageParameters = {firstName, lastName} ) )
12
3
4
5
• A BatchJob class annotation looks as follows …
248
BatchJob Class Annotation
4. These are the non-standard parameters to prompt for when the job starts
5. This specifies how the “ToDo” entries should be created if application errors are found
• A BatchJob class annotation looks as follows …
@BatchJob (rerunnable = false,
multiThreaded = true,
modules={todo},
softParameters = { @BatchJobSoftParameter (name=OUTPUT-DIR, type=string) },
toDoCreation = @ToDoCreation ( drillKeyEntity = user, sortKeys = {lastName, firstName}, messageParameters = {firstName, lastName} ) )
12
3
4
5
249
BatchJob Class Definition• The BatchJob class must extend class
“<BatchJob class Name>_Gen”, for example
• The _Gen class is generated by the artifact generator based on the BatchJob annotation
public class BatchErrorToDoCreation extends BatchErrorToDoCreation_Gen {
250
BatchJob Class Public Methods• The following standard public methods must exist in a
BatchJob class …• Method getJobWork()
• This method determines the workload (using a Hibernate query) –• It passes back to the framework an instance of JobWork,
describing the work to be done
The JobWork instance contains the appropriate number of ThreadWork instances (based on the runtime thread count)
Each ThreadWork instance contains the same number of ThreadWorkUnits
The good news: a convenience framework method exists to handle this for you!
251
BatchJob Class Public Methods• Method getThreadWorkerClass()
• This method passes back to the framework the class name of the “worker” class – i.e. the hand-coded class that will perform the batch work
• The framework will create an instance of this class for each ThreadWork instance – i.e. each thread
• Passing back the class name (instead of an object) allows the framework to control the instantiation of the ThreadWorker classes so that they can be executed anywhere, even on different servers
252
The ThreadWorker Class• The ThreadWorker class is responsible for processing a single
ThreadWork instance – in other words, it performs the “heavy lifting”• A ThreadWorker can be executed on the same, or a different, server
as its associated BatchJob and other ThreadWorker instances• A ThreadWorker class must extend the artifact-generated class
“<ThreadWorker class name>_Gen” (the example below illustrates)• By convention a ThreadWorker class is coded as a static inner class
in its parent BatchJob class
public class BatchErrorToDoCreation extends BatchErrorToDoCreation_Gen {
... BatchJob methods ...
public static class BatchErrorToDoCreationWorker extends BatchErrorToDoCreationWorker_Gen {
... ThreadWorker methods ...
}}
253
ThreadWorker Class Public Methods• The following standard public methods are implemented
in a ThreadWorker class …
254
ThreadWorker Class Public Methods• Method createExecutionStrategy()
• This methods tells the framework how the work for thread will be processed
• It returns to the framework an instance of ThreadExecutionStrategy, of which the following implementations currently exist … SingleTransactionStrategy
This tells the framework to process all work in single transaction (logical unit-of-work)
Failure results in a rollback of the entire thread’s updates
It is most appropriate for jobs that CANNOT tolerate errors in the middle of a run, for example interfaces from flat files
CommitEveryUnitStrategy
This tells the framework to process each ThreadWorkUnit (e.g. one bill) in its own transaction and to issue a commit after each successful completion
255
ThreadWorker Class Public Methods• Method createExecutionStrategy()
• ThreadExecutionStrategy implementations continued… StandardCommitTransactionStrategy
This tells the framework to process up to a configurable number of work units within the same transaction to increase performance. If an error occurs, the previously successful units are reprocessed and the record and error skipped after logging the error.
It is most appropriate for jobs with high performance demands that must also tolerate errors.
256
ThreadWorker Class Public Methods• Method InitializeThreadWork(boolean)
• This is called by the framework at the start of a thread run to perform initialization
• By default it does nothing, but can be overridden to open files, initialize variables, etc.
• A Boolean parameter indicates if it was called previously
• Method executeWorkUnit(ThreadWorkUnit)• This is invoked once for each ThreadWorkerUnit assigned to a
ThreadWorker• For example, for a thread processing 1000 accounts, this method
will be called 1000 times• It performs the actual work, for example to process an account for
billing• The ThreadWorkUnit parameter is a reference to the Business
Entity to be processed – you use this parameter’s getPrimaryId method to get the business entity’s Id object
257
ThreadWorker Class Public Methods• Method finalizeThreadWork()
• This method is called at the end of thread processing• It can be used to clean up after a thread, for example to close files
These are the basic building blocks for a batch classLet’s take a look at a complete example that creates ToDos for batch runs that ended in error …
258
BatchJob Example – Create Batch Error ToDos
/** * @BatchJob (rerunnable = false, * multiThreaded = false, * modules={todo}) */public class CmBatchErrorToDoCreation extends CmBatchErrorToDoCreation_Gen {
private static final Logger logger = LoggerFactory.getLogger(CmBatchErrorToDoCreation.class);...
1. The BatchJob annotation specifies this job is not rerunnable, not multi-threaded and a “ToDo” module (remember, for a custom batch class, modules should be empty – i.e. {})
2. The class extends its associated _Gen class
3. This is a static definition of the Log4j logger to log messages at runtime (Note: You really should not log application messages to the system log, but for these exercises we will)
1
2
3
• The annotation and class definition
259
BatchJob Example – Create Batch Error ToDos
1. The getJobWork method sets up query for the work – this query retrieves BatchRun objects (CI_BATCH_RUN) that have an error status
2. It sets the runStatus bind variable in the query to the lookup constant ERROR
• The BatchJob methods public JobWork getJobWork() { Query errorRunQuery = createQuery("from BatchRun run “+ “where run.runStatus = :runStatus");
errorRunQuery.bindLookup("runStatus", RunStatusLookup.constants.ERROR);
return createJobWorkForEntityQuery(errorRunQuery); }
public Class getThreadWorkerClass() { return CmBatchErrorToDoCreationWorker.class; }
1
2
3
4
260
BatchJob Example – Create Batch Error ToDos
3. By passing the query to the convenience method createJobWorkForEntity, it returns to the framework a JobWork object which will contain an array of ThreadWork objects, each with ThreadWorkerUnits
4. The getThreadWorkerClass method returns to the framework the name of the ThreadWorker class – this class is defined as an inner class (as we’ll see on the next slide)
• The BatchJob methods
public JobWork getJobWork() { Query errorRunQuery = createQuery("from BatchRun run “+ “where run.runStatus = :runStatus");
errorRunQuery.bindLookup("runStatus", RunStatusLookup.constants.ERROR);
return createJobWorkForEntityQuery(errorRunQuery); }
public Class getThreadWorkerClass() { return CmBatchErrorToDoCreationWorker.class; }
1
2
3
4
261
BatchJob Example – Create Batch Error ToDos
public static class CmBatchErrorToDoCreationWorker extends CmBatchErrorToDoCreationWorker_Gen { ...
• The class is defined in the same source file as the BatchJob class and is therefore an “inner” class
• This is the class whose name is passed back to the framework in the getThreadWorkerClass method – as seen on the previous slide
• It extends its associated _Gen thread worker class, which is created by the artifact generator
• The inner ThreadWorker class definition
262
BatchJob Example – Create Batch Error ToDos
public ThreadExecutionStrategy createExecutionStrategy() {
// Commit on every todo. This is not expected to be a // performance intensive process. return new CommitEveryUnitStrategy(this); }
• createExecutionStrategy passes back to the framework an object of type CommitEveryUnitStrategy, which tells the framework that the job wants to commit after each transaction
• A “transaction” in this case is after each ToDo has been created
• Inner ThreadWorker method createExecutionStrategy
263
BatchJob Example – Create Batch Error ToDos
public void initializeThreadWork( boolean initializationPreviouslySuccessful) throws ThreadAbortedException, RunAbortedException {
// determine the ToDoType_Id that should be used by querying // the ToDoTypes for an entry with the creation process equal // to this one. Query toDoTypeQuery = createQuery("from ToDoType type where type.creationProcessId = :thisBatchControlId"); ...
• initializeThreadWork here determines the ToDo type to be used by the executeWorkUnit method (next slide) when it creates the ToDos
• Inner ThreadWorker method initializeThreadWork
264
BatchJob Example – Create Batch Error ToDos
public boolean executeWorkUnit(ThreadWorkUnit unit) throws ThreadAbortedException, RunAbortedException {
BatchRun_Id errorBatchRunId = (BatchRun_Id) unit.getPrimaryId();
// get the run BatchRun batchRun = errorBatchRunId.getEntity(); ... createToDo(batchRun); ...
1. executeWorkUnit is called for each batch run in error, based on the initial query
2. It gets the primary Id from the ThreadWorkUnit object parameter and casts it to a BatchRun_Id class
3. It uses the Id object to get the BatchRun entity object for which the ToDo is to be created
4. It creates the ToDo entry
• Inner ThreadWorker method executeWorkUnit
1
2
3
4
265
BatchJob Example – Create Batch Error ToDos
• Inner ThreadWorker method finalizeThreadWork
This example does not implement the finalizeThreadWork method
public void finalizeThreadWork() throws ThreadAbortedException, RunAbortedException { }
266
BatchJob Creation Review
• Before we get to the exercise, let’s review how to create a new BatchJob class …1. Create the class file and code the annotation
2. Run the Artifact Generator to create the _Gen classes (you must refresh the Eclipse project after the AG has run)
3. Extend the class from the _Gen BatchJob class
4. Code the getJobWork and getThreadWorkerClass methods
5. Create the inner ThreadWorker class - extend it from the _Gen ThreadWorker class
6. Code the createExecutionStrategy method
7. Code the initializeThreadWork method (if needed)
8. Code the executeWorkUnit method
9. Code the finalizeThreadWork method (if needed)
10. Create the batch control
267
Exercise 10 – Batch 1All Students• Create a batch program to identify Persons without Accounts and display
their details on the log …• Name the program CmPersonsNoAccount in package com.splwg.cm.domain.batch• Identify the persons as follows …
• In the executeWorkUnit method, the ThreadWorkUnit parameter has a method getPrimaryId to get the Person_Id object – from this, determine the Person and the “primary” PersonName
• Display both the Person Id and the Entity Name on the log using the logger.info(…) method – the output should look like this …
from Person as per where not exists (from AccountPerson ap where ap.id.person = per)
Person without Account: 4470529669 French,JohnPerson without Account: 3909447990 Reliable Energy...
268
Exercise 10 – Batch 1All Students (continued)
• Add a new batch control using the online Admin menu – use batch code XXPNOA (where XX are your initials)
Test• Use the Batch - Training launch configuration from Eclipse to run
your job
The following run parameters should be specified:• Batch Code: Your batch code (e.g. XX-PNOA)
• Batch Thread Number: 1
• Batch Thread Count: 1
• User ID: SPL
• User Password: spladmin
• Language Code: ENG
Take the defaults for all the other parameters (i.e. hit Enter when prompted)
Batch II
270
SPL Batch II - ToDo’s
• In this section we look at how to create “ToDos” in a batch process
• FYI: The previous example (CmBatchErrorToDoCreation) class does that
It runs through all the batch program errors (CI_BATCH_RUN rows with status = “error”) and creates a ToDo for each
• The ToDo table structure looks as follows …
271
CI_TD_ENTRY
PK TD_ENTRY_ID
FK1 TD_TYPE_CD
CI_TD_TYPE
PK TD_TYPE_CD
CRE_BATCH_CDFK1 MESSAGE_CAT_NBRFK1 MESSAGE_NBRFK2 BATCH_CD
CI_TD_DRLKEY
PK,FK1 TD_ENTRY_IDPK SEQ_NUM
KEY_VALUE
CI_TD_SRTKEY
PK,FK1 TD_ENTRY_IDPK SEQ_NUM
KEY_VALUE
CI_TD_MSG_PARM
PK,FK1 TD_ENTRY_IDPK SEQ_NUM
MSG_PARM_VAL
CI_TD_SRTKEY_TY
PK,FK1 TD_TYPE_CDPK SEQ_NUM
DEFAULT_SWORDER_FLG
CI_TD_DRLKEY_TY
PK,FK1 TD_TYPE_CDPK SEQ_NUM
FLD_NAMETBL_NAME
CI_MSG
PK MESSAGE_CAT_NBRPK MESSAGE_NBR
FK1 TD_TYPE_CD
CI_BATCH_CTRL
PK BATCH_CD
PROGRAM_NAME
SPL Batch II – ToDo Data Model
CONFIGURED
PROGRAMMED
1
54
2
3
• The tables on the left are populated via the Admin menu1. ToDo Type identifies the type of ToDo
2. Drill Key Type identifies which page to jump to when the user selects a ToDo to “work” on
3. Sort Key Type determines how the ToDo list is sorted
272
CI_TD_ENTRY
PK TD_ENTRY_ID
FK1 TD_TYPE_CD
CI_TD_TYPE
PK TD_TYPE_CD
CRE_BATCH_CDFK1 MESSAGE_CAT_NBRFK1 MESSAGE_NBRFK2 BATCH_CD
CI_TD_DRLKEY
PK,FK1 TD_ENTRY_IDPK SEQ_NUM
KEY_VALUE
CI_TD_SRTKEY
PK,FK1 TD_ENTRY_IDPK SEQ_NUM
KEY_VALUE
CI_TD_MSG_PARM
PK,FK1 TD_ENTRY_IDPK SEQ_NUM
MSG_PARM_VAL
CI_TD_SRTKEY_TY
PK,FK1 TD_TYPE_CDPK SEQ_NUM
DEFAULT_SWORDER_FLG
CI_TD_DRLKEY_TY
PK,FK1 TD_TYPE_CDPK SEQ_NUM
FLD_NAMETBL_NAME
CI_MSG
PK MESSAGE_CAT_NBRPK MESSAGE_NBR
FK1 TD_TYPE_CD
CI_BATCH_CTRL
PK BATCH_CD
PROGRAM_NAME
SPL Batch II – ToDo Data Model
CONFIGURED
PROGRAMMED
1
54
2
3
• The tables on the left are populated via the Admin menu4. Message specifies the message to show in the ToDo list
5. Batch Control contains the batch control of the job that created the ToDo
273
CI_TD_ENTRY
PK TD_ENTRY_ID
FK1 TD_TYPE_CD
CI_TD_TYPE
PK TD_TYPE_CD
CRE_BATCH_CDFK1 MESSAGE_CAT_NBRFK1 MESSAGE_NBRFK2 BATCH_CD
CI_TD_DRLKEY
PK,FK1 TD_ENTRY_IDPK SEQ_NUM
KEY_VALUE
CI_TD_SRTKEY
PK,FK1 TD_ENTRY_IDPK SEQ_NUM
KEY_VALUE
CI_TD_MSG_PARM
PK,FK1 TD_ENTRY_IDPK SEQ_NUM
MSG_PARM_VAL
CI_TD_SRTKEY_TY
PK,FK1 TD_TYPE_CDPK SEQ_NUM
DEFAULT_SWORDER_FLG
CI_TD_DRLKEY_TY
PK,FK1 TD_TYPE_CDPK SEQ_NUM
FLD_NAMETBL_NAME
CI_MSG
PK MESSAGE_CAT_NBRPK MESSAGE_NBR
FK1 TD_TYPE_CD
CI_BATCH_CTRL
PK BATCH_CD
PROGRAM_NAME
SPL Batch II – ToDo Data Model
CONFIGURED
PROGRAMMED
• The tables on the right are populated programmatically1. ToDo Entry has one row per ToDo entry and a foreign key to
the ToDo Type
2. Drill Key contains the key value (e.g. person Id) to use for the navigational “drill down” when the user selects the ToDo to “work” on
1
4
2
3
274
CI_TD_ENTRY
PK TD_ENTRY_ID
FK1 TD_TYPE_CD
CI_TD_TYPE
PK TD_TYPE_CD
CRE_BATCH_CDFK1 MESSAGE_CAT_NBRFK1 MESSAGE_NBRFK2 BATCH_CD
CI_TD_DRLKEY
PK,FK1 TD_ENTRY_IDPK SEQ_NUM
KEY_VALUE
CI_TD_SRTKEY
PK,FK1 TD_ENTRY_IDPK SEQ_NUM
KEY_VALUE
CI_TD_MSG_PARM
PK,FK1 TD_ENTRY_IDPK SEQ_NUM
MSG_PARM_VAL
CI_TD_SRTKEY_TY
PK,FK1 TD_TYPE_CDPK SEQ_NUM
DEFAULT_SWORDER_FLG
CI_TD_DRLKEY_TY
PK,FK1 TD_TYPE_CDPK SEQ_NUM
FLD_NAMETBL_NAME
CI_MSG
PK MESSAGE_CAT_NBRPK MESSAGE_NBR
FK1 TD_TYPE_CD
CI_BATCH_CTRL
PK BATCH_CD
PROGRAM_NAME
SPL Batch II – ToDo Data Model
CONFIGURED
PROGRAMMED
• The tables on the right are populated programmatically3. Sort Key contains the value(s) to sort on (e.g. person name)
4. Message Parameter contains the values for message substitution
1
4
2
3
275
Exercise 11 – Batch 2
All Students• Modify the previous exercise’s class to create ToDo entries for Persons
without Accounts …• The ToDo creation methods to populate the right-sided tables as described on the
previous slide have too much complexity for this course, so the instructor will supply a template for the ToDo creation methods – you can simply copy and paste those into your class
• The supplied methods are …• public void initializeThreadWork
• private boolean toDoAlreadyExists
• private void createToDo
• private void addDrillKeyValue
• private void addSortKeyValue
• private void addMessageParameter
276
Exercise 11 – Batch 2All Students (continued)
• In your executeWorkUnit method …• As in the previous exercise, determine the Person_Id and PersonName_Id values
• Using the supplied method, determine if the ToDo already exists and, if so, write a message to the system log indicating such and return with a Boolean of true
• Optionally, write a message to the system log to show the person id and name being added to the ToDo
• Use the supplied method to create the ToDo entry
• Follow the instructions over the following slides to add the configuration data for the batch control, message and ToDo
Important note: Over the following slides, all the examples specify names that contain XX. To avoid overwriting another student’s definitions, substitute XX in all cases with your own 2-digit initials
277
Exercise 11 – Batch 2All Students (continued)• Create new batch control XX-TDPNA
• Create new message “Person %1 has no Accounts (XX)”
278
Exercise 11 – Batch 2All Students (continued)• Create new ToDo type XXTDPNA – Main tab
• ToDo Type XXTDPNA – Roles tab
279
Exercise 11 – Batch 2All Students (continued)• ToDo Type XXTDPNA – Sort Keys tab
• ToDo Type XXTDPNA – Drill Keys tab
280
Exercise 11 – Batch 2Test• Use the Batch - Training launch configuration from Eclipse to run your job
• Verify in the online system that it created a ToDo entry for each Person identified as not having an Account …• It should show the message with person name substituted• It should be sorted in person name ascending sequence• You should be able to drill into a person from the ToDo list
• With this exercise we do not create a JUnit test class, but it can be done in one of two ways …1. Extending the BatchJobTestCase class and implementing abstract methods
2. Calling the submitBatchJob(SubmissionParameters) method in any ContextTestCase - this allows testing a mix of one or more background process and other business logic to be tested
In both cases, the database does not get updated
281
Exercise 11 – Batch 2
delete from ci_td_drlkey where td_entry_id in (select td_entry_id from ci_td_entry where td_type_cd = ‘XXTDPNA');delete from ci_td_srtkey where td_entry_id in (select td_entry_id from ci_td_entry where td_type_cd = ‘XXTDPNA');delete from ci_td_msg_parm where td_entry_id in (select td_entry_id from ci_td_entry where td_type_cd = ‘XXTDPNA');delete from ci_td_entry where td_type_cd = ‘XXTDPNA'
Test (continued)Note: When testing your batch class and an error occurs, run the following delete statements before re-executing your batch class – these statements will delete all previously created ToDo entries to prepare for the re-run
End of day 4
283