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

Previous article

Next article


Support for Metadata-driven Selection of Run-time Services in .NET is Promising but Immature

Frank Piessens, Bart Jacobs, Eddy Truyen, and Wouter Joosen, Department of Computer Science, K.U.Leuven, Belgium

 

space

PDF Icon
PDF Version

Abstract

The .NET Framework allows developers to add run-time services to their classes by specifying them in metadata. This metadata-driven service selection is a very powerful and promising mechanism, closely related to ideas developed in the Aspect-Oriented Programming community. Interestingly, the .NET framework supports both services implemented by weaving and services implemented by interception. However, the weaving-based and the interception-based mechanisms seem to have been introduced in the framework independently, and show some unnecessary differences in flexibility, extensibility and configurability. Also both mechanisms still contain some anomalies in their design. In this paper, we describe the mechanisms, and discuss these shortcomings.


1 INTRODUCTION

The Microsoft .NET Framework improves on existing component systems, such as Java and J2EE, COM and COM+, and CORBA, in a number of ways. It combines many good features of each of these predecessors, while also including some innovations, such as:

  • Components are named using precise yet friendly names. Names include version and culture, which provides bind-time flexibility. Namespace ownership is cryptographically enforced.
  • Arbitrary metadata can be added to components and elements within components, for use at compile-time (both source compile-time and JIT compile-time), at link-time, or at run-time. At source compile-time, metadata labeling methods with the exceptions they can throw can be used by the compiler to perform similar checks as a Java compiler does for checked exceptions. Some types of metadata trigger the execution of services directly by the framework and the service code (call) is inserted at JIT compile-time. Other types of metadata trigger link-time checks, for instance a security check whether a component is permitted to link to a given method. Most types of metadata can be retrieved at run time by user code or by the framework for various purposes, such as determining the serializability of a class.
  • Better support for independent extensibility: when a developer introduces a late-bound method in a class, they need to explicitly state whether they intend to override a base class method or not. If a method is later added to the base class, it cannot inadvertently be overridden by an existing method in the derived class.
  • From a security perspective, making highly privileged components globally available on a machine does not automatically imply an increased attack surface, since partially trusted clients only have access to those components that have explicitly been marked as safe for such access by the developer.

All these features together provide for much improved support for component-based development [Szyperski 2002].

In this paper, we focus on how the .NET framework uses metadata to support the activation of various run-time services, such as access control, synchronization, transactions and so forth. The .NET framework is the first commercial product to provide an extensible system for defining metadata-driven run-time services. The authors feel that this approach is very promising, but the current implementation in .NET still has some conceptual and technical shortcomings.

2 METADATA-DRIVEN SELECTION OF RUN-TIME SERVICES

Developers add metadata to types, members and other elements by annotating them with so-called attribute specifications. An attribute specification consists of a type name which names an attribute class, plus an argument list consisting of literal expressions. The type name and the literal values are stored in the assembly by the programming language compiler.

At run-time, this information is used by the CLR to create an instance of the named attribute class. The CLR and the application itself can retrieve the instances associated with an element, look for specific types of instances, and act upon them.

A fairly large number of attribute classes are predefined in the .NET class library. These predefined attributes are used in a variety of ways in the CLR, e.g. to indicate if classes are serializable, or to drive optimizations. One particular use of attributes that concerns us in this paper is the triggering of run-time services in the CLR. Developers can decorate their components with such attributes to indicate to the runtime that certain cross-cutting services (such as access control or synchronization) should be activated at run-time.

We discuss two examples of attributes that request run-time services: security attributes and context attributes.

Security attributes

Security attributes derive from SecurityAttribute; they instruct the CLR to perform named security actions when the element is accessed [LaMacchia 2002]. For example:

For security attributes, the CLR provides the required service by adding extra code when a method is compiled to native code, i.e. essentially by a simple form of run-time code weaving. The added code performs the requested security actions. Code weaving can be applied to static methods as well as instance methods and is a fairly efficient mechanism.

Context attributes

