RSS feed

Linkedin Profile

Tags:
programming
seattle
things that bug me
wall art

Posts by month: 10/08 (2)
08/08 (1)
06/08 (2)
05/08 (1)
03/08 (3)
02/08 (1)
01/08 (2)
12/07 (2)
11/07 (1)
07/07 (1)
05/07 (2)
02/07 (1)
01/07 (1)
12/06 (1)
11/06 (1)
10/06 (1)
08/06 (1)
07/06 (1)
06/06 (2)
05/06 (1)
04/06 (2)
02/06 (1)
01/06 (2)
12/05 (3)
11/05 (2)
09/05 (5)
08/05 (5)
07/05 (7)
06/05 (3)
05/05 (6)
04/05 (8)
03/05 (7)
02/05 (7)
01/05 (6)
12/04 (2)
11/04 (3)
10/04 (5)
09/04 (3)
08/04 (5)
07/04 (5)
06/04 (4)
05/04 (4)
04/04 (9)
03/04 (4)
02/04 (3)
01/04 (5)
12/03 (1)
11/03 (14)
10/03 (8)


Where next for AOP?
2004-10-24

This entry re-packages some notes I put together early this year on solutions to cross-cutting, focusing particularly on what the next paradigm after aspect-oriented programming will look like. Although I like the goals of AOP, I am sceptical that it will ever really take off. But, I think some of the ideas in AOP will influence the next generation of languages and tools. But before I get onto that, I'd like to point out some problems with AOP as it stands.

Platforms are standardising extension points: AOP's extensibility hooks are usually used for a standard set of concerns, like synchronisation, transaction control, security, serialisation, tracing, logging and caching. Not surprisingly, these sorts of concerns are migrating to platforms and languages, and these implementations are beginning to handle some of the interdependencies that make aspect development so difficult.

Loss of locality of effects: Programmers are very comfortable with the sequential nature of program statements, as dictated by a single source file. While method calls, classes and macros disrupt this to some extent, they still allow fairly easy traceability (unlimited extensibility through sub-typing being the exception here). AOP cannot achieve this same sequential display without dedicated tools, which means it needs to deliver a lot of value to get a foot in the door of most development shops.

Interdependencies between concerns: It's easy to draw up an AOP example where a single concern is neatly modularised, but in fact there are very few independent concerns. For example, it's pretty easy to factor out auth/auth on a few methods and calling this a security concern. But what about serialisable types crossing that same interface? Tracing is pretty much independent, but try naming another.

Source code is the wrong level: AOP has two parts, WhereToInfluence and WhatToDo (Lieberherr, Palm et al. 2004). Unfortunately there is a big problem with the WhereToInfluence part – code queries that match on type and method names, interactions with other modules etc. don't deliver nearly enough to effectively deal with real-world cross-cutting. Take security for example – I could write a pretty tortured PCD to add some auth/auth checking to the right methods in a system, but I really want to write "Any callable method at a trust boundary". So what does a pointcut for a trust boundary look like? What does "callable" mean, if incoming calls are web service messages? This kind of WhereToInfluence needs all sorts of information, like physical deployment, architecture and interactions with other systems that I don't even own. It's a cross-cutting concern, but forget explaining it based on code.

AOP in a few pars

The term aspect-oriented programming was popularised by several papers co-authored by Gregor Kiczales, (Kiczales, Lamping et al. 1997; Mendhekar, Kiczales et al. 1997), describing a style of programming that separates particular concerns into dedicated modules. This separation aims to modularise cross-cutting concerns. A cross-cutting concern is a logically distinct part of a program that is implemented in several places within that program.

AOP allows concern modules to be somehow combined with other modules by a compiler or virtual machine to create a complete program, but also allows these concern modules to be developed, maintained and understood in isolation from other modules.

Filman and Friedman (Filman and Friedman 2000), suggest that AOP is characterised by the ability to make quantified programmatic assertions over programs that are not aware of these assertions. Douence, Motelet and Sudholt (Douence, Motelet et al. 2001) propose that AOP is defined by the ability to identify and abstract points of interest within a program, and add behaviour at these points through a framework. This definition is close to the essential characteristics of AOP in (Walker, Zdancewic et al. 2003): firstly, a means of designating control-flow points in a program, and secondly, a way of manipulating behaviour at these points.

