In the past few months I developed the prototype of a role system for Java agents that I called WhiteCat, in contrast to
RoleX (also known as BlackCat).
The prototype is developed in 100% Java and is based, as its precedessor, on the Javassist bytecode manipulation library. The idea is the same as in RoleX: Java agents should be enhanced thru roles (i.e., Java third party libraries) dynamically at run-time, and must be merged with the role they assume, becoming a single entity with their roles. The aim is thus to support role external visibility, that is the capability to recognize a role played by an agent simply looking at it. Since in Java "looking" at an object and retrieving its class information, the WhiteCat approach must manipulate the agent class structure in order to make the role visible as a part of the agent itself.
For a more detailed explaination of the inspiration behind this role approach please see
this article on the IBM Systems Journal.
There are a few drawbacks in the RoleX design that lead me to the design of the WhiteCat approach:
- RoleX merges the role in the agent changing the whole agent class structure. This means that not only the bottom of the agent class inheritance chain is modified, but that each level of the agent inheritance is changed depending on the role class structure. This is a time consuming activity.
- RoleX requires a different class loader for each role assumption/release. This means that if the same agent assumes different roles at different times, each assumption requires a different class loader (called role loader). This is resource consuming.
- RoleX requires developers to deal with reflection and invocation translators to make injected features (i.e., role features) available. This is complex to understand and to develop/mantain.
- RoleX provides only one kind of role, that is a publicly visible role. Each assumed role is automatically visible to external entities, and this could lead to security and privacy problems.
- RoleX injects the role into the agent class, without taking into account that the agent could be hidden behind a proxy (see for example Aglets), and thus the role external visibility is not exploitable.
- RoleX requires the role to be not instantiated and used by anything that is not the role loader. In other words you are not free to create a role object, since the role engine will workonly with the role classes, and thus having a role object will not provide you any meaningful capability.
- RoleX creates different class definitions at run-time, so you could have two classes with the same name and different implementation. It can thus be difficult to deal with objects, since you could not knowor recognize which one is the original (i.e., never manipulated) one. Moreover, this way of working requires that developers discard the old (i.e., not manipulated) agent reference and use the new one provided by the role engine. Of course, there is no way to enforce this practice, so a malicious developer could keep a reference to both.
Starting from the above drawbacks I decide to design a new approach with the same base concepts, but faster, simpler and with more role visibility options. The result is the WhiteCat prototype that provides three level of role visibility:
- invisible: the role is not visible at all from an external point of view. This means that an agent has assumed and uses a role without letting any other agent to be able to autonomously knowing it;
- visible: the role is visible to any external entity;
- public: the role is visible and can even be used from external entities.
Role visibility is defined thru interfaces and annotations, so that the role loader can recognize how and where to apply changes to the agent (or its proxy).
Moreover WhiteCat allows the manipulation of either the agent class structure or its proxy one. In the case the agent is hidden behind a proxy, and the role is visible or public, the proxy class structure is changed in order to make the role perceivable; in the case the role is directly accessible (i.e., there's no proxy) its class structure is directly changed in order to apply changes.
As an example consider the following application snippet:
// role class
Class roleClass = root.loadClass("whitecat.example.DatabaseAdministrator");
DatabaseAdministrator dbaRole = (DatabaseAdministrator) roleClass.newInstance();
AgentProxy proxy = .... // get the proxy in the way your platform allows you
// get a new role loader engine
RoleEngine engine = new RoleEngine();
// add the dbarole to the current agent
AgentProxy newproxy = engine.injectPublicRole(proxy, dbaRole);
if( newproxy instanceof IDatabaseAdministrator ) {
// the proxy can work as a IDatabaseAdministrator object from here!
}
As you can see the agent proxy is simply injected with the role. Please note that a role instance is used, not a role class. This means that the role can be instantiated and initialized as you like, and can injected to the agent or its proxy when you want, and of course the role will keep its state.
Just after the injectPublicRole() call, the agent proxy is perceivable (thru instanceof) as implementing the public role interface, that in the above example is the IDatabaseAdministrator one. In order to achieve this, the agent proxy class structure is extended by the role one, meaning that it is like the agent proxy has new subclasses with the role properties. Since the agent proxy class structure is extended (i.e., all the classes up to the agent proxy are not touched), there's no need to discard the "old" object reference. If the developer does not use the new proxy reference (i.e., newproxy in the code above), it will simply not be able to exploit role capabilities, but he will be able to exploit all
original proxy capabilities.
The RoleEngine is a special class loader that performs bytecode manipulation in order to extend the agent proxy class chain. The RoleEngine exploits the Javassist library in order to manipulate the class structure, and appends
to each new class a unique number, so that the same class can be manipulated different times by the same RoleLoader as the result will have a different name and so will not clashes with other already manipulated classes. Moreover the role adoption model is simpler than in RoleX, since the developers will handle roles in straight way: once the injectPublicRole() is called, the developer could
test the agent proxy against the injected role and cast the agent proxy to such role. Last but not least, since WhiteCat does not manipulate a whole inheritance chain, rather it appends a chain to another, the approach is much faster than the RoleX one.
Of course, as shown in the figure, the manipulation process can be repeated as many times as needed (i.e., one for every role assumption).
In my opinion WhiteCat represents a very enhanced role approach than RoleX, and even if still a prototype, it is enough stable to be used in role based applications.
I'm still working on WhiteCat, and I will release it under the GNU GPLv3 license. If you are interested in WhiteCat and want to know more details or discuss with me, please e-mail me (fluca1978 at gmail.com) and I will be happy to provide more information.
I'd like to thanks Mr.
Shigeru Chiba and the
JBoss team and community for their work and support on
Javassist.