.NET: THE PROGRAMER'S PERSPECTIVE: ECOOP WORKSHOP 2003

Previous article

Next article


Dynamic component composition in .NET

Anis Charfi, David Emsellem, Michel Riveill, Laboratoire I3S, Sophia Antipolis CEDEX, France

 

 

PDF Icon
PDF Version

Abstract

Components have brought with them the notion of services which let the programmer concentrate on the business behavior of his component while the non functional part (i.e. the services) is the responsibility of the platform provider. Thus services are not reusable throughout the different platforms; the mechanism used to integrate them in the component is totally platform dependant. In this paper we propose a model to define the integration of services and describe its implementation in the .NET framework. We also discuss the facilities offered by the .NET platform in comparison with the first implementation of this model which was in Java.


1 INTRODUCTION

Nowadays, adding and composing non-functional requirements at deployment time or at runtime have become a ubiquitous way to deal with service integration such as transaction, security, replication and other high-level features.

Deployment in component models. To partially achieve the static service integration, component models such as CORBA Component Model (CCM) and Enterprise Java Beans (EJB) have emerged. Those standards specify how some services can be statically plugged into components. One of the most important contributions in component models is to separate application programming from deployment. Indeed, deployment descriptors allow component programmers to give information about which services to use. Then the deployer has to customize the deployment descriptor in order to adapt the component to the specificity of the runtime environment (transaction, persistency, security, database support, etc.). According to a deployment descriptor, generators provided by platforms generate adequate interposition code. So, the integration of services by the deployer is basically done through a parameter file. As the definition of the services is integrated into the platform, their evolution and composition is handled by the platform. Consequently, we could suppose that the deployer does not have to deal neither with the application code nor with the generated code.

Deployment drawbacks in component models. However, the code generators support only services provided by the platform. For using new services like replicated EJB for example, the deployer must either modify the generated code or specialize the generator if it is open-source [8], [7]. So, it is a difficult task, because service calls must be plugged either in the generated code or in the code to generate. This makes maintenance, evolution and service composition quite impossible to manage. Moreover, as the code integration is predefined, the deployer has no high level way to adapt the code generation nor to control the composition of existing services. This is needed for example in order to modify or to trace access to persistent data. Finally, these component models do not allow neither dynamic service integration nor dynamic customizations of a single component instance.

Reflection and dynamic service integration. Run-time reflection is a powerful technique that is successfully used to implement non-functional requirements such as load balancing, fault tolerance and security [3], [12], [1], [11]. The code that realizes the non-functional requirements is expressed as meta-programs using a metaobject protocol to reflect the base-level behavior. Generally, these works apply to expert programmers able to deal with reflection. So they are often used by the platform providers, who hide these techniques inside the provided libraries. But we have shown that the deployer still needs to partially do the same job to integrate new services for example. However he/she is not necessarily an expert in reflective systems. Aspect oriented approach [5], [6], [11] proposes to define code integration as ”aspects”, providing a way to meta-program in a declarative form.

2 OUR APPROACH: DYNAMIC CONFIGURATION BASED ON INTERACTION PATTERNS

We propose to define dynamic services integration using a rule language. We have chosen to present it throughout examples. Our goal is to intercept invocations at least with similar controls as the ones generated by the Jonas EJB platform, in order to bear out our approach. So, we have defined integration of several services [8]. At the class level, interaction patterns describe all the possible connections among classes of the application. At the instance level, reified interactions represent actual connections among instances. An interaction pattern is expressed by rules (written in a specific language, based on Interaction Specification Language ISL [10]). An ISL interaction rule describes how the behaviour of an object should change when it interacts with another one. It consists of two parts: the left side is the notifying message and the right side is the reaction to that message. In object-oriented languages, behaviours correspond to class methods.

To show how interactions are created and used, we will take as an example the connection between a component and a security manager. Several mechanisms could be used to reach secure execution of an application. As a first step, we have chosen, to check the validity of invocation by a call to a security manager, which raises an exception if the invocation is not allowed. Figure 1 shows the interaction pattern describing such integration. The interaction modifies the behaviour of the JBean component. When this component is accessed by a controlled method and the security manager forbids the execution an exception is raised, otherwise the call is executed.

 

Figure 1: Interaction pattern for dynamic integration of the security service

The interaction pattern security, when plugged on components, will control any call that can be ”unified” with one of the operations to control. The ”.” operator in left part of the interaction rule denotes the message reception. The operator * stands for any message(method calls) and the operator -> expresses that the code in right part of the interaction rule is executed as a reaction to the notifying message (i.e. the call to the business method). The method call is reified as an object, which is designed by the operator _call.