The most prominent implementation of AOP is AspectJ (Kiczales, Hilsdale et al. 2001). The AspectJ approach extends the Java language (Gosling, Joy et al. 2000) with constructs for expressing points in program execution where behaviour should be added, along with constructs for specifying exactly how this behaviour should integrate with that point. These constructs are contained within AspectJ files, which are compiled along with regular Java files by the AspectJ compiler. This language extension and compiler model provides a rich integration model with minimal runtime performance impact.

A number of different implementations have been developed to support this model of aspect-oriented programming with different languages, including AspectS for Smalltalk (Hirschfield 2002), AspectC++ for C++ (Spinczyk, Gal et al. 2004), AspectC# for C# (Kim 2002) and AspectC for C (Coady, Kiczales et al. 2001).

Other notable approaches to AOP include Hyper/J (Ossher and Tarr 2000), which provides an entirely new language for describing the relationship between different modules implemented in Java. Several authors have used the concept of message interception to build systems that provide AOP functionality, notably composite adapters (Mezini, Seiter et al. 2000) and composition filters (Bergmans and Aksit 2001).

Loss of locality of effects

The ability of an aspect to alter the behaviour of some oblivious code makes that code hard to understand. By not only encapsulating a concern, but also removing any reference to that concern from the module it targets, AOP reduces the locality of effects in a system. While this completely separates concerns at the source code level, it can prove challenging when trying to understand a system's behaviour from source files.

Filman and Friedman (Filman and Friedman 2000) characterise AOP as a form of programming, where an aspect programmer makes quantified assertions about some modules, and where the developers of these modules are oblivious to these assertions. This emphasis on quantification and obliviousness is reflected in the design of AOP languages and frameworks, which use an advice declaration to identify relevant join points within a system (quantification), and a compiler or runtime process to alter behaviour at these points, without reflecting this in source code (obliviousness).

Encapsulation mechanisms like classes, macros, procedures and inheritance also reduce locality. However, each of these is referenced explicitly in the module they effect – via an 'inherits' clause, reference to a type, macro or method call.

AOP proponents acknowledge that tools to manage aspects are essential for developers (Kiczales, Hilsdale et al. 2001). Tool support, such as the aspect visualisation provided by AJDT/Eclipse (Eclipse.org 2004), supports understanding and management of aspects by creating a virtual source file.

Without these tools, determining the impact of aspects on a system is tedious and error prone. This is a problem for AOP adoption, since having just one aspect developer in a team means everyone needs the tools. This rules out stealth-adoption, where some programmer gets productive with a tool before selling it to everyone else in the team.

So, AOP better do something great, straight away, or I will be reticent to adopt the necessary tools. The next few sections explain why AOP actually has a lot of problems delivering that great improvement. Firstly, just separating out concerns doesn't mean we can ignore their dependencies. Secondly, the kind of features that AOP targets are increasingly migrating to platforms and languages, which at a minimum reduces clutter. Lastly, cross-cutting is difficult to describe neatly when working at the source code level.

Dependencies between dimensions of concern

While AOP allows concerns to be separated into separate files, significant dependencies may still exist between aspects and other modules. This sounds kind of obvious, but it is amazing the way AOP literature tends to ignore this. Even separated concerns are still bound together by logical dependencies, and while AOP provides languages and tools for specifying concerns in isolation, it does nothing to help developers reason about them in combination.

This separation is useful when concerns are truly independent. But how many independent concerns are there? I think they are pretty rare: of the classic AOP concerns (error management, tracing/logging, security, transaction control, synchronisation and caching), only tracing/logging could be considered entirely side-effect free, with respect to the underlying "base" system.

So are the concerns mentioned above interdependent? I think so. For example, there are large overlaps between error management, synchronisation and transaction control, and error management, security (in the loosest sense) and caching. This present problems for the "quantification and obliviousness" (Filman and Friedman 2000) model of AOP: Assuming programmers can safely ignore AOP concerns while developing core program logic, AOP programmers must then understand not only the base system, but also a host of interdependent aspects. In the situation where several aspects must be considered at once, it is not clear that the separation of concerns provided by AOP is not in fact an excessive separation.

Platform and language evolution