Context attributes [Lowy 2003] are attribute classes that derive from ContextAttribute. They should be applied to a class which derives from ContextBoundObject. Context-bound objects live in a so-called context, that is determined by a number of context properties. These properties essentially say what run-time services the context provides. On instantiation of a context-bound object, the runtime inspects the metadata, and decides in which context the object should be instantiated. If no suitable context is available, a new context providing exactly the required services is created.

Messages to context-bound objects sent from outside the object's context are intercepted and reified, and can then be manipulated by a number of message sinks. These sinks can do pre-processing, post-processing, collect state, manipulate the message and so forth, to provide a certain run-time service.

The mechanism of contexts is extensible by developers: new context attributes can be defined. Such a newly defined attribute can contribute new properties to a context, and can add a new message sink to the context-bound object's interception chain.

The .NET Framework currently includes a single predefined context attribute class, called SynchronizationAttribute. However, it is expected that some or all of the run-time services that are now provided through COM+ will be made available in the form of context attributes in a future version of the framework.

An example of the use of a context attribute, SynchronizationAttribute, is as follows:

Essentially, the mechanism of context-bound objects is a generalization of the idea of container-provided cross-cutting services, as was present already (in a non-extensible way) in COM+, J2EE and CORBA.

This interception-based approach of adding run-time services is more expressive, but less efficient compared to the weaving-based approach.

3 DISCUSSION

The idea of using metadata to drive activation of run-time services is very promising (and in fact has been suggested in the research community before the release of .NET). Metadata-driven selection of services can be considered to be a kind of aspect-oriented programming [Kiczales 1997, Shukla 2003]: the pointcut is determined by the metadata on the static program elements (components, classes and methods), whereas the advice is either the code that is woven in, or the code that is executed by the message sinks.

The current implementation in the CLR however, has some drawbacks. In this section, we will discuss these drawbacks and suggest some improvements.

Ad-hoc and separated definitions of the two mechanisms

It is clear that certain run-time services could be supported both by weaving and by interception. In the current CLR for example, the PrincipalPermissionAttribute can be used to do role-based access control implemented by weaving, and the SecurityRoleAttribute can be used to do role-based access control implemented by interception.

However, it seems as if both mechanisms have found their way into the CLR independently: the weaving-resolved attributes seem to have been introduced to deal with mobile code security and the context attributes are a generalization of contextual composition in COM+. As a consequence, the conceptual similarities between the two approaches are not brought out clearly at the level of, for instance, the attribute classes. This is unfortunate. We see two opportunities for improvement:

  • Conceptually similar design for both mechanisms.
    Since weaving-based and interception-based implementations each have their own advantages and disadvantages, it is clear that both techniques should remain present in the CLR. However, by more clearly showing the conceptual similarity, one could make it easier to switch between the two types of implementation for a given run-time service.
    Since the choice between weaving and interception is often a trade-off between flexibility and efficiency, such switching could be used in optimizations.
  • Flexible weaving mechanism.
    The interception-based mechanism has been designed to be developer-extensible. Developers can define new context attributes that specify new message sinks to be incorporated in the interception chain.
    The weaving-based mechanism is not extensible at all: the CLR recognizes a fixed number of predefined attributes, and there is no way for developers to define new such attributes and specify what code should be woven.
    It would be interesting, certainly from a research point of view, to have a runtime where both mechanisms are extensible.

Limited expressive power

The CLR only supports the addition of attributes to the declaration of static program structures (assemblies, classes, methods, ...), and not to dynamic structures (e.g. interactions, objects). As a consequence, what services are provided for a specific object is fixed at instantiation time of the object, and no client-specific customizations are supported.

The CLR does contain the necessary low-level concepts to support such client-specific customizations: the LogicalCallContext class is a collection object that carries information about the current logical thread of execution, even across remoting calls. It could be used to carry metadata about the current interaction.

