Site contents (c) 1998 ABLE Group. Carnegie Mellon University

Modeling a System with Acme

Andrew Kompanek
ABLE Group
School of Computer Science
Carnegie Mellon University

Abstract

Acme is a simple, generic language for describing software architectures and families of architectures. It is intended to be standard representation for tools. It provides constructs for describing systems as graphs of components interacting via connectors, a representation mechanism for hierarchical decomposition of components and connectors into subsystems and a way to describe families and types of elements in terms of these constructs.

Acme does not, however, provide a specific model for describing system behavior, its functional properties, its relationship to executable code or any other aspect of a system. Instead, elements may be annotated with properties that represent this sort of descriptive information. It is up to a tool operating on particular description (or a person reading a description) to interpret annotations in a meaningful way. By defining new ways of annotating descriptions, and developing new tools that operate on these descriptions, Acme can extended to support richer depictions of systems.

As a standard representation for software architectures, Acme can be used as way of integrating a wide variety of different types of architectural tools, including analysis tools, compilers and design and visualization tools. Existing tools can be made "Acme-compliant" by adapting them to work with the Acme representation, allowing designers to more easily use different tools together. New tools may also be written based directly on Acme, making these tools available to anyone using Acme. Tool writers developing new Acme tools can take advantage of the parser and design representation provided by the Acme developer's library (AcmeLib) rather than building a tool from scratch.

Acme's capabilities are extended by defining new encodings and developing tools based on the encoding. This article describes the concepts provided by Acme and how to use them as a basis for representing a description of an architecture in Acme. It is based on the following simple steps:

  1. Identify the concepts in the source model that correspond to the Acme architectural concepts: system, component, connector, port, role or representation.
  2. Define a family or set of families for the model. Define a component, connector, port or role type to represent each of the architectural concepts.
  3. Define a set of property types which will make up a property language for describing elements in the model.

This article describes the concepts provided by Acme, explains how to define a new family based on these concepts, and illustrates the process of encoding a particular model as a family.

Scope

This article is intended for anyone who wishes to model a system in Acme. It should be of particular value to someone who wishes to adapt a tool to operate on the Acme design representation.

Note: This article does not address the issue of translating between different semantic models. For information on translation see < ... >.

Contents

Overview is an overview of software architecture and Acme.
Acme Core Concepts describes the core concepts provided by Acme.
Representing a Style in Acme describes the Acme notion of an architectural family.
A Sample Acme Family follows an example of how a model can be encoded as a family.

Overview: Representing Architectures in Acme

Acme is intended as a language for describing the architecture of a system. While there is no agreement on specific definition of architectural design, what all architectural approaches have in common is the goal of understanding a system at a high level of abstraction. All approaches provide some way of capturing system-level structure and behavior in terms of large granularity components, abstracting away from "implementation level" details like the algorithms used in an implementation and particulars about how data is represented.

Acme is one of several different architectural description languages (ADLs) that have been developed during the past several years. ADLs provide a way of describing architectures in a formal or semi-formal way. They help a designer document design decisions and reason about the implications of those decisions. The particular sort of model used depends on what kinds of design questions the designer needs to answer. They include questions about the correctness of a design (for example, absence of deadlock in a concurrent system) as well as answering questions about properties like performance.

The Acme language was developed from the realization that the set of possible problems designers would like to model and techniques necessary to do the modeling is large and difficult to circumscribe. Consequently, rather than providing a fixed set of models for formulating solutions, Acme provides general, domain-neutral foundation for developing new tools and notations.

The concepts provided by Acme language are intended as generalizations of the concepts common to different ADLs. The structures provided by Acme are based on the simple observation that all the different models have their origins in the informal box and line diagrams traditionally used to depict the architecture of the system. That is, most descriptions of architectures have at their heart a structural description of a system as graphs of components. Any additional semantics are expressed in terms of this basic structure. Acme provides a syntax for representing this structure and an annotation mechanism for describing additional semantics.

It's important to recognize that even though Acme is based on the idea of representing an architecture as a static structure, this does not mean that only static, fixed architectures can be represented in Acme. Acme may also be used as a way of representing reconfigurable architectures by expressing the possible reconfigurations in terms of the Acme structures. For example, a system might include properties that describe components that may be added at run-time and how to attach them to the current system.

Acme may also be used to represent design alternatives. An Acme description can be used to describe sets of architectures -- not just a single fixed architecture. For example, a component description in Acme may include several different implementations, only one of which would actually be chosen in an implementation of the architecture.

The first step in using Acme for any application is understanding the basic concepts defined by the Acme language. Although Acme lacks the expressive power of a full-fledged ADL, the Acme language constructs do correspond to real world software design concepts.

Acme Core Concepts

Components