AOP literature identifies several non-functional concerns as ideal targets for aspect-oriented programming, including error handling (Kiczales, Lamping et al. 1997), security (specifically authorization) (Viega, Bloch et al. 2001; Welch and Stroud 2003), transaction control (Popovici, Alonso et al. 2003), tracing and logging (Miles 2004), synchronization (Kiczales, Lamping et al. 1997) and caching (Holmes 2004). This is just a partial list, but captures the spirit of AOP work.

In these examples, applying AOP removes a great deal of tangled code, replacing it with well factored aspects. While AOP may produce significant benefits in these examples, it is worthwhile to consider the impact of platform and language evolution on these areas. Here are just a few examples that are relevant:

  • Configurable error handling frameworks (Microsoft 2003)
  • Declarative access control in Microsoft's .Net platform (Microsoft, Hewlett-Packard et al. 2000) and Enterprise Java Beans (DeMichiel 2003)
  • Declarative or externally configurable transaction control in Microsoft.Net and Enterprise Java Beans
  • Synchronisation primitives in Java (Gosling, Joy et al. 2000) and C# (ECMA 2002)

While these do not address every application of AOP mentioned above, they are indicative of ongoing platform and language evolution to specifically address the sort of concern targeted by AOP. While increasing support at framework and language level does not entirely remove the tangling of concerns, it does greatly reduce the amount of custom code and the amount of clutter and associated complexity. When configuration is separated into different files which are used by the framework at runtime, programmers achieve an AOP-like separation, but using standardised mechanisms.

The drawback of these standardized concerns is that they are a "one size fits all", and will definitely not work for everybody. They do have some big pluses:

  • Developers know about them. They are part of the platform, and every programmer can legitimately be expected to understand how they impact their code.
  • To some extent, they can coordinate and validate interdependent concerns. An example: RPC frameworks can allow separate configuration of security (and apply it consistently) across remote interfaces.

Levels of abstraction and Pointcuts

Aspect-oriented programming defines pointcuts according to programming language constructs like methods and classes, as well as behaviour, like handling a particular exception, or calling a particular method. This code-centric approach presents problems for large scale development, where more information and more intentional languages are required.

At the core of this problem is the lack of useful information in source code. Sure, source has a lot of low-level instructions, but is missing the contextual stuff that has a huge impact on non-functional concerns. Think about deployment – how does that impact caching and security? What about runtime identity – what is that going to do to my data access pattern? In a complex distributed system, where is the best place to insert tracing behaviour?

I'm going to need a lot of information that isn't in source to deal with these kind of concerns, and the join point / pointcut model degenerates into enumeration rapidly when I begin to deal with them using AOP. The kind of information that is needed here isn't stored in source, but architectural diagrams, deployment descriptors, installer programs, and other non-code artifacts.

So what kind of tools do we need to work at the right level of abstraction here? At a minimum, we need to be able to draw information from outside source code, ideally from a set of complementary viewpoints that provide the information above. And going back to my example in the introduction, we need a more intentional way of tying these things together – something like "Trace messages at machine boundaries, and include caller information at trust boundaries", or "Demand data access permissions when calling into the data access layer".

This wouldn't have to be textual – it could just as easily be achieved by annotating higher-level diagrams, or using dedicated visual languages. This level of sophistication definitely requires tool support, which I've already mentioned as a tough sell, but it would also deliver a lot more than AOP.

Where to from here?

So, AOP gets the problem right, but the solution still needs work. What are the key ideas that must be carried forward into next generation tools?

Cross-cutting concerns exist, and are hard to manage – The motivation for AOP is well founded: cross-cutting concerns do lead to tangled code, and this presents scalability problems for software development. Managing cross-cutting concerns is currently a labour-intensive exercise, and steals resources from other activities. Despite efforts to manage these concerns, inconsistency is practically guaranteed in large systems because of scatter and redundancy.

It may be useful to consider some concerns in isolation… Concise definitions of a single concern, removed from the visual clutter of other code and localised in a single module, are a good thing.

…but concerns are inherently related – While concise definitions are nice to look at, they can't be considered without other concerns. And the more support developers get in managing these interdependencies, the more productive they will be.

