Custom SmartObject Service Brokers
You can think of K2 SmartObjects as an abstracted data access layer which exposes data and services from Providers (such as SQL databases, SharePoint lists and so on) to applications (such as Reports, Workflows and User Interfaces). SmartObjects are essentially a “middle layer” between sources of data and applications that require the data. K2 provides a number of standard "connectors" (known as "Service Brokers") that can expose common LOB systems (such as SQL databases, Oracle Databases, Active Directory, SharePoint, Microsoft CRM and many more) as SmartObjects, However, it is certainly possible to extend the standard set of brokers by writing custom Service Brokers that integrate with other LOB systems. SmartObjects are the preferred mechanism to integrate with LOB systems and therefore it is quite common to write custom Service Brokers to connect to other systems not supported by K2 out-of-the-box.
SmartObjects act as a translator (or “adapter”) which takes the complex "language" of the LOB system and converts the data and methods into an easy-to-understand, consistent interface that the consumers can understand. Internally, this is achieved through the use of system-specific "connectors" called Service Brokers and exposing those as Service Objects, which can be used to assemble SmartObjects that in turn are used in Form, Workflows and Reports.
SmartObjects are executed by the K2 Server. Essentially, the K2 server acts as the "engine" that will query the Provider through the Service Broker when a SmartObject is executed. While you can certainly use or "call" SmartObjects in many consuming applications, it is not the application itself that does the actual processing to retrieve the data from the underlying system: that task is performance by the K2 server. The K2 server does not cache SmartObject data. This means that K2 will always retrieve the latest data from the Provider whenever the SmartObject is executed.
Service Brokers are implemented by creating a .NET class library which references SourceCode.SmartObjects.Services.ServiceSDK.dll and then inherits and extends the SourceCode.SmartObjects.Services.ServiceSDK.ServiceAssemblyBase base class. The class library would implement all the necessary logic to expose the Provider's Objects as Service Objects and handle all interaction with the Provider. Once the assembly is coded and built, you would copy the resulting .dll file to each of the physical K2 servers in your environment, and then use K2 Management to register the Broker with the K2 environment and create Service Instance(s) for the Broker. For more information see the Service Types topic in the K2 User Guide.
There are two main approaches to creating Service Brokers: Static or Dynamic Static (Described) Service Brokers
Static (Described) Service Brokers are used when the underlying Schema of the provider does not change frequently. Think of a Static Broker as something that describes the Service Objects, because it always knows what the Provider's schema looks like. Here is an example: suppose you have a class library in your organization that exposes Time Zone information. This TimeZone object has properties like Name, UTCOffset and Abbreviation and methods like Read TimeZone and List TimeZones. We don’t expect that the definition or Schema of the TimeZone object will ever change. Now imagine that we wanted to create a Service Broker that exposes this custom TimeZone object so that we can create SmartObjects for TimeZones. Since the Schema of our TimeZone object never changes, we can create a static Service Broker which describes the TimeZone as a Service Object. Essentially, the description of the Service Object is hard-coded into the Service Broker code.
Dynamic (Discovered) Service Brokers are used when we want to create a re-usable Broker that can expose different instances of the same technology. They are Dynamic because the broker will discover the Schema of the provider when a Service Instance of the Broker is registered or refreshed. Here is an example: suppose you wanted to create a Service Broker that can interact with MySQL databases. The Broker will always interact with the same technology (MySQL), but we cannot predict what the underlying Schema of a particular instance of a MySQL database will look like. You therefore need to create a Dynamic Service Broker that will query a particular instance of a MySQL database when a Service Instance is registered or refreshed. The discovery process will use some kind of query to determine the various tables, views and procedures available in that MySQL database, and then describe these in Service Object terms.
Before creating custom Service Brokers, it is very important that you understand the terms that are used in the SmartObject architecture, how the components work together and the role of each component.
The diagram below illustrates the conceptual architecture of SmartObjects. Pay special attention to the red, italic text: these are the terms you need to understand. We describe each term in more detail following the diagram.
Let’s take our sample diagram and break down the SmartObjects components:
Service Broker
A Service Broker is a .dll file which contains all the code necessary to interact with a specific provider. Typically, this assembly would reference an API or Service exposed by the provider, and it would contain code that discovers and/or describes that provider's objects to K2 in a consistent way. In some circumstances, the Service Broker may also implement logic to handle the security that is required by the provider's API. Essentially, the Service Broker's task is to "translate" the provider's native objects into Service Objects (and vice-versa), and to call the various methods in the Provider's API at runtime.
Since the Service Broker contains all the code necessary to interact with a provider, this is the only item you need to implement when you want to expose some other system as K2 SmartObjects. As long as the broker implements the necessary behavior required by the ServiceAssemblyBase base class from the Service Broker SDK, it will be usable in all the other layers (Service Type, Service Instance and Service objects)
You can have as many Service Brokers in a K2 environment as required, but note that the physical .dll file (and any dependencies that do not already exist on the target K2 server) MUST be copied to each physical K2 server in your environment.
Service Brokers are normally written to expose a particular technology. In our example diagram, we have two different Service Brokers: the SQL Broker.dll contains all the logic necessary to interact with Microsoft SQL databases, while the AD Broker.dll knows how to interact with Microsoft Active Directory APIs.
Service Broker Dll's reside in the "%PROGRAMFILES%\K2\\ServiceBroker" directory on the K2 Application servers. The screenshot below shows the location of the Brokers
Service Type
The only purpose of a Service Type is to register a Service Broker .dll file in a K2 environment so that it can be used to register Service Instances. K2 Administrators would use the K2 Management site to register a Service Broker .dll with a K2 environment. Normally, each Service Broker .dll would only have one associated Service Type entry.
Note that it is only necessary to register a Broker .dll once with each logical K2 environment, Since the Service Type registration is saved in the K2 database, all other physical K2 servers that reference the same database will automatically know about the Service Broker. You must register the Service Type with a K2 environment before you can start adding Service Instances of that type.
The screenshot below shows a typical list of Service Types in a K2 environment, in this case we used the SmartObject Service Tester Utility to explore the available Service Types.
Service Instance
Once a Service Broker is registered as a Service Type, K2 Administrators can create one or more Service Instances for that Service Type. A Service Instance is essentially a configured instance of a broker, and contains various configuration values that are required by the Broker. The configuration values could include values that the Broker needs to connect to the provider (for example, a server name or URL) and perhaps additional configuration values that describe how the Broker should behave. Another important setting for a Service Type is the Authentication Mode setting: this determines the user credentials that are passed to the underlying Service Broker.
In the diagram, notice that there are two Service Instances for the SQL Service Type. Each Service Instance contains the configuration values necessary for the broker to find and communicate with the HR and Finance SQL databases, respectively.
The screenshot below shows Services Instances and a Service Instance Configuration, in this case we used the SmartObject Service Tester Utility to explore the available Service Instances.
When a Service Instance is registered or configured, the following actions are taken:
- An appropriate name and description is provided for the Service Instance.
- The Instance is assigned a GUID identifier
- Appropriate service configuration is provided, such as server name, connection details, and security details.
- The Service Broker discovers or describes the underlying provider’s schema and describes the Schema as Service Objects to the K2 platform.
Service Object
Service Objects are essentially "translated" representations of the entities in the underlying Provider. Under the covers, Service Objects are just XML representations of the entities in the provider and contain no processing logic.
Service Objects expose the properties and methods for objects in a Provider as consistent SmartObject Method Types and Property Types; it is the responsibility of the Service Broker to perform the necessary mapping between the Provider's types and the types used by SmartObjects.
Service Objects are always tied to a Service Instance, because the Service Instance exposes the Schema that describes the Service Object. Note that Service Objects are created automatically by a Service Broker and cannot be modified manually. In addition, Service Objects can only be consumed by SmartObjects: designers must create SmartObjects that are tied to one or more Service Objects before the service can be consumed in a workflow, report or user interface.
A Service Instance would usually contain one or more Service Objects. As part of the translation, the Service Broker will convert the objects in the provider to Service Objects; the properties of the objects are converted to Service Object Properties and the methods in the provider's objects are converted to Service Object Methods. A large part of implementing a customer Service Broker is to write the code that will perform this "translation" to represent the underlying system's artifacts as Service Objects.
Consider the example screenshot below. Here we have a Service Object called [Denallix].[Assets] which represents the Assets table in the Denallix database. The Service Broker converted the columns in the SQL database table to Properties, and generated Service Object Methods for the typical CRUD operations that may be performed against a SQL database table. At runtime, the Service Broker will convert the service object methods into equivalent statements that can be executed on a SQL server, and will return information from the SQL server in a format that can be consumed by the Service Objects. Hence the description of a service broker as an ‘adapter’ or a ‘translator’: it translates Service object methods and properties into statements that are used to interact with the underlying system’s APIs.
SmartObject
The final step to expose a provider to consumers is to create SmartObjects which reference one or more Service Objects. This task can be performed by K2 Administrators or K2 Designers/Developers using the Service Object Tester tool or K2 Designer. Eventually, these SmartObjects are used in the consumers, and you will note that the consumer does not need to know anything about the underlying provider to use the SmartObject.