Components are the basic building blocks in an Acme description of a system. They represent centers of computation: the elements of a system responsible for doing the "work" in a system. This is in contrast to connectors (see the next section) which model communication and interaction between components in a system. Components may be used to model both hardware and software or provide an abstraction that may be mapped to either hardware, software or a combination. Intuitively, components correspond to the "boxes" in box and line diagrams

Examples of typical components include a process running on some processor, a hardware resource like an external device, a server that supports some request protocol, a filesystem, a database, a conceptual chunk of a program like a view (in document-view model) or a data stream filter. A component may be used to capture any number of abstract characterizations of the computational aspects of a system.

Components may be used to describe elements of a variety of different computational models at varying levels of abstraction. For example, a component may represent a component in the functional decomposition of a single executable or to represent concurrent processes communicating based on some synchronization model. Alternatively, particular models may either hide details about concurrency and synchronization details or leave them undefined (for example, in a model that shows data flow through a system but not control flow).

In many cases, components are used to abstract away from the details of a "chunk" of large implementation, providing a useful abstract depiction of what would otherwise be too complex to reason about. This "chunk" may or may not have a clear analogue as a module, library, class or other encapsulated part of an implementation. In the case where the anologue doesn't exist, Acme provide a way of explicitly documenting organization that would otherwise be lost.

In some cases, certain aspects of component usage will be captured in the connectors used to connect the component rather than by component itself. An important part of architectural design is properly separating concerns about communication from computational concerns. For example, consider a component that supports some set of operations. In a system based on a synchronous procedure call, it would be connected to components that need to use it by a simple connector that allows direct procedure calls between the components. In another context, that same component might connected to a "smart" implicit invocation connector which knows how to translate broadcast events into the appropriate invocations on the component.

Like all elements in Acme, a component includes properties which can be used to describe aspects of its computational behavior or structure. Properties provide a way of encoding information that can be interpreted by someone who understands the encoding. See the Acme Syntax appendix for a summary of Component syntax.

A typical use of a property is to describe the actual computation performed by a component. Typically this would be done using a language that provides a more abstract depiction of the computation than a programming language so that the properties of the computation can be reasoned about more easily or to simplify the problem of defining an implementation. For example, the pattern of reads and writes of a data transformation Filter in a pipe and filter system is described:

Component TheFilter = {
   Port in;
   Port out;
   Property implementation : String = 
            "while (!in.eof) { 
               in.read; in.read; compute; out.write}";
}

A description like could be used as part of analysis that computes the optimal size of buffers for different pipes in a pipe and filter system or as part of a specification that can be used to generate an executable.

A property may also be used to describe a non-functional property of a component. For example, fault-tolerance, memory requirements or performance properties of the component. These properties may be specified (as estimates or requirements) by a designer, or computed by a tool. For example, the expected time it takes a server to respond to a request could be encoded as a property:

Component Server = {
   Port requests;
   Property responsetime : Float = 15.00 << units="ms">>;
}

Of course many characteristics of a component are implicit. For example, our two descriptions above only make sense if you understand what we mean by a "Filter" and a "Server". In order to make sense of the properties of a component, you need to understand what assumptions are being made about the nature of a component and what concept (either something quite abstract or something in the real world), the component is being used to model. People and tools exchanging Acme descriptions must have this shared understanding to communicate meaningfully. A property can be used to refer to some external definition. For example, returning to our filter example:

Component TheFilter = {
   Property external-type = "SomeADL::Filter";
   Port in;
   Port out;
   Property implementation : String = 
            "while (!in.eof) { 
               in.read; in.read; compute; out.write}";
}

The "external-type" property can be used as way of capturing the fact that TheFilter is an instance of of the "Filter" concept defined in the adl "SomeADL". This is slightly better than simply relying on the reader's intuitive understanding of what a "Filter".

Frequently, it's more useful to capture the concept in the form of an Architectural type that captures the external concept. This allows you to explicitly define the Filter as a concept that can be instantiated. The Acme Component Type description can then be used to specify the properties that are required to specify a particular filter and any structure that is included in all instances of a filter. The section Representing an Architectural Style in Acme describes types in detail and how they can be used.

Whether external type information is encoded as a property or captured in an explicit type depends in large part on the application. If the "type" is something that a designer might change during design after a component has been instantiated, a property may be adequate. On the other hand, if the type is a fundamental to the definition of the concept, the concept should probably be represented as a type. For example, it probably does not make sense to be able to change a Filter into a Server in a design. However, it is entirely reasonable to change a property on a Filter that describes the type of implementation -- since this decision may be delayed if it is not relevant in early design, or changed.

Components expose their functionality through their ports. A port represents a point of contact between the component and its environment. A component may have several ports corresponding to different interfaces to the component. A port may be used to represent what is traditionally thought of as an interface: a set of operations available on a component. But it may represent a source of requests, an abstract service provided by the component, or represent a source of or destination for data. A port may be very simple, essentially describing the interface as a set of signatures, or it may represent something more complex, like a set of operations that may only be invoked in certain orders. Like components, ports are explicit first class entities with properties.