Solving the cross-cutting problem will involve more than just extensibility points – Separating concerns into dedicated modules is only a partial solution. Developers need some way of managing the consistency of concerns. In practice, this means some higher level of abstraction during development. This higher level is really massive scope creep on AOP, but it's necessary to get this cross-cutting thing to go away. Particularly, the higher level must:

  • Provide more intentional modelling languages for each concern dimension
  • Have the ability to understand the meaning of a concern
  • Manage interdependencies between concerns
  • Allow languages to integrate at different levels (e.g. Tracepoints vs. top-level views)

Tangling problems will always exist to some extent – Lastly, it is important to realise that the scope and perspectives needed by architects, developers and analysts will probably continue to surpass the capabilities of tools and languages for some time.

References

Bergmans, L. and M. Aksit (2001). "Composing Multiple Concerns Using Composition Filters." Communications of the ACM(October 2001).

Coady, Y., G. Kiczales, et al. (2001). "Using AspectC to Improve the Modularity of Path-Specific Customization in Operating System Code." Proceedings of ESEC/FSE 2001: 88 - 98.

DeMichiel, L. G. (2003). Enterprise JavaBeans Specification, Version 2.1, Sun Microsystems.

Douence, R., O. Motelet, et al. (2001). A formal definition of crosscuts. Nantes, École des Mines de Nantes.

Eclipse.org (2004). AspectJ Development Tools.

ECMA (2002). The C# Language Specification, Second Edition, ECMA International.

Filman, R. E. and D. P. Friedman (2000). Aspect-Oriented Programming is Quantification and Obliviousness. Workshop on advanced separation of concerns, OOPSLA 2000, Minneapolis.

Gosling, J., B. Joy, et al. (2000). The Java Language Specification, Second Edition. Santa Clara, Sun Microsystems, Inc.

Hirschfield, R. (2002). "Aspect-Oriented Programming with AspectS." Lecture Notes in Computer Science: Objects, Components, Architectures, Services, and Applications for a NetworkedWorld: International Conference NetObjectDays, NODe 2002.

Holmes, J. (2004). "Taking Abstraction One Step Further." Oracle Magazine(September / October 2004).

Kiczales, G., E. Hilsdale, et al. (2001). "An Overview of AspectJ." Proceedings of the European Conference on Object-Oriented Programming, Budapest, Hungary: 327 - 353.

Kiczales, G., J. Lamping, et al. (1997). "Aspect-Oriented Programming." European Conference of Object Oriented Programming (ECOOP): 220 - 242.

Kim, H. (2002). "AspectC#: An AOSD implementation for C#."

Lieberherr, K., J. Palm, et al. (2004). Expressiveness and Complexity of Crosscut Languages. Boston, Northeastern University.

Mendhekar, A., G. Kiczales, et al. (1997). RG: A Case Study for Aspect-Oriented Programming. Palo Alto, Xerox PARC.

Mezini, M., L. Seiter, et al. (2000). Component Integration with Pluggable Composite Adapters. Software Architectures and Component Technology: The State of the Art in Research and Practice. M. Aksit, Kluwer.

Microsoft (2003). Exception Management Application Block. Redmond, Microsoft Corporation.

Microsoft, Hewlett-Packard, et al. (2000). ECMA and ISO/IEC C# and Common Language Infrastructure Standard.

Miles, R. (2004). "An Introduction to Aspect-Oriented Programming with the Spring Framework, Part 1." O'Reilly OnJava.com.

Ossher, H. and P. Tarr (2000). "Multi-dimensional Separation of Concerns and The Hyperspace Approach." Proceedings of the Symposium on Software Architectures and Component Technology: The State of the Art in Software Development.

Popovici, A., G. Alonso, et al. (2003). "Spontaneous Container Services." Proceedings of the 17th Europeean Conference for Object-Oriented.

Spinczyk, O., A. Gal, et al. (2004). Aspect-Oriented Programming with C++ and AspectC++. Tutorial at AOSD 2004, Lancaster UK.

Viega, J., J. T. Bloch, et al. (2001). "Applying Aspect-Oriented Programming to Security." CutterIT Journal 14(2): 31-39.

Walker, D., S. Zdancewic, et al. (2003). "A Theory of Aspects." Proceedings of ICFP 2003: 127 - 139.

Welch, I. S. and R. J. Stroud (2003). "Re-engineering Security as a Crosscutting Concern." The Computer Journal 46(5): 578-589.

Back to weblog