The Lasagne customization model [Truyen 2001] takes this idea to its extreme. Instead of allowing attribute specifications scattered across the program code, Lasagne proposes to externalize all such specifications in a separate first class entity, called a composition policy object, that automatically propagates with the logical control flow of subsequent interactions. As such, service selection logic travels as metadata with the locus of execution, rather then being locked up and scattered across the code of the program. In this approach programmer-driven selection of services can still be supported since the composition policy object can be inspected and manipulated at any execution point.

Example

The delivery of non-repudiation evidence of the result of a method call can be programmed as a server-side message sink (that signs incoming parameters, result, and any other information as required, for example). A client-side message sink can then verify this and save it for later use.

For this example, it is definitely useful that the client chooses whether this service is activated for a specific method call.

Unclear composition model for run-time services

While the CLR supports extensible interception chains providing an arbitrary number of services, it is not clear how to deal with services that are not completely orthogonal. For such services, developers should be able to specify at least the ordering of the interceptors, and possibly also interfaces between dependent services. There is no support for this available in the framework.

Adding support to specify dependencies between services to determine the ordering of message sinks, and enabling some form of communication between dependent sinks is relatively straightforward in the current model. Also, some limited support to detect conflicting services is built in: after a new context has been created, each service is queried whether the resulting context is ok or not. If one of the installed services answers negatively, an exception is thrown.

However, since general composition of aspects is itself still an active research topic, there are no simple general solutions to be expected.

The issues surrounding composition of services are discussed by Szyperski in [Szyperski 2002]. He gives the example of composing a logging service and an encryption service; he notes that the effect of the composition depends on the order in which these services are composed, and that for some applications a cooperation between these services is required that is more specialized than a simple execution of one service after the other.

Limited support for configuration of run-time services by administrators

Selecting run-time services by decorating static program structures with attributes provides developers with a powerful mechanism to choose appropriate services. However, for some services (for instance, access control), the deployer and administrator of an application should also have their say, for instance by means of a configuration mechanism. For COM+ services, both metadata-driven and configuration data-driven selection of services is possible already. For context-bound objects in the CLR, support for configuration of services is possible by means of dynamic context properties (but the current CLR does not yet seem to provide tool support for this).

For the weaving-resolved attributes however, only development-time metadata is taken into account. As a consequence, if the developer did not add any security attributes to the code, there is currently no easy way for a deployer/administrator to add them. Of course, enabling administrators to add arbitrary run-time services through configuration also carries some risks: in an extensible system, services can add arbitrary code and hence the semantics of a component could change radically through reconfiguration. This is clearly undesirable.

A possible solution could force developers of run-time services (e.g. developers of context attributes) to indicate if the service could be selected through metadata, through configuration or both.

Low-level programming model

Programming services as message sinks is not the most developer-friendly programming model. Moreover, there is little type-checking on composition of sinks. Programming a service as a decorator of a class solves these two problems, and such a decorator could be compiled to a message sink. However, in such a model, services are less polymorphic in the sense that they cannot easily be applied to many classes. The challenge here is to design a programming model that combines the ease-of-use and type-safety of decorators, with the polymorhphy of message sinks.

For example, the CAESAR [Mezini 2003] programming model is a step towards resolving this challenge. It proposes the notion of collaboration interface [Mezini 2002] as a higher-level module concept on top of aspect-oriented join point interception. A collaboration interface allows to separate the implementation of an aspect - specified in an aspect implementation, from how to connect that implementation with a particular application, which is specified in an aspect binding. Aspect implementation and aspect binding are indirectly connected to each other since they implement two loosely coupled facets of the collaboration interface. This loose coupling is the key to polymorphic reuse of implementations and bindings. Since collaboration interfaces are typed, the type-safety of decorators is provided while the loose coupling between implementation and binding provides the polymorphy of message sinks. An interesting application of this programming model in the context of metadata-driven selection of services is to split message sinks as well in an implementation part and a binding part and have the binding part automatically be generated from the programmer-specified metadata.

4 CONCLUSION