In this example, a port is used to capture the set of requests that a Server supports through a particular connection:

Component Server = {
// Particular requests available through this port
Port requests = {
   Property validRequests = 
    <
     [name="getCreditReport"; type="secure-request" ];
    >
};
}

Note that components should not be used as a way of arbitrarily representing the vertices that appear in other sorts of software design diagrams. For example, a component should not be used to model a state in a state-transition diagram, or a function in a call-graph diagram. However, a component might be a meaningful way of modeling certain object-oriented abstractions: for example, a component could certainly be implemented using a class in an object oriented programming language.

Connectors

Connectors are the abstract "mortar" between components. They represent communication glue that captures the nature of an interaction between components. Like components, connectors may be used to model a variety of different sorts of interactions under a number of different models. A typical connector might define a synchronization model for communication, a communication protocol, a locking model, or characteristics of a communication channel like bandwidth. An important feature of architectural modeling is the fact that interactions are an explicit first class concept. This is in contrast to many object-oriented design approaches where communication is often implicit in the description of classes/objects/components. Intuitively, connectors correspond to the "lines" in box and line diagrams.

While the Connector construct may used to represent something like a synchronous procedure call between parts of a program, it is frequently used to capture a more complex run-time interaction between components. For example, a connector might embody a particular protocol (like HTTP). It might be used as an abstraction of a set of primitive interactions the define some sort of collaboration. A connector may also be used to represent data flow channel (e.g., a Pipe) or an asynchronous communication medium like an event bus.

A connector includes a set of interfaces in the form of roles, each of which defines a participant in the interaction captured by the connector. A simple role may be seen as an interface to a communication channel, defining an interface to the connector in the same way a port provides an interface to a component. A more complex role may provide a description of a part played in interaction.

Examples of simple roles include the reading and writing roles of a pipe or the sender and receiver roles of a message passing connector. Here, the writing and sender roles correspond to an interface to a data channel that may be written to, the reading and receiver roles to interfaces that may be written from.

As an example of more complex role, consider a concurrent system based on several distributed components. A "Collaboration" connector can be used as a way of capturing the way in which this distribution occurs, which might involve splitting up a task into sub-tasks and compiling the results in a particular way. Here, each role represents some part played in this collaboration and corresponds to the responsibility the role has to perform certain subtasks.

Connectors frequently do not correspond to directly to artifacts of an implementation. They are often implicit in the implementation. For example, the implementation of an event connector might be spread between the code that defines an event loop and the callback the mechanism that associates event handlers with events. Other connector implementations may be implicit in the pattern of interaction between parts of an implementation. For example, the "collaboration" connector described above might actually be embodied by the order in which the components make requests of each other, and which components wait for data from which others.

By providing an explicit Connector concept, Acme provides a way of explicitly documenting the communication in a system.

Like components, connectors includes properties which can be used to describe aspects of the connector. In the case of components, these properties relate to the computation performed by the component. For connectors, the communication "glue" is described.

For example, a property can be used to define the way in which tasks are distributed in the collaboration connector:

Connector collaboration = {
   Role requestor;
   Role slave1;
   Role slave2;
   Property distributionMap =
      "requestor.requestA -> slave1.doX,
                             slave2.doY;
       requestor.requestB -> slave1.doU | slave2.doV"
}

or the set of events broadcast by an event connector:

Connector AnEventBus = {
  // All the events broadcast by this connector
  Property eventList = <
     "WorldWar3Begins",
     "RainToday"
   >;
 
   // Two different listener roles
   Role curiousListener;
   Role selfInvolvedListener;
 
}

Like components, ports, and connectors, roles may also have properties. For example, expanding on the EventBus example above, we might further define the curiousListener and selfInvolvedListener roles to include a property that specifies the events actually available to a port attached to one of these roles:

Connector AnEventBus = {
   // All the events broadcast by this connector
   Property eventList = <
     "WorldWar3Begins",
     "RainToday"
    >;

   // Add Events available to different listeners

   Role curiousListener = {;
      Property events = <
          "WorldWar3Begins",
          "RainToday"
       >;
   };

   Role selfInvolvedListener {;
      Property events = <
          "RainToday"
       >;
   };
 
}

When using Acme, connector concepts like EventBus can be captured as connector types just as component concepts are captured as component types. For more details, the reader is again referred to Representing an Architectural Style in Acme.

Note that connectors should not be used to represent any arbitrary "association" between elements of a design. For example, a set of components may all be implemented as instances of subclasses of some parent class in an object-oriented language. This subclass relationship, as well as other sorts of associations found in object model diagrams should not be confused with connectors. Nor should transitions in state transition diagrams or other non-architectural relations be confused with connectors, which should only be used to represent an interaction or potential interaction between components.

Systems

