CLIX Component Framework

 

CLIX is a Python plugin framework. "Clix" is a pun on the word 'clique' which means an association or a gathering. In this case, CLIX gather components and or classes into the same executable space, and creates a databus for them to use in communicating with one another. CLIX is not (by itself) a strategy for building loosely coupled systems, although it may participate in such systems. It is a framework for plugging in components and objects into the same namespace conveniently and in a standard way.

 

CLIX Lifecycle

When an instance of CLIX starts up via:

 

Python clixManager.py

 

it builds a databus and loads all the plugins you add to a folder called clixlets that you position relative to the startup folder for CLIX's switchboard and plugin manager located in clixManager.py. Once loaded, CLIXLETS communicate with all other CLIXLETS in the same namespace via input and output queues. A CLIXLET may have both input and output queues, or only one of the other (for example, a GUI CLIXLET might only have an output queue if it is only issuing commands to other CLIXLETS). After loading CLIXLETS, it then acts as a switchboard to route messages between CLIXLETS. Currently, a shutdown message addressed to the ClixManager itself will shut down the system.

 

CLIX Messages

CLIXLET messages must contain a sender's name, a recipient's name, and a payload. Optionally, they may contain a type, which should be either CONVERSATION, PRESENCE, or INFOQUERY. CONVERSATION messages normally carry argument or result payloads between CLIXLETS. PRESENCE messages may advertise the availability of a service to a peer in the same namespace. INFOQUERY messages generally are directed to the ClixManager itself. If a queue is included in a message, the message is automatically enqueued on the given queue. On the other hand, you may put the message on a particular queue yourself. Generally (and for convenience and simplicity sake,) all messages can be routed through the ClixManager acting as a switchboard, but nothing prevents an arbitrary pair of conversing CLIXLETS from exchanging their queues (as messages) and conversing directly. It is left as an exercise for the implementer to parse message payloads into expected arguments and package them into results

Here are some examples:

 

self.managerQ.put( ClixMessage(sender=ME,receiver=self.sender, payload="http:''slashdot.org") ) # CONVERSATION Message
ClixMessage(sender=ME, receiver='ClixManager', queue=self.managerQ , payload="thisClixlet IN SERVICE", type=INFOQUERY)  # INFOQUERY for the manager

 

Creating CLIXLETS

 

What motivates CLIX is an interest in having a common and well-defined component management framework.

For example, let's say that you have a simple module such as this:
 

 

# adder.py
def add(a,b):
           return a+b

 

One way to use this defined function might be simply to import it into the context of some python program:

 

# tester.py
from adder import *
x = add(31,11)
z = add('foo','bar')

 

Suppose however you have an arbitrarily large set of components (methods and classes) you want to integrate, and furthermore:

 

In this case, CLIX provides a simple but elegant solution, with relatively low overhead.

 

What's needed to extend an ordinary function into a CLIXLET is for it to 1). add a method called registerClixlet() so that the module can join into the clique, becoming a CLIXLET, 2).  converse through queues, and 3). deal with the incoming and outgoing payloads  So our trivial example module now does becomes this:

 

 

   1:from clixMessage import ClixMessage
   2:from clixMessageTypes import *
   3:import Queue
   4:import os
   5:def add():
   6:    while True:
   7:        msg = gInputQueue.get()
   8:        args = msg.getPayload()
   9:        result = args[0] + args[1]
  10:        gClixManagerQueue.put(ClixMessage(sender=gClixletRegistry['name'],receiver=msg.getSender(), payload=result))
  11:def registerClixlet(clixletRegistry=None, clixManagerQueue=None):
  12:        global gClixManagerQueue
  13:        global gInputQueue
  14:        global gClixletRegistry
  15:        gClixManagerQueue = clixManagerQueue
  16:        clixletRegistry['name'] = "adder"
  17:        clixletRegistry['function'] = add
  18:        clixletRegistry['params'] = ()
  19:        
  20:        # if this clixlet is going to talk across the databus,  then give it input and possibly output queues:
  21:        gInputQueue = Queue.Queue()
  22:        clixletRegistry['inputQueue'] = gInputQueue     
  23:        clixletRegistry['outputQueue'] = gClixManagerQueue
  24:        gClixletRegistry = clixletRegistry.copy()
 

 

This may seem like a bit of overhead for such a trivial function; bear in mind that it is arbitrarily trivial for the sake of illustration. Real applications (we would hope) are rather more complex.