The support for metadata-driven service selection is powerful and promising. In the research community, it has long been recognized that weaving and interception are two powerful techniques to compose cross-cutting services with an application. The .NET framework supports both techniques, but in a very asymmetric way: the weaving-based approach is tuned to support only a small number of built-in services, whereas the interception-based approach is designed to be easily extensible. Also, both approaches still allow for improvements in configurability, usability, and composability of provided services.

REFERENCES

[Kiczales97] G. Kiczales, J. Irwin, J. Lamping, J.-M. Loingtier, C. V. Lopes, C. Maeda, and A. Menhdhekar. “Aspect-oriented programming”. In M. Aksit and S. Matsuoka, editors, ECOOP ’97 — Object-Oriented Programming 11th European Conference, Jyväskylä, Finland, volume 1241, pages 220–242. Springer-Verlag, New York, NY, 1997.

[LaMacchia02] B. LaMacchia, S. Lange, M. Lyons, R. Martin, and K. Price. .NET Framework Security. Addison Wesley, 2002.

[Lowy03] J. Lowy. “Contexts in .NET: Decouple components by injecting custom services into your object’s interception chain”. MSDN Magazine, March, 2003.

[Mezini02] M. Mezini and K. Ostermann. “Integrating independent components with on-demand remodularization”. In Proceedings of the 17th ACM conference on Object-oriented programming, systems, languages, and applications (OOPSLA-02), volume 37(11) of ACM SIGPLAN Notices, pages 52–67. ACM Press, Nov. 4–8 2002.

[Mezini03] M. Mezini and K. Ostermann. “Conquering aspects with CAESAR”. In Proceedings of the 2nd International Conference on Aspect-Oriented Software Development (AOSD), pages 90–100. ACM Press, March 17-21 2003.

[Shukla03] D. Shukla, S. Fell, and C. Sells. “Aspect-oriented programming enables better code encapsulation and reuse”. MSDN Magazine, March, 2003.

[Szyperski02] C. Szyperski, D. Gruntz, and S. Murer. Component Software: Beyond Object-Oriented Programming, 2nd Ed. Pearson Education, 2002.

[Truyen01] E. Truyen, B. Vanhaute, W. Joosen, P. Verbaeten, and B. N. Jørgensen. “Dynamic and selective combination of extensions in component-based applications”. In Proceedings of the 23rd International Conference on Software Engineering (ICSE’01), pages 233–242. IEEE Computer Society, May 12–19 2001.

 

About the authors

Frank Piessens is a professor at the Department of Computer Science of the Katholieke Universiteit Leuven in Belgium. His research interests are in security aspects of software, including security in middleware and operating systems, security architectures, formal methods for security, Java and .NET security, and software interfaces to security technology. He can be reached at frank.piessens@cs.kuleuven.ac.be.

Bart Jacobs is a PhD student at the same Department. His research interests are in formal methods for security, including type systems, calculi, automated and semi-automated reasoning, programming language semantics, and secure platforms. He can be reached at bart.jacobs@cs.kuleuven.ac.be.

Eddy Truyen is a PhD student at the same Department. His research interests are in language technology and middleware, with a focus on client-specific and dynamic customization of distributed systems. He can be reached at eddy.truyen@cs.kuleuven.ac.be.

Wouter Joosen is a professor at the same Department. He is a co-founder and director of Ubizen N.V. and Luciad N.V., two spin-offs of the Department. Wouter's research interests are distributed systems and network applications, with a focus on software security, agent-based systems, real-time systems and development environments. Wouter Joosen is also an Adjunct Professor for Software Engineering at the University of Odense, Denmark. He can be reached at wouter.joosen@cs.kuleuven.ac.be.


Cite this article as follows: Frank Piessens et al.: “Support for Metadata-driven Selection of Run-time Services in .NET is Promising but Immature”, in Journal of Object Technology, vol. 3, no. 2, Special issue: .NET: The Programmer’s Perspective: ECOOP Workshop 2003, pp. 27-35. http://ww.jot.fm/issues/issue_2004_02/article3


Previous article

Next article