A description of a system in Acme is assembled from a set of components and connectors, describing the elements of the system and how they interact. Systems are first order entities in Acme, and may also define properties which describe "system-level" attributes.

The value of a property might be derived from graph of components and connectors and represent some emergent property of the system. For example, system fault-tolerance might could be inferred from the fault-tolerance properties of the components and connectors that make up the system. A property might also be used to represent properties of the environment in which the system is operating, or "global" properties that apply to all elements of the system.

The graph of a system -- how everything is connected -- is defined by a set of attachments. Each attachment represents an interaction between a port and some role of a connector. Depending on the ports and roles themselves, the meaning of an attachment might be very simple or more complex. Attachments are first class entities in Acme which may define properties which describe their

The following is a simple example of a client server system with a single client represented in Acme:

System ClientServerSystem = {
   Component server = { 
      Port requests; 
   }; 
   Component client1 = { 
      Port makeRequest; 
   }; 
   Connector req = { 
      Role requestor; 
      Role requestee; 
   }; 
   Attachments {
      server.requests to req.requestor;
   ...client.makeRequest to req.requestee;
   }
 }

Note that a system should not be used to represent the dependencies between source code files, the structure of an executable, classes and associations or other graphs commonly used to describe software designs or implementations. What constitutes "software architecture" isn't precisely defined, but a shared qualitative understanding has begun to emerge. [literature reference].

Representations

A Representationis used to further describe an element in terms of the Acme system construct. This description may correspond to an alternative architectural view of an elemen or a more detailed decomposition of an element into a sub-system. Elements in Acme may have more than one representation, each of which corresponds to a different conceptual view or one of several alternative ways of decomposing an element. The Acme representation does explicitly define the nature of the relationship between an element and its representations; instead, like other concepts in Acme, the exact nature of the representation depends upon the particular modeling context.

Using Representations to Encapsulate Subsystems

For components and connectors, a representation is typically used to represent a more detailed architectural description of the element. Frequently, the parent element may be seen as a signature that describes the "interface" that the element exposes to its environment while the representation system corresponds to the implementation of this interface. Alternatively, the representation may be seen as a more refined depiction of the parent element. The exact nature of this decomposition depends on the particular model being encoded in Acme.

In the simplest case, the representation system may be described in terms of the same component and connector concepts as the element's parent system. For example, consider a component that describes an object that exposes some set of operations. It may be implemented as a set of child component objects, each of which is responsible for handling some subset of the operations. This aggregation can be encoded as an Acme representation of the parent component and has a simple relationship to the parent component.

In other cases, the representation may represent a logical abstraction boundary. A system may be composed of abstract components, each of which has a representation that provides a more detailed or concrete depiction of the component. For example, a "Filter" in a pipe and filter system may have a representation that corresponds to implementation based on procedure calls. In this case, the relationship between element and representation may be more complex.

In either case, there must be some understanding of the relationship between the more detailed view and the component or connector itself. This understanding is captured by the concept of an abstraction map that associates the more abstract component description with the detailed representation. Acme includes a mechanism for representing this abstraction map as a binding list.

For a component, a binding provides a way of associating a port on a component with some port within the representation. For example, consider a component that satisfies two types of requests through two different ports with a representation that defines the implementation in terms of two child components. The first port, easyRequests is bound to a port on the fastButDumbComponent, signifying that requests made to the easyRequests port are actually satisifed by the port "p" on the fastButDumbComponent. This is shown in the following example:

Component theComponent = {
   Port easyRequests;
   Port hardRequests;

   Representation { 
      System details = {
         Component fastButDumbComponent = {
            Port p;
         };
         Component slowButSmartComponent = {
            Port p;
         };
      };
      Bindings {
         easyRequests to fastButDumbComponent.p;
         hardRequests to slowButSmartComponent.p
      };
   };
};

A binding list can be used in a similar way map connector roles to roles on connectors in a connector representation that defines a detailed description of the functionality of a connector.

Note that Acme does not define the precise nature of the relationship between an "outer" and an "inner" port/role.

In some cases, the inner interface may be seen as a refinement of the outer interface (alternatively, an implementation of a specification) where refinement is precisely defined and there is a way of determining whether or not a representation is a valid refinement of an element. In this case, tools are necessary to actually perform validity checks and, in effect, "implement" the additional abstraction map semantics.

In others, there may be a requirement that outer port always be identical to the inner port.

In other contexts, a binding may have a different meaning, and carry with it any number of different constraints on what constitutes a valid binding.

In the case where a binding represents an equilavency or refinement, it is possible to imagine "breaking" the encapsulation boundary by replacing the components and connectors in a system with their representations, thereby creating a more detailed description of the system. In other cases, the abstract map may represent a relationship between systems that are at different levels of abstraction, representing very different concepts. In this case, a substution of this sort may require a more sophisticated transformation.

Representations as Alternative Views

