SDK reference

The complete contract between a connector and the platform.

The Connector SDK turns any Python client library into a Zato connection type - an internal library your company maintains, a vendor's package or a client for a protocol used only by your own systems. You wrap the library once and services use it like any built-in type.

Use the Connector SDK to:

  • Manage connections of your own types centrally, by name
  • Store secrets encrypted, omitted from listings
  • Access connections in services through self.out, like every built-in type
  • Deploy connectors as regular Python modules, through hot-deployment and without a server restart

The contract

A connector is a subclass of Connector from zato.common.sdk. You give it a type name, declare its configuration fields and implement two methods - everything else is optional.

# -*- coding: utf-8 -*-

# Zato
from zato.common.sdk import Connector, Field

class MyConnector(Connector):

    # Services access connections of this type as self.out.my_type
    type = 'my_type'

    # Configuration schema
    host = Field.Text()
    port = Field.Int(default=9950)
    api_key = Field.Secret()

    def create_client(self):
        # Build and return the underlying client object
        ...

    def ping(self, client):
        # Confirm the connection works, raise an exception if it does not
        ...

Configuration fields

Fields are class attributes declared with Field types. Their values are stored with each connection definition and delivered to the connector, already resolved, through self.config.

Field typeDescription
Field.TextA text field
Field.IntAn integer field
Field.BoolA boolean field
Field.SecretA secret, stored encrypted and omitted when definitions are read

Each field can declare a default, for example Field.Int(default=9950), which applies when a definition does not provide the value.

Methods the platform calls

MethodRequiredDescription
create_clientYesBuilds and returns the underlying client object, called when the connection starts
pingYesConfirms the connection is usable, called when the connection is pinged
on_stopNoCloses the client, called when the definition is deleted or edited
validateNoChecks the client is still usable before each use. The default calls ping and an exception makes the platform evict the client and reconnect
refresh_credentialsNoRenews expired credentials. It runs when an invocation raises CredentialsExpired and the invocation is then retried once
start_process-Provided by the platform. Starts and supervises a helper process

Invocation methods are plain methods you add for each operation the remote system supports - get_customer, send or query. Services call them directly and the platform wraps each call with the behavior described below.

Invocation behavior

When a service calls an invocation method, the platform wraps the call.

A watchdog timeout caps how long the call may take. The definition's timeout field sets the cap and a single call can override it: conn.get_customer('C1', timeout=5).

validate runs before the call and, when it raises an exception, the platform evicts the client and rebuilds it before the call proceeds. Pooled connections validate at checkout instead.

An invocation that raises CredentialsExpired triggers refresh_credentials and is retried once.

An invocation that raises ConnectionLost receives the exception, and the platform rebuilds the connection in the background, with backoff.

Exceptions

zato.common.sdk defines two exceptions that client code raises to report a lost connection or expired credentials:

ExceptionMeaning and what the platform does
ConnectionLostThe connection is down. The platform evicts the client and rebuilds it with backoff
CredentialsExpiredThe credentials expired. refresh_credentials runs and the call is retried once

Pooled connections

When connections hold per-connection state - a logon handshake followed by calls that run one at a time - subclass PooledConnector instead of Connector. The platform owns a pool of connections, create_client builds one connection each time the pool grows, and invocation methods borrow one per call through get_connection:

# -*- coding: utf-8 -*-

# Zato
from zato.common.sdk import Field, PooledConnector

class MyConnector(PooledConnector):

    type = 'my_type'

    def create_client(self):
        # Build and return one connection - the platform calls this each time the pool grows
        ...

    def send_command(self, command):
        # Borrow a connection for the duration of the with block
        with self.get_connection() as conn:
            return conn.send(command)

PooledConnector adds these to the contract:

MethodRequiredDescription
get_connection-Provided by the platform. Borrows a connection for the duration of a with block
on_get_from_poolNoResets conversational state. It runs each time a call borrows a connection
on_return_to_poolNoCleans up. It runs each time a with block using get_connection ends

A definition's pool_size field caps how many connections its pool holds and connections are built lazily, as the load requires them. To see a complete connector of this kind, see handshake protocols.

Subscribing connections

When the remote side pushes messages - market data feeds, event streams - subclass SubscribingConnector. It adds one hook to the contract:

MethodRequiredDescription
on_startedNoSubscribes and replays state. It runs when the connection first starts and again after every reconnect

The platform watches subscribing connections in the background. When one goes down, the platform reconnects with backoff and re-runs on_started, which re-establishes the subscription. To see a complete connector of this kind, see server push.

Helper processes

start_process runs a component next to the server - a Java jar, a .NET assembly, a native binary or a Python module in an interpreter of its own:

def create_client(self):
    process = self.start_process(['java', '-jar', self.config.jar_path, '{port}'])
    return MyClient('127.0.0.1', process.port)

Any {port} placeholder in the command is replaced with a local port allocated for the process. The returned Process object has these members:

MemberDescription
pidThe process ID
portThe local port allocated for the process
is_runningWhether the process is still running
stopStops the process

The platform supervises the process. When the process exits unexpectedly, the platform rebuilds the whole connection with backoff, which re-runs create_client, and the process stops together with the connection it belongs to. To see a complete connector of this kind, see foreign runtimes.

Ambient attributes

Every connector instance has these attributes available:

AttributeDescription
self.nameThe name of the connection definition this instance serves
self.configThe resolved values of the fields the class declares
self.clientThe object create_client returned, once the connection has started
self.loggerA logger writing to Zato server logs
self.invokeInvokes a service with a message, matching self.invoke in services
self.publishPublishes a message to a pub/sub topic, matching self.publish in services

Per-tenant credentials

When one definition serves many tenants, each with credentials of their own, services resolve them at runtime with with_config - the platform keeps one client per distinct set of overrides and stops the ones that stay idle beyond the idle period:

conn = self.out.crm['My CRM'].with_config(api_key=tenant_api_key)
response = conn.get_customer(customer_id)

Each distinct set of overrides gets a client of its own, built through create_client with the overridden values in self.config, and repeated calls with the same overrides reuse it.

Runtime behavior

When your module is deployed, the platform finds the Connector subclasses in it and registers each one as a new connection type. From that moment:

  • Definitions of the type can be created, edited, deleted and pinged like any other connection
  • Each definition is served by one connector instance with one client, shared by all the services on a server
  • Editing a definition stops the old client through on_stop and builds a new one with the new configuration
  • Redeploying the module updates the type in place and the definitions that are already running keep working
  • After a server restart, definitions start automatically as soon as the module is deployed again at boot

See also

PageWhat it covers
Connector SDK tutorialBuild a complete connector and call it from a service
Handshake protocols and pooled connectionsA complete PooledConnector, from logon to service
Server push and subscriptionsA complete SubscribingConnector with resubscription after reconnects
Java, .NET and native codeA complete connector built around start_process