| the Apache Ant site |

| Home |

| Projects |

# Design Overview

Introduction

The purpose of this document is to communicate the overall structure and design patters used in Antidote, the GUI for Ant. This document is a work in progress, as well as a living document, and it is most likely not be in full synchronization with the source code. Therefore, if there is any doubt, view the source ;-)

Overview

The Antidote architecture design aims to provide a high level of modularity and extensibility. Ideally the components of Antidote will be able to be assembled in different configurations to provide the type of application or plug-in desired.

To achieve this modularity, a high level of decoupling is necessary. The standard UI design approach of providing separation of view (presentation) from model (data) is applied, leveraging the built-in Ant data model where possible, as well as the predefined Swing model interfaces. Furthermore, the architecture is highly event driven, whereby modules communicate via a shared communications channel.

To a large extent, the configuration of application modules is driven by localized configuration files, allowing new modules or data views to be added, as well as providing multi-language support.

The diagram below conveys a high altitude view of the application's structure. As the application grows, new components will be plugged in to what will be described as the `EventBus`

Antidote Component Architecture/Event Bus

       +---------------+ +----------------+ +-------------+ +-------------+
       |               | |                | |             | |             |
       | ActionManager | | EventResponder | |  AntModule  | |  AntModule  |
       |               | |                | |(ProjectNav) | |(SourceEdit) |
       +---------------+ +----------------+ +-------------+ +-------------+
               |                  ^               ^               ^
               |                  |               |               |
          ActionEvent         EventObject      AntEvent       AntEvent
               |                  |               |               |
               v                  v               v               v
      /---------------------------------------------------------------------\
     /                                                                       \
    <                                   EventBus                              >
     \                                                                       /
      \---------------------------------------------------------------------/
               |                  ^               ^               ^
               |                  |               |               |
          EventObject         ChangeEvent      BuildEvent     EventObject
               |                  |               |               |
               v                  |               |               v
       +---------------+ +----------------+ +-------------+ +--------------+
       |               | |                | |             | |              |
       |   Console     | |  ProjectProxy  | |    Ant      | | (Your Module)|
       |               | |                | |             | |              |
       +---------------+ +----------------+ +-------------+ +--------------+

The backbone of the application is the `EventBus`. Any component of the application can post events to the `EventBus`. Components that wish to receive events are called `BusMember`s.

The `EventBus` will dispatch any object of type `java.util.Event`, which means that Ant `BuildEvent` objects, as well as `AWTEvent` objects can be posted (if desired). A new class of events called `AntEvent` is defined for Antidote specific events, which have the additional capability of being canceled mid-dispatch.

Each `BusMember` must provide a `BusFilter` instance, which is the members' means of telling the bus which events it is interested in. This allows a `BusMember` to, say, only receive `AntEvent` objects.

When a `BusMember` registers itself with the `EventBus`, it must provide a (so called) interrupt level which is a integer value defining a relative ordering for dispatching `EventObject`s to `BusMember`s. The purpose of this is to allow certain `BusMember` instances to see an event before others, and in the case of `AntEvent` objects, keep the event from propagating onward. The `EventBus` class defines the interrupt level constants `VETOING=1`, `MONITORING=5`, and `RESPONDING=10` to help define categories of members. The implied purpose being that:

- `VETOING`: Listens for certain types of events, and may process them in a non-default manner to determine if the event should be canceled before being dispatched to the `RESPONDING` group.
- `MONITORING`: Just listens for events, like a logger or status monitor.
- `RESPONDING`: Process events in a default manner, knowing that the event has passed any `VETOING` members.

Within a specific interrupt level, the order in which members will receive events is undefined. A `BusMember` may be registered at a level that is +/- of one of the defined levels, as long as it follows the constraint `MONITORING <= interruptLevel <= MAX_INTERRUPT`.

Actions and ActionManager

Extensive use of the `javax.swing.Action` interface is made for defining the set of menu and tool bar options that are available. The configuration file `action.properties` exists to define what should appear in the menu and toolbar, how it is displayed, and the `Action` command name that is dispatched when the user invokes that action. A class called `ActionManager` exists for not only processing the configuration file, but also for dispatching invoked action events to the `EventBus`, and for controlling the enabled state of an `Action`. When a new menu item or toolbar button is desired, first it is added to the `action.properties` file, and then the code to respond to it is added to the `EventResponder` (see below).

Commands and EventResponder

At some point in the stages of event processing, an event may require the data model to be modified, or some other task be performed. The `Command` interface is defined to classify code which performs some task or operation. This is distinct from an `Action`, which is a user request for an operation. A `Command` class is the encapsulation of the operation itself.

When an `Action` generates an `ActionEvent`, the event is posted to the `EventBus` which delivers the event to all interested `BusMember`s. It eventually makes it to the `EventResponder` instance (registered at the `RESPONDING` interrupt level), which is responsible for translating specific events into `Command` objects, and then executing the `Command` object. For example, when the user selects the "Open..." menu option, an `ActionEvent` is generated by the Swing `MenuItem` class, which is then posted to the `EventBus` by the `ActionManager`. The `ActionEvent` is delivered to the `EventResponder`, which converts the `ActionEvent` into a `Command` instance. The `EventResponder` then calls the method `Command.execute()` to invoke the command (which displays a dialog for selecting a file to open).

When adding new `Action`s or general tasks to the application, a `Command` object should be created to encapsulate the behavior. This includes most operations which modify the state of the data model.

The purpose of this encapsulation is to allow the clean separation of making a request, and servicing a request. Due to various conditions in the application state, the actually response to a request may change, as well as who services it. This design approach facilitates that.

Data Model and Views

NB: This part of the architecture is not fleshed out very well. There needs to be a discussion of the degree to which the Antidote development should be able to impose changes on the Ant data model, and to what level that model should be mirrored in the Antidote code base. The coupling between them should be kept low, and at the same time changes to one should affect the other minimally. Still, features like property change events and bean introspection (or BeanInfo) may be needed to be added to the Ant data model. Right now the data model is encapsulated in the package `org.apache.tools.ant.gui.acs` (where "`acs`" stands for "Ant Construction Set").

Application Context

In order to keep the coupling among application modules to a minimum, a single point of reference is needed for coordination and data sharing. The class `AppContext` is the catch-all class for containing the application state. Most modules and `Command` classes require an instance of the `AppContext` class. Because all state information in contained in an `AppContext` instance, multiple instances of Antidote can run inside the same JVM as long as each has it's own `AppContext`. (Interestingly, two instances of the Antidote could conceivably share an `AppContext` instance through RMI, allowing remote interaction/collaboration.)

Configuration and ResourceManager

Full "i18n" support should be assumed in modern applications, and all user viewable strings should be defined in a configuration file. For Antidote this configuration file is `antidote.properties`, which is located (with other UI resources) in the sub-package "resources".

To aid in the lookup of text properties, as well as other resources like icons, a class called `ResourceManager` is defined. There are various convenience methods attached to this class, which will likely grow to make looking up configuration values as easy as possible.

The organization of configuration properties is based on the fully qualified path of the class that requires the property. For example, the "about" box contains a messages, so it looks for the property "`org.apache.tools.ant.gui.About.message`" for the text message it should display. Therefore, the `ResourceManager` method `getString()` takes a `Class` instance as well as a `String` key. Please see the `ResourceManager` documentation for more information. Given this support, no user visible strings should appear in the source code itself.

Copyright © 2000-2003 The Apache Software Foundation. All rights reserved.