Representations may also be used to present an alternative view of a system. One simple application of a view would be to define a simplified view of a system that filters out certain aspects of the system. For example, in a system with several different sorts of connectors, a particular view may only show one sort of interaction, hiding details of the other interactions.

Note that Acme does not provide any explicit mechanisms for describing the correspondences between different, but related views. Maintaining or checking correspondences under some model would have to be implemented by some new Acme-based tool.

Other Applications of Representations

In other ADLs (e.g., Aesop), constructs called Representations were also be used to encode information like documentation. However, in Acme, it is recommended that Representations only be used to define a logical abstraction boundary: the encapusulation of sub-system as a component or connector. Something like documentation is better represented as a property of an element rather than as a representation. However, it is reasonable to use a representation to describe an implementation that is defined in essentially non-architectural terms (e.g., as a source file), even if there is no explicit abstraction map that relates the implementation to its parent.

Note: Ports and Roles may also have representations. However, these representations should be used with caution since it does not necessarily make sense to describe a port as a system of components and connectors. One application one a port representation is to encapsulate a large set of API calls as a single port. Inside the representation, a set of ports is used to represent individual API calls.

Summary of Core Acme Concepts

The Acme structure provides a set of what are intended as a set universal architectural concepts. The Acme model makes few assumptions about the detailed nature of the elements of design beyond the qualitative definitions of components, connectors, ports, roles and systems. However, these assumptions should be considered inviolate if Acme is to serve as as a common language for describing software architectures.

The core Acme concepts provide an adequate basis for representing most sorts of systems structure. Component concepts from other ADLs can encoded as Acme components with annotations that capture the semantics of the original concepts. The instances of a particular component concept in the original ADL can be represented as a set of instances of Acme.

However, this really isn't enough to make Acme a useful way of representing architectures described in terms of different sorts of models. The core language provides a way of representing instances of concepts from other models, but it does not provide any way of representing the concepts themselves -- that is, no way of specifying the vocabulary of the other model and the syntactic/semantic rules for using that vocabulary.

The Acme Family and Type address this problem. A Family provides a way of describing a family of architectures -- an architectural style -- as a set of element types that make up the vocabulary family and a set of rules encoded as properties, for using the family.

Representing an Architectural Style in Acme

A family includes a collection of component and connector types that embody the vocabularly of a style. When a system is declared it may optionally be assigned a family. This means that the system may include instances of the types defined in the family:

Family PipesAndFiltersFam = { 
   Component Type FilterT = {};
   Connector Type PipeT = {};
};
System APFSystem : PipesAndFiltersFam = {
   Component filter1 : FilterT = new FilterT;
   Component filter2: FilterT = new FilterT;
   Connector pipe : PipeT = new PipeT;
   ...
};

A family may also define infrastructure that must be present in all instances of the family:

Family EventsFam = { 
   Connector Type EventBusT = { ... };
   // Every system must include at least one bus
   Connector theEventBus : EventBusT = new EventBusT;
};
System EventSystem : EventsFam = new EventsFam extended with {
   ...
};

The Acme type model dictates that all instances of a type must define the properties declared by the type, and include any structure mandated by the type. Typically, a family also embodies a set of rules that specify design rules that constrain how designs can be pieced together and declare certain "well-formedness" rules. However, the Acme type model is actually quite weak, which places a burden on someone defining the family to include either language descriptions about these assumptions, or to specify the constraints in some form that can be interpreted by a tool (e.g., Armani).

Element Types

Types in Acme are defined in the same way as instances; they define a prototype that is instantiated by copying its structure. The Acme type model is simple: it states that all instances of a type must include the structure defined by the type.

For properties, this means that if a set of properties are defined for a particular type, any instances must have the same properties. This can be used in two ways. First, as a way of stating what sorts of attributes a particular concept must have. For example, a component type that represents an element capable of listening to and responding to events may require an implementation and an event map which maps events that it receives into operations in the components implementation:

Component Type EventListenerT = {
   Property eventMap;
   Property implementation;
};

Of course, we can do exactly the same thing and define a complementary connector type that represents an event bus. An event bus must at the very least have a broadcaster. Furthermore, every event bus must define the behavior of the bus -- say, how it decides to whom to broadcast events announced by the broadcaster:

Connector Type EventBusT = {
   Role broadcaster;
   Property glue;
};

A particular instance of eitherof thes types would have to provide a value for these properties. The following example shows an instance EventListenerT component type:

Component AListener: EventListenerT = { 
   Property eventMap = 
             < 
               [ name="eventA"; operation="OnEventA"; ],
               ...
             >;
   Property implementation = 
      [file="AListener.cpp"; classname="CAListener"]; 
};

Using the "instance: type1, ...typeN" syntax, we declared that the component AListener conforms to the type EventListenerT. An Acme parser that implements the Acme type system would report an error if the instance did not conform to the type.

