Manager Builder for OT Cordys

When developing a business function like Contract Management, Purchasing, Inventory Management, etc, there can be quite a number of related UI’s. Think of UI’s for configuration, master data setup, viewing invoice data, UI’s for processing invoices, utilities, reports, BAM, etc. Rather than having many individual UI task items or UI’s with lot of tabs, alternatively you can build a Manager UI for the particular business function. E.g. for Purchase Management:

Purchasing Manager

The menu on the left has static items like the UI’s for Configurations, and dynamic items like the individual Invoices for a particular Supplier. The items do have ACL attached for role-based access. The related UI for an item gets opened in the right pane, optionally in a new tab.
Best way to build the menu tree is to have a config file in between as to define the structure and build the tree in OT Cordys by utility function:

<Tree xmlns="http://schemas.cordys.com/1.0/xmlstore">
 <Name>PurchasingManager</Name>
 <RootId>PurchasingManager</RootId>
 <Node>
  <Id>PurchasingManager</Id>
  <TreeItemTag>Folder</TreeItemTag>
  <Description>Purchasing Manager</Description>
  <Children>
   <NodeId>MasterData</NodeId>
   <NodeId>Invoices</NodeId>
   ...
  </Children>
 </Node>
 ...
 <Node>
  <Id>MasterData</Id>
  <TreeItemTag>Folder</TreeItemTag>
  <Description>Master Data</Description>
  <AccessControl>
   <Role>cn=PurAdmin,cn=Purchasing</Role>
  </AccessControl>
  <Children>
   <NodeId>BusinessUnits</NodeId>
   <NodeId>CostCentres</NodeId>
   ...
  </Children>
 </Node> 
 ...
 <Node>
  <Id>BusinessUnits</Id>
  <TreeItemTag>Application</TreeItemTag>
  <Description>Business Units</Description>
  <App>appBusinessUnits</App>
 </Node>
 ...
 <Node>
  <Id>InvoicesBySupplier</Id>
  <TreeItemTag>Folder</TreeItemTag>
  <Description>By Supplier</Description>
  <Children>
   <NodeId dynamicContentDirective="InvoiceSuppliers">InvSupplier</NodeId>
  </Children>
 </Node>
</Tree>

The dynamicContentDirective directs this dynamic part of the tree to be build by webservice server-side as per database content.

Composite Control Development in OT Cordys

 
Some background and basics

Cordys UI development enables development of ‘desktop-like’ UI’s running in a browser. Development itself is also browser hosted. In Cordys UI development, 2 layers can be distinguished as important to Application Developers: Web Toolkit and XForms. Web Toolkit is a library of components by which html can be enriched. It also has a component for communication to the Cordys Bus via web gateway. The components come as html components, residing in <cordys instance>/webroot/shared/wcp/library:

Toolkit Components Folder

When developing your own components, it can be useful to have a look at some sources of these components.

The other layer, the XForms layer, goes further than offering an API; XForms enables to model UI’s using WYSIWYG. The models are stored as XML in the XDS database. To run the UI’s, no code is generated beforehand – instead the model itself is executed in the XForm runtime. Server-side, the model translates to HTML/javascript, where use is made of both components from the Web Toolkit as well as components which come with XForms itself. An example of a component in XForms which makes use of a Web Toolkit component is the Tree component. From this, when exploring the tree functionality, it is useful to see both the XForms API documentation as well as the Web Toolkit API documentation. XForms are run with help of the formrunner host library.

XForms components can be found in <cordys instance>/webroot/shared/cas/xforms:

XForms Components Folder

UI components in Cordys do come as so called Type Libraries. They are dynamically loaded by Cordys, and can be attached to DOM objects. The low level API for this is the application.addType() method, enabling to add the library to the specified DOM object.

Add Type

Example: adding the upload component (by its fully qualified name) to a button with id ‘upload’ :

application.addType(upload, "wcp.library.util.Upload");

after which the methods from the upload type library are available on the upload button DOM object:

upload.browse(1);

The other way to attach a type library is by specifying the ‘cordysType’ attribute on the HTML element. For example for the web toolkit validate component:

<div cordysType=”wcp.library.util.Validate” id=”validateID”>
  <div>
     …
  </div>
</div>

What happens is upon runtime loading the html, when the DOM has been populated, the onload event is used by Cordys to traverse the DOM for elements with the ‘cordysType’ attribute and attach the corresponding Type Library. For this, it is using the application.initializeHTMLElements() method:

InitializeHTMLElements

In above screenshot, another Cordys feature wrt Type Libraries can be seen: one type library can inherit the functions from another type library.

Above is some low level background on Cordys UI components and libraries. Normally when doing XForms development, these low level details are abstracted away from the application developer. However, when it comes to developing your own components, this knowledge will be relevant to you.

Cordys wiki links:
https://wiki.cordys.com/display/bop43/The+Type+Library
https://wiki.cordys.com/display/bop43/Migrating+Libraries+to+Type+Libraries
https://wiki.cordys.com/display/bop43/Using+Web+Components+in+XForms

 

Control Development

To learn on development of Composite Controls, next resources can be used.

From help documentation:
https://wiki.cordys.com/display/bop43/Working+with+Composite+Controls

Additional examples on control development:
https://wiki.cordys.com/display/DUI/How+to+create+Composite+Control-Sample+Composite+Control
https://wiki.cordys.com/display/DUI/Build+a+Composite+Control+based+on+Fusion+Widget

In the runtime library, there are 3 important lines in top:

// Set the mycontrol type to public so the Cordys composite control framework will
// be able to add this library type to the runtime control object
setPublic(mycontrol, “com.ekemait.controls.runtime”);
// Inherit the base functionality from CompositeControl (like text translate)
importType(“cas.xforms.runtimelibrary.CompositeControl”);
inherit(mycontrol, CompositeControl);

Your library will be attached to the control as passed to attachType:

mycontrol.attachType = function ( control )

You can use this control object here as to add properties which you can refer to in your library methods via the this object:

control.numberOfColumns = control.getAttribute(“numberOfColumns”);

