7 Implementing interception

Declarative programming is commonly understood to be concerned with telling the computer what to do, and not how it should go about doing it. Imperative programming, on the other hand, entails writing statements that inform the computer of the exact steps needed to accomplish a certain task. It is becoming increasingly common to mix imperative programming with declarative attributes. Such programs are at their heart still imperative, but certain aspects of them are described declaratively.

Component technology, especially as used in enterprise settings, has been at the forefront of this movement. Many component models enable programmers or administrators to configure components declaratively, using either custom metadata written in the programming language that the component is implemented in, specialized tools for administrators or separate configuration files (often written in an XML-based language). Describing an aspect declaratively enables a runtime environment to provide services that would otherwise have to be called upon manually. This is especially useful for enterprise software, that would otherwise have to include error-prone and repetitive code. Modern component models enable many of the services enumerated in section 1.7 through declarative attributes.

One aspect that is commonly expressed using declarative attributes is threading. An object that keeps no state outside of the arguments its operations receive, and is not otherwise dependent on globally accessible resources, is both reentrant and thread-safe, and can be used concurrently by several threads with no ill effects. An object that is not reentrant may ensure that it is safe to access from multiple concurrently executing threads by keeping its state in a data store specific to the currently executing thread (so-called thread-local storage). Some objects are inherently not thread-safe, and it is vital that only one thread at a time is allowed to enter their operation bodies. The classic way to achieve the latter is to manually ensure that threads wait their turn, using a construct such as a counting semaphore. A declarative attribute related to threading enables a developer to state whether an object is thread-safe, and rely on the component model implementation to enforce the desired threading policy.

COM, for instance, subdivides a process into apartments, which are concurrency boundaries that implement different threading policies. Objects belong to apartments that cater to their threading characteristics. COM ensures that only one call at a time reaches a non-thread-safe object by insisting that inter-apartment calls are made using proxies (thus serializing invocations). As a result, calls to non-thread-safe objects are converted to messages, which are put in a message queue to be inspected at the leisure of the thread servicing the non-thread-safe objects. Only one thread is allowed in an apartment that houses non-thread-safe objects (Prosise 2001).

Database transactions are also a popular target for declarative attributes. Instead of manually having to commit or roll back a transaction, an object that is part of a context that participates in a transaction can have the component model implementation perform this service for it—committing the transaction if no exception is thrown, and rolling it back otherwise. Component model implementations are typically able to manage transactions that span multiple database servers. Microsoft introduced their Microsoft Transaction Server (MTS) product, built on top of COM, to provide services such as transaction processing to components running in its application server. MTS was later merged with COM to form COM+. Declarative transaction processing is provided by many other environments for the enterprise, such as CORBA, .NET, and the Enterprise Edition of the Java platform.

Component models provide these services by intercepting calls to objects. When a call has been intercepted, the component model implementation executes code specific to the requested service before passing control to the target object, if such access has not been barred by a security service. Code may also be executed after the commencement of a call (for instance, to commit a transaction if no exception has been thrown). This mechanism is similar to aspect-oriented programming in that the services provided by component models can be likened to aspects.