A property declared in a type might also include a value In this case, a property is being used to make a particular claim about all about an attribute of all instances of that type. For example, suppose we want to define a more refined version of the EventBus type that has specified broadcast semantics. Namely, it has the simple behavior of re-broadcasting any event received by the connector to all the listeners. Given some language for describing connector behavior, we might define a "glue" property that describes this behavior. We do this by extending the base type EventBusT:

Connector Type EventBroadcastT extends EventBusT with { 
   // Add some listeners
   Roles { l1, l2, l3; } 
   // Define the behavior
   Property glue = 
         "broadcaster.receive(e) --> 
                l1.send(e), l2.send(e), l3.send(e)";
};

All instances of the EventBroadcastT must have a property "glue" with the value given above -- which means every EventBroadcastT connector must have this behavior.

Property Types

The next step in representing a model in terms of Acme family defining a set of property types that can be used to represent the properties that are relevant in a particular semantic model. If performance is being modeled, these might include component response times, latency associated with connectors in the system, or properties that describe the input coming into a system. If behavior is being modeled properties might include encodings of state models that describe the behavior of components.

Acme allows user-defined property types that may be defined in terms of built-in property data types. Built-in types include primitive types: floats, integers, strings; and compound types: records, sequences and sets.

Property Type ExecutableNameT = String;
Property Type ExecutablesListT = Sequence <ExecutableNameT>;
Property Type EventDescriptionT =
     Record [ eventName: String;
              argc:Integer;
              argv=Sequence<String> ];
Property Type MessageTypesT = 
     Enum { change_announcment, command };

<< Why we need families to define a set of compatible vocabulary. Seeing a particular semantic model as an Acme Family. Limitations on Acme families (no design rules). Importance spelling out assumptions made by model, both explicit and qualitative assumptions about what the constructs "mean" in the real world.>>

Defining an Acme Family

In this and following sections, we illustrate how to encode a semantic model in Acme by defining an Acme family for a simple performance analysis tool based on queueing network theory.

This includes a number of steps:

  1. Define model vocabularly. That is, define a family of component and connector types. This includes both a description in Acme as well as a description of the assumptions made about components and connectors under this model.
  2. Define a set of property types to encode properties needed by the model. This includes the Acme description as well as a description of the meaning of the properties under the model.
  3. Define constraints that define what it means for a description to be "well-formed" under this model. These include constraints on individual properties (e.g., a probability must be between 1 and 0), as well as design constraints.

Semantic Model for Performance Analysis

< Give simplified presentation: servers with queues. requests. probalistic trigger of new requests. average case analysis. Show that from analysis point of view, we don't care about connectors -- connectors simply constrain the analysis space.>

Defining Component Types

Under the performance model, a component in a system is capable of satisfing some set of requests. Each request takes a certain amount of time to satisfy -- pending requests accumulate in a component's queue while the component is busy processing requests. The performance analysis sees every component in a system in these terms -- that is, there is only a single component concept in the model.

A component to be analyzed using this model must include properties that describe the set of requests handled by the component and the response time for the component. Given property types to encode these properties (which we will define in a moment), the component type may be defined as:

Component Type Perf_CompT = {
   Property sResponseTime : ResponseTimeT;
   Property sRequests : Sequence < RequestT >;
}

The type definition states that every a component conforming to this type must include these two properties. Instances of this type are expected to define values for these properties.

Defining Connector Types

The original computational model for performance analysis does not include an explicit notion of connection. Components service and generate requests for other components. In adapting the performance tool to Acme, we decided to define a new connector type that represents a request channel between components. Under this model, a component may only make a request of a component to which it is conencted through

Performance Model Property Types

The request type (RequestT):

A component handles some collection of requests. We describe a single request as a record that includes the name of the request, a flag that indicates whether this request originates from this component, and a rate that indicates the rate it which the request is generated if it does originate with this component. Finally, we include a field which describes the list of requests in other components that may be triggered by this request.

Property Type RequestT = 
   Record [
      name : String; 
      rate : Float;
      start : Boolean;
      dest : Sequence<DestinationT>;
   ];

Under the performance model, each type of request made to the component may trigger a request of another component. We associate a probability with each triggered request:

Property Type DestinationT = 
   Record [
      name : String; 
      parent : String;
      start : prob;
   ];

Finally, a component has a single response time represented as a floating point value:

Property Type ResponseTimeT = Float;

Encoding a Semantic Model

After mapping descriptions onto the basic ontology, the next step in developing an Acme based tool is defining a set of property types that can be used to represent the properties that are relevant in a particular semantic model. If performance is being modeled, these might include component response times, latency associated with connectors in the system, or properties that describe the input coming into a system. If behavior is being modeled properties might include encodings of state models that describe the behavior of components.

For example, for the performance model, we might define a component property type that defines the response times for a set of named requests:

Property Type requestT = Record [reqName: String; 
                                 responseTime: Float];