mycontrol.prototype.addOptions = function(optionCollection)
{
   var numberOfRows = Math.ceil(optionCollection.length / this.numberOfColumns);
   …

Use next lines in case you want to add the autoextent feature to your control programmatically:

control.ownerDocument.defaultView.WebForm.getLayoutElement(control).className += ” autoextent”;
control.ownerDocument.defaultView.WebForm.getLayoutElement(control).style.height = “auto”;

In case you have a composite control in which you want to dynamically include other XForms alike components (like input control) with equal behavior, what you can do is inspect the XForms generated HTML by DOM explorer, and reuse that HTML, adding the regular XForms behavior by:

this.ownerDocument.defaultView.WebForm._initializeHTMLElements(this.cblContentNode, this.ownerDocument.defaultView, false, false);

where this.cblContentNode is the top node in your control. As you can see, this is an underscore API (also used in the std xforms upload component); alternatively you can see if you can use the webtoolkit behavior; disadvantage though is the look and feel will be different.

To have your methods listed in the Intellisense feature in the XForms designer script editor, include a scriptlet in your designtime library similar as illustrated by next example:

<script xmlns=”http://www.w3.org/2002/xforms/cr” type=”cordys/xml” id=”_methodsDefinition”>
  <methods>
    <method name=”addOptions”>
      <parameter name=”optionCollection”/>
    </method>
  </methods>
</script>

 

Reload Library

When running a form which has a composite control, Cordys loads the runtime library. This can be seen in Web Task manager.

When changing and publishing the designtime/runtime library, the changes might not get effective immediately as Cordys might still have the old version loaded. To get your changes effective, either you need to refresh the browser, or use this provisional code:

unloadLibrary(“<design lib qualified name>”);
unloadLibrary(“<runtime lib qualified name>”);

function unloadLibrary(libraryURL)
{
   application.removeType(<control id>, libraryURL);
   system.containers[libraryURL].close(true);
   delete system.libraries[libraryURL];
   system.raiseEvent(“onunloadlibrary”, {libraryURL: libraryURL});
}

 

User Admin for Cordys

Sharing a utility to manage users in Cordys, enabling better overview and faster interaction. Features include:

  • create, clone, update, delete users
  • role/task/team assignments in one shot
  • multiple user management
  • import/export users
  • synchronize users from external directory (like Active Directory)

A 3-layered Java API is included (UI, domain, Cordys layers), hosted in WS-AppServer.

Google Code: https://code.google.com/p/cordysuseradmin
GitHub: https://github.com/kekema/cordysuseradmin

UserAdmin

 

WS-AppServer BusObject Mapping (3)

In last post, we had a look at mapping a database query result to a BusObject. This post lists you the WS-AppServer facilities which you can use for mapping data into a target BusObject, taken from a source XML or source BusObject/BusObjects.
Suppose you are getting data from an Enterprise Information System. The data structure has been fully prepared already as per the definition of the WS-AppServer class. In that case, you can directly set the object data. Taking forward the bankaccount example, but assume it’s coming from EIS now, your XML might look like this:

<AccountFull xmlns="http://schemas.cordys.com/TestDB">
  <AccountID>1</AccountID>
  <CustomerID>2</CustomerID>
  <BondType>1</BondType>
  <Dividend>2456</Dividend>
  <Customer>
    <CustomerID>2</CustomerID>
    <Name>Howard Ldt</Name>
    <Address>Hovard road 2412, AD</Address>
  </Customer>
  <RiskProfile>
    <RiskLevel>2</RiskLevel>
    <DateAccessed>2012-12-03</DateAccessed>
  </RiskProfile>
</AccountFull>

Take care of the namespace!

Setting the object data of a fresh object can now be done like this:

No event handlers will be fired, which is most likely also not needed.  Memory cleanup responsibility for eisXmlHandle is still with the caller (see API documentation).

In most cases, the XML as comes from the EIS won’t fit as per the WS-AppServer class definition. In that case, first an XSLT transformation can be applied with help from an XSLT in XML Store:

Now suppose you have an AccountSummary class. You want to map the data from an existing AccountFull busObject, for the bold elements:

<AccountFull xmlns="http://schemas.cordys.com/TestDB">
  <AccountID>1</AccountID>
  <CustomerID>2</CustomerID>
  <BondType>1</BondType>
  <Dividend>2456</Dividend>
  <Customer>
    <CustomerID>2</CustomerID>
    <Name>Howard Ldt</Name>
    <Address>Hovard road 2412, AD</Address>
  </Customer>
  <RiskProfile>
    <RiskLevel>2</RiskLevel>
    <DateAccessed>2012-12-03</DateAccessed>
  </RiskProfile>
</AccountFull>

This can be done in 2 steps. First we map the needed attributes from the top level using sys_mapAttributes. Subsequently, we map the inner Customer structure using sys_mapAttributesEx:

In the second step, effectively an inner object gets established.

In case you want to map the data the other way back, use sys_mapAttributesReverse, using same mapping array:

Now suppose, you want to map an XML to an inner busobject using setObjectData(). Or you want to map a subset of attributes from a source object to the inner object. In that case, we will first need to create the inner object (upon creating a new busobject, initially no attributes and no inner objects will be there).

Subsequently you can map into the customer target object.

Last option I want to show you is applicable for a situation in which you can map a whole structure into the target, but maybe just one substructure is not applicable. In that case, you can use the ‘sys_dropPropertyXML’ method. Suppose we want to eliminate the RiskProfile from an AccountFull object:

By supplying ‘true’, the NOM memory gets garbage collected for this substructure.

And that concludes the 3-part series on BusObject Mapping. By this you should have a good toolbox of facilities to manage your WS-AppServer UI, business and data objects. I’m leaving you with a link to the CROM Framework as found on Cordys wiki, which also touches on the topic of ORM mapping, and gives support for Order-OrderLine-Item type of patterns.

WS-AppServer BusObject Mapping (2)

This post shows you about mapping between DB and BusObjects. Suppose we have next 2 tables in our DB:

Upper case is used as per Oracle convention, and the tables names do have a prefix to indicate the application the tables belong to (customer practice). For our WS-AppServer generated standard classes, we can provide the mapped names, both on class and attribute level, achieving camel case and skipping the ‘CIM’ prefix:

Result is, our Java classes and WSDL have the naming as camel case, and getting a customer gives next result:

<tuple>
 <old>
  <Customer>
   <CustomerID>1</CustomerID>
   <Name>Coleman Ldt</Name>
   <Address>Antony drive 122, NY</Address>
  </Customer>
 </old>
</tuple>

Let’s see how this is achieved in Java. First, we execute a plain query and get the result which is by default an AnonymousBusObject.

Result:

<CIM_CUSTOMER>
 <CUSTOMER_ID>1</CUSTOMER_ID>
 <NAME>Coleman Ldt</NAME>
 <ADDRESS>Antory drive 122, NY</ADDRESS>
</CIM_CUSTOMER>

Now, we use the setResultClass() method:

Because of this, not only will the query.getObject() now return an object of class Customer, but also it will know now from the ClassInfo on how to do the mapping. In CustomerBase.java:

In case you want to have full control yourself, you can set a so called mapping strategy by utilizing the customizeClassInfo() event handler:

To set the mapping strategy, supply an instance of a class which implements the ImapClassStrategy interface. There are 3 standard mapping strategies with corresponding classes:

  • TrivialStrategy: enables table-class name mapping
  • NameMappingStrategy: for table-class and column-attribute mapping with help of a mapping array
  • DefaultStrategy: maps both class-table as well as attributes-columns. Supply an instance of a class which implements the ImapAttributeStrategy interface (by which you can map both class-table as well as attributes-columns), or don’t supply any instance and let WS-AppServer fall back on DefaultAttributeStrategy (for attributes) and in case no class-table mapping is known, WsApps falls back on the TrivialStrategy for this.

Setting the mapping strategy by customizing the class info can be applied for standard and inherited classes, and will take care of mapping for both getting data as well as updating (reverse mapping).

Now instead of a standard class, we have a custom class with next layout (join of BankAccount and Customer):

<AccountFull>
 <AccountID unique="true">i8</AccountID>
 <CustomerID>i8</CustomerID>
 <CustomerName>string</CustomerName>
 <CustomerAddress>string</CustomerAddress>
 <BondType>i4</BondType>
 <Dividend>r4</Dividend>
</AccountFull>

For custom classes, we can only specify the class and attribute names in the repository. The mapping can be achieved by using the setResultMapping() method:

Attribute mapping can effectively also be achieved by specifying the individual columns in the select-clause, and using the AS-clause (select CIM_BANKACCOUNT.ACCOUNT_ID as AccountID, etc). Notice though, only upon using setResultMapping(), WS-AppServer will also map the class name (query.setResultMapping(QueryObject.MAP_CUSTOM, null)).

In case you only need to map a subset, leaving the other attributes unchanged, you can use “*”:

This only works for this method setResultMapping().

To conclude for now, let’s explore the nested scenario:

<AccountFull>
 <AccountID>1</AccountID>
 <CustomerID>2</CustomerID>
 <BondType>1</BondType>
 <Dividend>2456</Dividend>
 <Customer>
   <CustomerID>2</CustomerID>
   <Name>Howard Ldt</Name>
   <Address>Hovard road 2412, AD</Address>
 </Customer>
</AccountFull>

which can be achieved like this:

In next post we will see some more advanced mapping scenario’s.

WS-AppServer BusObject Mapping (1)

Suppose you are involved in a Cordys project with a good amount of detailed business logic to be developed in WS-AppServer, UI development, and data storage in some RDBMS and/or External Information System. You will need to come up with the database model (relational modeling), the domain model (OO modeling), UI’s, etc, typically resulting in 3 main categories of WS-AppServer classes. Let’s take a next perspective, and depict a layered organization of these classes as per class instances. At the same time, lets include quite a number of possible combinations on how the data for a certain class instance is collected with help of other classes or RDBMS/EIS.

Not every project will need this much layers and combination of classes, however the above picture will illustrate you the subject of this blog post. It gives a runtime view; the design view with inheritance, association and composition is quite a different view, and not relevant here.

Class instances in WS-AppServer are so called BusObjects. BusObjects which directly map to a database table are called StateBusObjects; transient instances are called CustomBusObjects. A CustomBusObject which has no corresponding class definition in the WS-AppServer Repository is called an AnonymousBusObject.

Notice a BusObject in the Business layer or UI layer possibly can be directly derived from the data in the database in case the structure happens to be 1 to 1.

As the Cordys platform is very much XML-based, this approach is also visible in WS-AppServer, where the object data inside the BusObject is represented as an XML document. Apart from the fact this integrates well into the Cordys ESB, it also gives advantages in the subject of BusObject mapping, as mapping on the XML layer handles more smoothly as compared to mapping  with help of native object properties.

Mappings can be bi-directional: one direction to collect the data and the other direction to update the data. Next are scenario’s of BusObject mapping:

  • When querying a record from the DB, the XQY layer returns an XML data structure. It needs to be mapped to the structure of the WS-AppServer class definition. Possible situations:
    • 1 to 1 mapping
    • The class name is different from the table name, and so to be mapped.
    • One or more attributes do have names different from the column names.
    • Table/column names are in upper case, whereas the class definition uses camel case, and so to be mapped.
    • The resulting structure from the DB query is nested, but the class definition is flat.
    • The class definition only has a subset of data elements as compared to the DB query result.

  • A domain object like a Claim needs to have its data collected and mapped from claim business data in EIS and claim process data in the RDBMS. Subsequently, several UI objects needs to get there data from the Claim object, where those UI objects can have different structures as per the user role perspective (claim handler, approver, financial controller, fraud investigator, etc), or as per the claim process step in the BPM.
    Notice the ‘intermediate’ role of the Claim domain class here as between the backend structures and the UI structures.

 

 

In the next 2 articles, we will subsequently dive into more details wrt these 2 scenario’s as to see what facilities WS-AppServer offer us to support these mappings.

Cordys WS-AppServer

In Cordys projects, I have 2 ‘secret’ weapons. The first one is to optimally leverage all the Cordys direct and inherent constructs, features and infrastructure to resolve the business problem, and to exploit its model-driven architecture to the max. This way, you outperform conventional development, and inherit other (future) Cordys functionality and features on top. It sounds obvious, but it is not so easy.

The second weapon: WS-AppServer. Rather than just having a small data access layer and resolving a lot in the UI layer client side, WS-AppServer development brings you a strong object model hosted in a robust environment using a strongly-typed language, and is well integrated with XForms, rules-engine, etc. WS-AppServer enables to establish a data access layer, a business layer (domain objects) and a UI layer for server-side UI support. WS-AppServer is an ORM-tool as it supports Object-Relational mapping, but it is more than that: it gives full support to developing Business Objects/Web Services, running in a framework and exposing an event-model to bring in custom constraint validation, access control, etc. On top, it enables you to establish excellent server-side support for UI development. The WS-AppServer – XForms integration builds on the concept of Business Object synchronization (or ‘Mobile’ objects), in which either side can enrich the (UI) Business Object, and synchronize it back to the other side.

Together with its repository-driven and XML-based approach, WS-AppServer is a unique solution in the domain of ORM tooling and Business Logic frameworks, and this counter-balances the fact it’s a native Cordys solution. In the future to come though, I expect Cordys will come up with support for general known programming models. Also an area which is open is the availability of a graphical entity modeling tool – in the current BOP 4.1, this is not available.

In this blog, I’m planning a number of articles as to dive into some of the details of WS-AppServer, bringing you concepts and features which are not that explicit or extensive described in the official documentation, enabling you to make optimal use of this application server. I’ll start off on the subject of ‘BusObject Mapping’. Stay tuned!

 

Gantt Chart in Cordys

To some extent, the Cordys platform offers a direct support for charting (single/multi-series, gauge, BAM charts). Your project though might require extended support/other types of graphs. For example, the requirement might come up to depict the timeline of a BPM/Case process into a Gantt Chart.

Example:

Claim Timeline

The Cordys platform comes with a version of Fusion Charts (<Install>\Web\wcp\flash\fusion). One of the Fusion charts is the Gantt Chart widget. It is quite an effort though to produce the low-level XML to make up a chart. To ease things, I’ve uploaded a Cordys project which offers a high-level API to produce a Gantt Chart.

Google Code: http://code.google.com/p/cordysganttchart/
GitHub: https://github.com/kekema/cordysganttchart/

It comes as a server-side component (Java API), and has an example class and XForm to illustrate on how to request for the chart from server and populate it client side. Quickly you can build up a chart by adding a calendar with timescales/non-working days, columns, rows and row items (tasks, milestones, events). See for details the project documentation.

Introduction

Hi! Welcome to my blog. My name is Karel Ekema, working as a freelance Enterprise Application Developer. My focus is on Case Management, Business Process Management and Data-centric Applications, applying Cordys BOP + Java or Metastorm + .NET. I’m internationally oriented, working on the edge between business and IT in high-end enterprise IT environments. In particular, I’m also interested in applying Business Logic Frameworks and ORM tools like Cordys WS-Apps, Microsoft Entity Framework and the CSLA framework.

What will you find in this blog? If you are interested in the areas which  I mentioned, or you are developing in Cordys or Metastorm, pay a visit from time to time as I try to publish some interesting articles for you. It can be conceptual stuff like Case Management backgrounds, Cordys/Metastorm development articles, articles on Enterprise Application Development at large, working in cultural diverse settings, etc. etc. I just finished off a small open source widget for using Gantt charts in Cordys, so I’ll kick off on that and make it the first real article. Hope you enjoy reading this blog, and let me know your comments!