During execution, an end-user may decide to use this interaction pattern to connect an account instance of JBean (say, MyAccount) to an instance of Security- Manager (say LocalManager) and dynamically integrate the security service on the component MyAccount.

Interaction patterns represent models for component interactions. They contain one or more interaction rules expressing the control that should be executed on the connected components. An interaction rule consists of two parts: the left side is the notifying message and the right side is the reaction. Both sides are separated by the -> operator. Interaction patterns are specified in the Interaction Specification language ISL. The ISL language allowsthe specification of interactions independently of the application language. It defines many operators such as the conditional operator (if . . . then . . . else . . . endif), the sequence operator (;), the concurrency operator (//), waiting operators, exception handling and others.

Implementation of the interaction model. We have adapted our prototype build for Java component model [2], [4], [10] to the component model offer by the CLI to allow dynamic service integration using interaction rules for all languages supported by the CLI platform. We also attempted to define interactions across different platforms, so that we can connect a .NET component to a Java component by means of interaction patterns.

3 PORTING AN INTERACTION SERVICE FROM JAVA TO .NET

Structure of the interaction service

The interaction service can be split into two main parts. The Interaction Server on the one hand and the component management services on the other hand.

The Interaction Server acts as a central repository for interaction patterns and provides methods for pattern instantiation as well as for rule merging. Rule merging is required when two interaction rules with the same notifying message are applied to a component. The component management services include the execution of interaction rules, the management of interactions (adding, removing, call redirection), inter-component communication (using proxies) and code instrumentation tools such as GenInt.

Our purpose was to extend the interaction model and support .NET components without rewriting the interaction server. Therefore we only ported the component management services to the .NET platform and we reused the Java interaction sever (called Noah).

Independently of the targeted platform interaction rules should be represented as an abstract tree. The tree comprises several node types respectively to the actions that can be specified in ISL (Concurrency, Sequence, if then else etc). Another constraint is communication between .NET components and the java interaction server. It is required during two phases of the interaction lifecycle.

The first time is when an interaction pattern is instantiated. The server needs somehow to talk to the target components and hand them the respective interaction rules. Those rules are available at the server as tree of java objects. They need to be packed appropriately before accessing the .NET world. We have chosen to serialize them in XML. This is a universal format which can be easily handled in both Java and .NET.

The second time communication is needed is when more than one interaction rule is applied to the same notifying message of a component. In this case the rules should be merged so that we have only one reaction for each notifying message. As we said before rule merging belongs to the tasks of the interaction server. Since it is already implemented in Java we wanted to reuse it. Therefore we exposed the merging service to .NET components using a webservice.

Component code instrumentation

An interacting component modifies its behaviour dynamically according to the current interaction rules. This ability has to be acquired by the component and therefore it must be prepared to manage interactions and execute them. The interaction server needs among other things to instantiate and remove interaction rules from the component. Moreover the component should store the interaction rules that affect it. This transformation makes a component ”Noah compliant”. The component class is modified in such a way that it provides an interaction management interface (addRule, removeRule, getBehaviour) as well as wrappers for the business methods (call interception) and additional fields to store the interaction rules.

In Java this task is accomplished by the GenInt tool. It instruments the class bytecode using the BCEL library. We developed a similar tool for .NET components which instruments .NET assemblies.

4 IMPLEMENTING THE INTERACTION SERVICE IN .NET

In this part will we discuss some technical details concerning the implementation of the component management services in .NET compared with Java. In addition we show to which extent the .NET platform helped us. We also address some aspects where we think the .NET platform should provide more support to the programmer.

Threading

An ISL tree represents the reaction to a notifying message. Each interacting .NET component should be able to execute reactions. A reaction is an abstract ISL tree with several types of nodes e.g. notify call, global call, assignment, sequence etc. The execution of the tree is multithreading and requires therefore thread synchronisation.

When the executor thread comes to a concurrency node (reaction with several parallel sub-reactions) it starts a new child thread for each sub-reaction. The parent thread blocks and waits until all child threads terminate. The .NET framework provides the Join() method in the class System.Threading.Thread which makes the current thread wait till another thread exits. Java does not provide the Join functionality. For this reason we used the methods wait() and notify() of the class java.lang.Object to get this functionality. We derived a class ReactionThread from java.lang.Thread. A ReactionThread has a reference to its parent thread. This reference is needed to wake up the waiting father thread (call notify()) at the end of the run() method.

ISL trees also include qualified message nodes and waiting message nodes. A qualified message is a labelled message e.g. [1] obj1.method1(). The label enables other nodes to reference the qualified message node and in particular allows waiting message nodes to block till the execution of the qualified message exits.

When the executor comes to a waiting message node e.g. obj2.method2() @X it first checks if the message with label [X] has been executed. If not the executor waits till it gets notified of the end of the execution of the message labelled by X.

In .NET we used the ManualResetEvent class for inter-thread communication. This class notifies one or more waiting threads that an event has occurred. We associate a ManualResetEvent instance with each qualifier (label) in the ISL tree. When the executor visits a waiting message it retrieves the corresponding Manual-ResetEvent instance and calls the WaitOne() method on it. This results in blocking the executor thread so long as the respective qualified message has not been executed. When the executor visits a qualified message it calls the Set() method which sets the state of the ManualResetEvent object to signalled and releases all waiting threads. Hence the execution of the waiting message resumes.

Java does not provide a similar concept to synchronisation events. For this reason we created our own. The class MessageMutex is used for thread synchronisation in Java; it holds a vector of waiting threads. A MessageMutex object is initially locked. The unlock() method releases the MessageMutex and notifies all waiting threads.

In conclusion the System.Threading namespace in .NET provides many useful classes that considerably reduce the work for the programmer. However these concepts (monitor, lock, synchronisation events, join, . . . ) can be also implemented in Java with some additional coding.

Reflection

We used code instrumentation in order to make a class ”Noah compliant”. We developed therefore the previously mentioned tool GenInt. It is available for both Java and .NET. In Java GenInt works at the bytecode level whereas it operates on the MSIL level in .NET. GenInt inserts new fields, methods and constructors to a class and modifies some of its methods and Constructors. The basis for GenInt is the Reflection mechanism.

Compared to Java the .NET framework provides a more powerful reflection API. It is sort of a ”read-write” API in .NET while a ”read-only” API in Java. The System. Reflection.Emit namespace in the .NET framework contains several classes that allow programmers to dynamically create new types at runtime. The TypeBuilder class defines and creates new instances of classes during execution while the classes FieldBuilder and MethodBuilder create new class members. We can even dynamically create code at runtime using the class ILGenerator.

In Java reflection only allows programs to interrogate objects at runtime about their members, their access modifiers and their parent class. Dynamic method invocation is also provided but all the classes and the methods must be defined at compile-time. This means we can neither dynamically create a new class nor even add a field into a given class. Nevertheless, The emitting functionality is not really part of the reflection in the OOP sense but it is rather a bonus in .NET.

On the other hand,we missed some important methods in the .NET reflection API such as a GetMethodBody method in the class MethodInfo that returns an array of IL instructions representing the body of a method. We also expected to retrieve somehow information about exceptions. With .NET reflection we can not find out which exceptions a given method catches or may throw.

Figure 2: Type unification in .NET

Another problem is related to .NET Emitting. In fact,when emitting new types is not possible to create a new Type starting from another type. Let us examine this through an example: we have defined a class Foo and we want to dynamically create a new class FooName which is the same as Foo except that it has a string field name more. We want to tell the TypeBuilder object ”do not start from scratch but start from Foo”. Unfortunately this is not possible. Instead we have to traverse all members of Type Foo using reflection (fields, methods, events, properties, constructors and their access modifiers), then we create a TypeBuilder for FooName and consequently add all members of Foo into the TypeBuilder object. Thereby it is easy to copy the field members and method headers but not the bodies of constructors or methods. To achieve this we used a PE file Reader library. We hope that in the next release, PE Reader/Writer classes will be integrated to the .NET framework.

Type Unification

The code instrumentation tool GenInt creates wrappers for the component’s business methods. A wrapper intercepts the method call and checks whether any interaction is applied to that method. If yes the reaction to the rule should be executed. That means the method parameters should be passed to the reaction executor (as an array of objects). In .NET every thing is an object. The type system unification provides value types with the benefits of object-ness and thus bridge the gap that exists inmany other languages such as Java. This means ValueTypes as well as Reference-Types are derived from the ultimate base class System.object. In situations where value types need to be treated as objects, the CLR automatically converts them to objects. This process is called boxing. The reverse process is called unboxing. Both transformations are totally transparent to the programmer.

Java has another approach on data types. It differentiates between primitive types and classes. Primitive types are not inherited from the java.lang.Object class and must therefore be treated specially. Unlike C#, wrapping and unwrapping in java must be managed by the programmer using wrapper classes such as Integer, Double, Boolean etc. Further more if a method (such as the reflection-based invocation method: Object invoke(String methodName, Object[] parameters) returns an Object of class Double for instance, we should be smart enough to know if this Double is a real Double or a primitive double. We have therefore some overhead because we must store somewhere the real return type that we expect.

Figure 3: Wrapping and Unwrapping in Java

Language Interoperability

The common language runtime CLR provides the necessary foundation for language interoperability by specifying and enforcing a common type system and by providing metadata [9]. Language interoperability is a great advantage we had while porting the interaction platform from Java to .NET. We did the code instrumentation at the MSIL level and thus we could seamlessly support many languages such as VisualBasic, C#, Eiffel, Cobol and others. This is due to the fact that all languages targeting the CLR follow the common type system rules for declaring and using types. The common type system plays a similar role to the IDL in Corba or type libraries in COM. In fact it is also possible to compile many programming languages to Java bytecode but they can not really share and extend each others libraries simply because Java has no match to the Common Type System.

5 CONCLUSION

This paper describes our experience with the .NET platform. We partially ported an Interaction Service(originally implemented in Java) and now are able to provide that service for .NET components too. This experience was very valuable for us at least in two respects.

First we gained insight into many interesting aspects provided by the .NET environment such as remoting, threading, reflection and Web Services. The rich set of capabilities the .NET framework made our task easier to achieve. In particular the reliable language interoperability enabled us to target many languages such VB.NET, C# and Cobol.

Secondly, by porting the service into a new platform, we worked out a set of core functionalities that must be ported in order to support other component platforms. Thus we have a kind of cookbook that can be used to extend the interaction model to Corba Component Model for example.

REFERENCES

[1] W. Joosen B. Robben, B. Vanhaute and P. Verbaeten. "Non-functional policies", Springer 1616, p. 74 - 92, Meta-level Architectures and Reflection, Reflection’99, 1999.

[2] L. Berger. "Mise en oeuvre des interactions en environnements distribués, compilés et fortement typés: le modèle Micado", Nice University phd, 2001.

[3] E. Bergmans and M. Aksit. "Constructing reusable components with multiple concerns using composition filters", in Software Architectures and Component Technology: The State of the Art in Research and Practice, M. Aksit (ed.), Kluwer Academic Publishers, 2000.

[4] L. Bussard. "Towards a pragmatic composition model of corba services based on aspectj", ECOOP 2000 Workshop on Aspect and Dimensions of Concerns, 2000.

[5] J.Hugunin M. Kersten J. Palm G. Kiczales, E. Hilsdale and W.G. Griswold. "An overview of aspects", ECOOP 2000.

[6] F. Budinsky I. Simmonds. H. Ossher, W. Harrison. "Subject-oriented programming: Supporting decentralized development of objects", IBM, http://www.research.ibm.com/sop/.

[7] JBoss. http://www.jboss.org/.

[8] JOnAS. Javatm open application server. http://www.objectweb.org/jonas/jonasHomePage.htm.

[9] MSDN library. http://msdn.microsoft.com.

[10] S. Moisan M. Fornarino, A.-M. Pinna. "Distributed access knowledge-based systems: Reified interaction service for trace and control ”, International Symposium on Distributed Object Applications (DOA 2001), Roma, Italy, 2001.

[11] L. Bergmans P. Tarr, M. D’Hondt and C. V. Lopes. "Workshop on aspects and dimensions of concern: Requirements on, challenge problems for, advanced separation of concerns”, ECOOP 2000 Workshop proceedings, Springer Verlag, 2000.

[12] G. Florin R Pawlak, L. Duchien. "An automatic aspect weaver with a reflective programming language", Springer 1616, p. 250, Meta-level Architectures and Reflection, Reflection’99, 1999.

 

 

About the authors



space Anis Charfi is a computer science student. In the framework of his master thesis, he focused on interactions within the .NET platform. He can be reached at charfi@essi.fr.


  David Emsellem is a research engineer at CNRS/I3S Laboratory, University of Nice. He can be reached atemsellem@essi.fr.


  Michel Riveill is professor of computer science at the University of Nice - Sophia Antipolis. He heads the Rainbow project at the Laboratoire I3S (http://www.i3s.unice.fr). Previously, he was successively professor of Computer Science at Université de Savoie, Institut National Polytechnique de Grenoble since 1993. He can be reached at riveill@essi.fr. See also http://rainbow.essi.fr/riveill.

Cite this article as follows: Anis Charfi, Davis Emsellem, Michel Riveill: “Dynamic component composition in .NET”, in Journal of Object Technology, vol. 3, no. 2, Special issue: .NET: The Programmer’s Perspective: ECOOP Workshop 2003, pp. 37-46. http://ww.jot.fm/issues/issue_2004_02/article4


Previous article

Next article