Property Type requestSeqT = Sequence<requestT>;

Defining a Family with a Semantic Model

Once the set of property types are defined, a useful way to specify which properties are required in which context is to define an Acme Family for the model. Then, someone who wishes to use the tools that operate on this semantic model need only derive their design vocabularly from types defined in the tool-specific family.

For example, a performance analysis tool might expect that all components in a system must include a description of the component's response time:

Family perfAnalysisFam = {
        	Component Type paCompT = {
        		Property requests : requestSeqT;
        	}
}

Once the basic encoding is established, there are very likely a number of restrictions on designs that are defined in terms of this semantic model. These might nclude constraints on a particular property values (for example, a property value that represents a probability must be between 0 and 1), or constraints over a set of property values (for example, that a set of probabilities sum to 1). In the performance analysis example, we would probably require that responseTime be non-negative.

Other constraints might specify which information is optional and which information is required. They may also include expectations about the structure of a system, or the presence of detailed representations of components that meet certain constraints. In the performance analysis example, a constraint might be that all components in a system being analyzed must conform to the paCompT type.

Constraints beyond the basic statements about required structure the Acme type system can express cannot be expressed directly in Acme -- it is a tool's responsibility to make sure that none of its assumptions are violated. The TypeManager class in the AcmeLib class libraries provides methods for checking that a particular element satisfies a particular type according to Acme typesystem, but any constraint checking beyond this requires additional tools.

In this case, the description may be annotated with additional properties (or meta-properties attached properties) that express the constraints. These annotations can then be interpreted by an automatic checker tool. In fact, the Armani language includes a powerful general purpose constraint language that can be used to express these sorts of assumptions.

Note: It is extremely important that the model being used by a tool be spelled out explicitly for the tool to be useful. This is expecially important if a description includes annotations for several different semantic models. There is nothing to guarantee that a description that these annotations will actually be consistent.

Partial Translation

For many analysis tools, it only makes sense to do a complete translation of the all the concepts of the semantic model; otherwise, it isn't possible to do a meaningful analysis. For general purpose ADLs, however, it usually isn't feasible to do a complete translation. While it is theoretically possible to encode every description that can be represented in some a particular ADL in Acme, it is frequently useful to do a partial translation. That idea is to only translate those concepts from a source ADL that are requried exploit some particular sub-set of analysis or design capabilities. For example, the UniCon tool provides a wide variety of built-in component and connector concepts but a particular application will probably only need some subset of those concepts.

Beyond providing a description of the graph of a system, a generic Acme description does not provide a particular meaningful depiction of a system, except insofar as choices of names of instances and types suggest meaning to a person looking at the description. In order for an Acme description to provide an useful picture of a system, it must be annotated in some meaningful way.

Summary of Acme Syntax

The following summarizes basic Acme syntax with an overview of Acme semantics. It is not intended to represent complete language documentation. Please see the Acme BNF or other reference materials on Acme for language details.

Design Syntax

A design in Acme is composed of set of family and type declarations followed by a set of system declarations that may refer to those types and families (also note that types descriptions may include references to previously defined types, too).

TYPE-OR-FAMILY-DECLARATION;*
SYSTEM-DECLARATION;*

Element and Property Syntax

Element Syntax

Systems, Components, Connectors, Ports and Roles all include a list of properties and a list of representations and are defined using the same basic syntax:

CATEGORY-NAME ELEMENT-NAME : TYPE-REFS = {
    CHILDREN;
    PROPERTIES;
    REPRESENTATIONS;
};

Or, when instantiating an element type:

CATEGORY-NAME ELEMENT-NAME : TYPE-REFS = 
    new TYPE-REFS extended with {
     CHILDREN;
     PROPERTIES;
     REPRESENTATIONS;
    };

where CATEGORY-NAME is one of: system, component, connector, port, role; ELEMENT-NAME is the name of the element being described; TYPE-REFS is a list of zero more more types (or families, in the case of systems) that the element conforms to; CHILDREN is a set port declarations in components, connectors have role declarations, systems have component and connector declarations; PROPERTIES is a list of property declarations that describe the element; REPRESENTATIONS is a list of representation declarations that describe the representations for this element.

System Syntax: SYSTEM-DECLARATION

A system can be described explicitly: 
System SYSTEM-NAME [ : [ FAMILY-NAME, ]* FAMILY-NAME ] = {
   COMPONENT-DECLARATION;*
   CONNECTOR-DECLARATION;*
   PROPERTY-DECLARATION;*
   REPRESENTATION-DECLARATION;*
   ATTACHMENT-LIST;
};
or by instantiating an existing type and extending it in some way:
System SYSTEM-NAME [ : [ FAMILY-NAME, ]* FAMILY-NAME ] = 
 new FAMILY-NAME extended with {
   COMPONENT-DECLARATION;*
   CONNECTOR-DECLARATION;*
   PROPERTY-DECLARATION;*
   REPRESENTATION-DECLARATION;*
   ATTACHMENT-LIST;
};

where FAMILY-NAME refers to a family declared in the global typespace (or other accessible typespace; see the Acme language documentation for details on visibility of types and families in a design). Types after the colon designate the families to which the system belongs and must conform to in order to remain valid.

where COMPONENT-DECLARATION, CONNECTOR-DECLARATION, PROPERTY-DECLARATION and REPRESENTATION-DECLARATION are defined below,

and where an attachment list is defined as:
Attachments {
    [COMP-NAME.PORT-NAME to CONN-NAME.ROLE-NAME;]*
}

Component Syntax: COMPONENT-DECLARATION

Component NAME [ : [TYPE-NAME,]* TYPE-NAME ] = {
   PORT-DECLARATION;*
   PROPERTY-DECLARATION;*
   REPRESENTATION-DECLARATION;*
}
or, instantiating an existing type: 
Component NAME [ : [TYPE-NAME,]* TYPE-NAME ] = 
 new TYPE-NAME extended with {
   PORT-DECLARATION*
   PROPERTY-DECLARATION;*
   REPRESENTATION-DECLARATION;*
}

Port Syntax: PORT-DECLARATION

Port NAME [ : [TYPE-NAME,]* TYPE-NAME ] = {
   PROPERTY-DECLARATION;*
   REPRESENTATION-DECLARATION;*
}
or, instantiating an existing type: 
Port NAME [ : [TYPE-NAME,]* TYPE-NAME ] = 
 new TYPE-NAME extended with {
   PROPERTY-DECLARATION;*
   REPRESENTATION-DECLARATION;*
}

Connector Syntax: CONNECTOR-DECLARATION

Connector NAME [ : [TYPE-NAME,]* TYPE-NAME ] = {
   ROLE-DECLARATION;*
   PROPERTY-DECLARATION;*
   REPRESENTATION-DECLARATION;*
}
or, instantiating an existing type: 
Connector NAME [ : [TYPE-NAME,]* TYPE-NAME ] = 
 new TYPE-NAME extended with {
   ROLE-DECLARATION;*
   PROPERTY-DECLARATION;*
   REPRESENTATION-DECLARATION;*
}

Role Syntax: ROLE-DECLARATION

Role NAME [ : [TYPE-NAME,]* TYPE-NAME ] = {
   PROPERTY-DECLARATION;*
   REPRESENTATION-DECLARATION;*
}
or, instantiating an existing type: 
Role NAME [ : [TYPE-NAME,]* TYPE-NAME ] = 
 new TYPE-NAME extended with {
   PROPERTY-DECLARATION;*
   REPRESENTATION-DECLARATION;*
}

 

Property and Property Value Syntax

A Property is declared with the following syntax:

Property PROPERTY-NAME [ : TYPE-NAME ] = PROPERTY-VALUE;

where TYPE-NAME referes to some property type that has been defined elsewhere or a built in type (Integer, String, Float, Boolean, Sequence, Set, Enum, Record) PROPERTY-VALUE may be a floating point (e.g., 3.1416 or 6.023e23) , an integer (e.g., 41, 15) boolean value (true, false), a quoted string ("abracadabra") or a name referring to an element of an enumerated type.

A PROPERTY-VALUE may also be compound value based on a record, sequence or set:

A record:

\[ [FIELD-NAME = PROPERTY-VALUE ]* FIELD-NAME = PROPERTY-VALUE \]

A sequence:

< [ PROPERTY-VALUE ]* PROPERTY-VALUE >

A set:

{ [ PROPERTY-VALUE ]* PROPERTY-VALUE }

Representation Syntax

Representation {
   SYSTEM-DECLARATION;
}

Translating from an Acme Representation

It is an important to remember the fact that connectors are first class concepts in the Acme language. This means that the particular semantics of a connector (potentially quite complex) are going to depend on the interpretation of a connector's properties. Consequently, an analysis tool that operates on Acme descriptions based on a model that does not include explicit connectors may need to make simplifying assumptions about the the nature of the interactions in a description being analyzed. Otherwise, the analysis may not be meaningful: for example, a tool that analyzes dependencies between components may not want to consider an event connector with a small fixed set of events as a dependency between a set of components.

Note: Whether or not this event connector represents a dependency is determined by what we mean by dependency. For example, if we're talking about dependency in the context of fault-tolerance the fact that a event connector provides "anonymous" communication may be irrelevant if components connected throught the event connector are running in the same process. In other cases, there may simply not be enough information to make a decision about something like dependency.

Alternatively, a model can be adapted to work meaningfully with more interesting connector. In this case, components may .

It is necessary to spell out the assumptions made by the semantic model explicitly if the tool is to be used effectively with other tools. This is discussed further <....>.


This site's contents 1998
ABLE Group, School of Computer Science
Carnegie Mellon University