To get an overall idea of the structure of a software system is intrinsically difficult. The notion of architecture has proven to be a powerful metaphor for describing the structure of a system, that is the components and their interrelations, in a sufficiently abstract way.
Additional keywords and phrases:
components,
information architecture,
multimedia information retrieval,
feature detection,
portability
A later definition, given in
An exhaustive list of definitions of
the notion of software architecture is given
at the Web site of the Software Engineering Institute (SEI),
of which the url is given in slide Elements.
At the time of writing, the most comprehensive book
concerning software architectures is
The software architecture of a program or computing system
is the structure of the system, which comprises
software components, the externally visible properties
of those components, and their interrelationships.
Note the stress on externally visible properties
here.
It is meant to express that both components and their
relations must be described at a suitable level of
abstraction.
Also note that the phrase relationships between components
may cover quite a lot.
For example, when considering the architecture of a
Web application,
issues such as communication protocols and
document standards must be considered as well.
In addition, the technological infrastructure,
elements of which are given in slide infrastructure,
must also be taken into account.
In the definition or rather collection of definitions, given
by the IEEE Architecture Working Group,
for the terms architect,
architectural description,
stakeholder and viewpoint,
utmost care is taken to suppress the phrase structure.
Instead, the notion of architecting is defined
as defining, maintaining, improving and
certifying proper implementation of an architecture,
and an architecture as a collection of views
relevant to the stakeholders of a system.
Explicit attention for the architecture of a system becomes
increasingly relevant as the complexity of the system grows.
As argued in (class hierarchies) (wrappers) (horizontal, vertical, metadata) (reference models, infrastructure, policies) (standards)
In business applications a distinction can be made
between horizontal components
(covering general functionality, such as GUI-aspects
and document interoperability),
vertical components
(covering domain-specific functionality for one
area of business, such as finance),
and meta-data,
representing the more volatile, knowledge-level
aspects of a system.
What are stored are not the actual multimedia objects
themselves, but structural descriptions of
these objects (including their location) that may be used for
retrieval.
The ACOI model is based on the assumption that
indexing an arbitrary multimedia object
is equivalent to deriving a grammatical structure
that provides a namespace to reason about the object
and to access its components.
However, there is an important difference with
ordinary parsing in that the lexical and grammatical items
corresponding to the components of the multimedia object
must be created dynamically by inspecting the actual object.
Moreover, in general, there is not a fixed sequence
of lexicals as in the case of natural or formal languages.
To allow for the dynamic creation of lexical
and grammatical items the ACOI framework supports both
black-box and white-box (feature)
detectors.
Black-box detectors are algorithms, usually
developed by a specialist in the media domain,
that extract properties from the media object
by some form of analysis.
White-box detectors, on the other hand,
are created by defining logical
or mathematical expressions over the grammar itself.
In this paper we will focus on black-box detectors only.
As an example, look at the (simple) feature grammar below,
specifying the structure of a hypothetical community.
A community has a name.
The actual purpose of this grammar is to select the persons
that belong to a particular community from the input,
which consists of names of potential community members.
Note that the grammar specifies three detectors.
These detectors correspond to functions that are invoked
when expanding the corresponding non-terminal in the grammar.
An example of a detector function is the
personDetector function partially specified below.
The companyDetector differs from the personDetector
in that it needs to inspect the complete parse tree
to see whether the (implicit) company predicate is satisfied.
When parsing succeeds and the company predicate is satisfied
a given input may result in a sequence
of updates of the underlying database, as illustrated below.
The overall architecture of the ACOI framework is depicted in slide acoi.
Taking a feature grammar specification, such as the simple
community grammar, as a point of reference, we see
that it is related to an actual feature detector
(possibly containing an embedded logic component)
that is invoked by the Feature Detector Engine (FDE)
when an appropriate media object is presented for indexing.
The feature grammar and its associated detector
further result in updating respectively the data schemas
and the actual information stored in the (Monet) database.
The Monet database, which underlies the ACOI framework,
is a customizable, high-performance, main-memory database
developed at the CWI and the University of Amsterdam, see
At the user end, a feature grammar is related to
a View, Query
and Report component,
that respectively allow for inspecting a feature grammar,
expressing a query, and delivering a response
to a query.
Some examples of these components are currently implemented as applets
in Java 1.1 with Swing, as described in
The grammar given below corresponds in an obvious way with
the structure depicted in slide midi-structure.
To extract relevant fragments of the melody we
use the melody detector, of which a partial listing is given below.
Parsing a given MIDI file, for example kortjakje.mid,
results in updating the Monet database.
The updates reflect the structure of
the musical information object that corresponds to
the properties defined in the grammar.
In this section we will look at some studies
(executed within the hush framework)
that exemplify a multi-paradigm and multi-lingual approach.
We will first look at the issues that arise when embedding
a logic (that is Prolog) interpreter.
Then we will extend the embedded logic with objects
that may correspond to (native) objects in the host language,
that is C++.
These sections may safely be skipped by readers
not interested in logic programming.
Finally, we will look at how to realize corresponding
collections of objects in (native) C++ and Java.
Traditionally, the information components are often
taken care of by a database that allows
for the formulation of views to obtain (possibly aggregate)
information.
Logic or logic programming is a strictly more powerful
mechanism to deal with information and knowledge.
In our group, we have been studying the use of logic programming
in knowledge-intensive software engineering applications.
The query tag is an element of one of the
text processing filters to provide
hypermedia
support for software engineering described
in
The query example was motivated by the need to maintain
Web pages for the administration of a colloquium
within our group.
The actual knowledge base consists of a list of people
and some rules to determine their affiliations
and email addresses.
The knowledge base is made available by
consulting the file
As concerns the implementation,
the Java fragment below indicates how to access the logic
programming interpreter from a (Java) program.
However, processing the information accessed by url
is still done locally.
So, the next step that may be suggested is to
distribute the knowledge processing itself,
for example by using CORBA.
Native bindings for these languages are available
only on the level of functions.
Even for Java, native methods of an object are defined as
functions that receive a handle to the invoking object.
Given a language with objects, possibly by
adopting an object extension for the languages
without objects,
the problem is to find a proper correspondence
between objects defined in the high-level (script)
language and the native objects defined in C/C++.
In this (sub)section we will first study an extension of Prolog
with objects, and then indicate
a solution to establish a close correspondence
between the (Prolog) objects and their native counterparts.
In the next (sub)section, we will apply this
approach to establish a correspondence between
Java and C++ objects.
Objects (or classes of objects, if you prefer)
are defined by a collection of clauses with a head predicate
of the form class_method(This,...),
specifying the class, method
and object identity parameter.
The actual invocation of the method takes
the form self(This):method(...), where
the colon acts as the familiar dot object access parameter.
Note that the identity parameter (This) does not
occur among the method parameters, but is instead contained
in the object specifier.
Instead of the keyword self,
we may also use a class name to enforce a cast to specific
object type when invoking the method.
In the actual object extension, we also support
object state instance variables, which are however not relevant
for our discussion here.
Object methods may be defined as native by including
a goal of the form native(Handler, Method, Result),
where Handler specifies the (native) handler
to be invoked, Method the actual request,
and Result a variable to store the possible
outcome of the request.
When the Handler parameter is left unspecified,
the handler defined for the object will be taken to
effect the native call.
Let's look at some examples first, to augment this
admittedly concise description.
As outlined in section Reactor, in the hush framework we use an event-based
mechanism to effect foreign language bindings.
This means that the information concerning the native call
is stored in an event object that is passed to a handler,
which invokes the operator function
on the occurrence of an event.
In the code fragment below it is shown how native method
dispatching is taken care of in the operator
function of a C++ kit_object, for which
a corresponding object in Prolog is assumed to exist.
The solution to establishing corresponding object class hierarchies
in Java and C++ that we have adopted relies on
storing a reference to the native C++ object in the Java object
and the conversion of this reference to a smart pointer
encapsulating access to the native C++ object.
Upcalls, which occur for example when Java handlers are invoked
in response to an event, require some additional machinery,
as will be explained shortly.
Each Java class in hush is derived from the obscure
class, which contains an instance variable _self
that may store a C++ object reference, encoded as an integer.
As an example, look at the (partial) Java class description for
kit below.
Each native method must be implemented as a function,
of which the name and signature are fixed by
the JNI conventions, as illustrated below.
In somewhat more detail,
the Java handler object is invoked through the
C++ handler object created in the bind method
of the kit.
The C++ handler is activated when an event occurs, or a Tcl
or Prolog command is given.
Activating the handler amounts to calling the dispatch
method with an appropriate event.
To decide whether the activation must be passed through
to the Java handler object,
the handler::dispatch
method checks for the availability of a smart pointer,
as illustrated below.
The Java smart pointer template class for the Java/C++
binding is derived from the smart pointer
template class introduced in the previous (sub)section.
Architectural choices lead to a particular decomposition
into components and a characterization of
the relation between components.
Classifying groups of software architectures,
we may speak of architectural styles,
which may be defined, following
In this section we will look at architectural styles
for distributed object systems.
Three styles will be introduced, and we will discuss
how these styles are related to technological constraints
imposed by particular component technologies.
Then we will investigate how these styles work out in practice,
by a simple case study in which we explore the consequences
of a particular style for the solution of a specific problem,
in our case the problem of dynamically changing a viewpoint
or perspective in an interactive visualization system.
The distributed objects style comprises software architectures
which consist of software components providing services to client
applications.
Each object is located at a single, fixed place.
Objects on different machines are connected by an ORB (Object Request Broker).
Example technologies supporting this architectural style
are CORBA and DCOM.
The second architectural style
is the (dynamically) downloadable code style.
Classes may be downloaded, to be used on client machines
for instantiating objects, which will run on the client machine.
Example technologies supporting this style are
Java applets, JavaBeans, and ActiveX controls.
Finally, in the mobile objects style,
objects may migrate from host to host, carrying both functionality
and data when they move.
Consequently, mobile objects may communicate
with the local objects of the host they currently reside on.
Mobile objects are a means to implement agents
which wander through a network,
collecting information, negotiating with other agents,
periodically reporting back results to the user who launched the agents.
Technologies supporting the mobile object style
are agent ORBs such as Voyager.
We may regard the location issues as the
prime discriminators of the architectural styles discussed.
Adopting the distributed objects architectural style,
new objects can be added at the server-side, where
they will stay for the remainder of their lifetime.
In contrast, adopting the downloadable code style,
objects may be created at the client-side,
from classes obtained from the server.
Most flexible is the mobile objects style,
which allows for objects to reside on either
server or client machines.
The location properties directly affect
the way that the system is extensible
with new functionality.
Clearly, the mobile code style offers the maximum
of flexibility and functional extensibility.
Nevertheless, as we will discuss shortly,
there are tradeoffs involved.
The maximum in flexibility and extensibility does not
necessarily offer the optimal solution!
Nevertheless, for other parts of the system we were
forced to choose a different solution.
For example, since we use a C++ simulation library for obtaining
the information, we had to use distributed objects (read CORBA)
for making the information available.
And for developing control applets, agent technology
seemed to be a bit of an overkill
so we restricted ourselves to plain Java technology,
that is the downloadable code style.
Generalizing, from our experience we can formulate
the following rules of thumb, listed in slide Guidelines.
distributed objects distributed objects downloadable code downloadable code mobile objects
When a large amount of clients is running
an application on a server,
the server can easily become overloaded.
In this case, moving the processing to the client,
by deploying dynamically downloadable classes,
is a natural solution.
Additionally, when (parts of) an application
are updated often, for example because of changing legislation,
architectures based on downloadable code
are much easier to keep up-to-date.
Clients are then automatically using the latest version
of the available software.
The latter guidelines hold for the mobile objects style
as well.
However, agent technology is much more complex.
And there is, generally, an efficiency price to pay.
So, it is reasonable to introduce agent technology
only when real benefits can be expected from the
migration of objects, for example when the communication
and negotiation with local objects is substantial.
Concluding,
we may state that the adoption of
a style will often be dictated by the technological constraints
a system must satisfy.
Nevertheless, a word of warning is in place here.
Choosing a style may well have consequences for
the overall complexity of the system.
Minimalism is to be strived for, in this respect.
For example, adopting the mobile object style,
that is the use of agents,
may significantly complicate the semantics of the system,
and consequently induce
an increased verification and validation effort.
Unix vs NT
Considering the opportunities for platform-independent
or cross-platform development, we may distinguish between
three approaches:
When it comes to porting applications from Unix to
Windows 95/98/NT,
we may look at AT&T U/WIN, which provides a POSIX
extension for Win32, or Cygnus GNU-win32 support,
which offers many of the GNU utilities and libraries
for the Windows platform.
Similar functionality, as well as support for Motif/X11
GUI capabilities, is offered by the (commercial)
NuTCracker environment.
(A detailed discussion of the technical merits of the
various offerings is beyond the scope of this book.
However, the interested reader may find more information in the online
version of this book.)
The Windows platform is not only popular with end-users
but also with many developers,
who enjoy using the Microsoft Visual Studio suite of tools
and (object-oriented) frameworks such as MFC.
Recently, toolkits have entered the market
that allow for porting Microsoft technologies
(including Visual Basic, ActiveX and MFC applications)
to the Unix platform,
in particular Wind/U from bristol.com
and Mainwin from mainsoft.com.
As a word of warning, these toolkits are still terribly
expensive.
Yet for more information, consult the online version of this book.
For those who wish to develop directly on the Unix platform,
but using Microsoft Visual Studio, there is Tributary,
from bristol.com,
which offers a Unix-server and client-extensions to
Visual Studio.
An excellent book on software architectures is
(C) Æliens
04/09/2009
Elements of architecture
Models and views
Definitions
Technological infrastructure
Distributed object architectures
When considering the architecture of a system,
invariably the technological infrastructure
plays a role.
In particular, when considering client/server
or distributed object systems the choice for
respectively a particular
client and server platform, middleware and
communication infrastructure
may to a large extent determine the characteristics
of the software architecture.
Distributed object patterns
Case study -- multimedia feature detection
In this section, we will look at the indexing and retrieval
of musical fragments.
This study is primarily aimed at establishing the
architectural requirements for the detection of musical features
and to indicate directions for exploring the
inherently difficult problem of finding proper discriminating
features and similarity measures in the musical domain.
In this study we have limited ourselves to the analysis
of music encoded in MIDI, to avoid the technical difficulties
involved in extracting basic musical properties
from raw sound material.
Currently we have a simple running prototype for
extracting higher level features from MIDI files.
In our approach to musical feature detection,
we extended the basic grammar-based ACOI framework
with an embedded logic component to facilitate
the formulation of predicates and constraints over
the musical structure obtained from the input.
The ACOI framework
put name(person) on tokenstream
putAtom(tks,"name",t);
}
...
}
Formal specification
The anatomy of a MIDI feature detector
Implementation status
Queries -- the user interface
Crossing boundaries
subsections:
It is futile to hope for a single language or paradigm
to solve all problems.
Therefore, as our small case study concerning
multimedia feature extraction indicates,
components may differ in how they are realized.
Some components are better implemented using
knowledge-based systems technology,
whereas other components require the use of a systems programming
language such as C++.
Even within components it may be necessary to transgress
the language boundary. For example in Java applications,
wrapping legacy applications or operating system-dependent
code is usually done using the native language interface.
Embedded logic -- crossing the paradigm boundary
Knowledge is a substantial ingredient in many applications.
By knowledge we mean information and rules
operating on that information, to obtain derived information.
As in any (software) engineering effort, maintenance,
that is knowledge maintenance, is of crucial importance.
When we do not avoid the dispersion of knowledge and information
in the actual code of the system, maintenance will
be difficult.
Put differently, for reasons of flexibility and maintenance
we need to factor out the (volatile) knowledge and information
components.
pl.eval("X:assistant(X)");
String res = null;
while ( (res = pl.result()) != null ) {
System.out.println(" Distributed knowledge servers
:- source('www.cs.vu.nl/~eliens/db/se/people.pl').
:- source('www.cs.vu.nl/~eliens/db/se/institute.pl').
:- source('www.cs.vu.nl/~eliens/db/se/property.pl').
:- source('www.cs.vu.nl/~eliens/db/se/query.pl').
void source(in string file);
long eval(in string cmd);
string result(in long id);
oneway void halt();
};
Native objects -- crossing the language boundary
Embedding (script) language interpreters is becoming
standard practice, as testified by the existence
of embeddable interpreters for Tcl,
Perl, Python, Javascript, Java, and Prolog.
Each of these languages also supports calling native
code, that is code written in C or C++,
to allow for accessing system resources or simply
for reasons of efficiency.
Objects in Prolog
In our solution, objects are represented by dynamic
fact clauses, containing a Handler,
indicating how native calls are to be dealt with,
a Class, and object identity ID,
possibly a reference REF to a native C/C++ object,
and a list of Ancestors.
:- use(library(midi:[midi,lily,music,process])).
:- declare(midi:object,class(midi),[handler]).
midi_midi(This) :- // constructor
midi(This):handler(H), // gets Handler from class
declare(H,new(midi(This)),[],[],_).
native methods
midi_read(This,F) :- native(_,This,read(F),_).
midi_analyse(This,I,O) :- native(_,This,analyse(I,O),_).
midi_open(This,F) :- native(_,This,open(F),_).
midi_header(This,M) :- native(_,This,header(M,0,480),_).
midi_track(This,X) :- native(_,This,track(X),_).
midi_tempo(This,X) :- native(_,This,tempo(X),_).
midi_event(This,D,C,M,T,V) :-
native(_,This,event(D,C,M,T,V),_).
C++ bindings
public:
vm(event* e) {
int p = 0;
char* id = e->option("ref");
if (id) {
p = atoi(id);
}
_self = (T*) p;
}
virtual inline T* operator->() { return _self; }
private:
T* _self;
};
Combining Java and C++
The designers of the Java language have created an elegant
facility for incorporating native C/C++ code
in Java applications, the Java Native Interface (JNI).
Elegant, since native methods can be mixed freely
with ordinary methods.
When qualifying methods as native,
the implementer must provide a dynamically loadable library
that contains functions, of which the names and signatures
must comply with the JNI standard,
defining the functionality of the methods.
Nevertheless, the JNI does not provide for
generic means to establish a direct correspondence
between an object class hierarchy in C++
that (partially) implements a corresponding object class
hierarchy in Java.
In this section, we will study how
such a correspondence is realized in the hush framework,
using the Java Native Interface.
public int _self; // peer object pointer
...
};
public kit() { _self = init(); }
protected kit(int x) { }
private native int init();
public native void source(String cmd);
public native void eval(String cmd);
public String result() {
String _result = getresult();
if (_result.equals("-")) return null;
else return _result;
}
private native String getresult();
public native void bind(String cmd, handler h);
...
};
include @lt;hush/hush.h>
include @lt;hush/java.h>
include @lt;native/hush_dv_api_kit.h>
#define method(X) Java_hush_dv_api_kit_##X
JNIEXPORT jint JNICALL method(init)(JNIEnv *env, jobject obj)
{
jint result = (jint) kit::_default; // (jint) new kit();
if (!result) {
kit* x = new kit("tk");
session::_default->_register(x);
result = (jint) x;
}
return result;
}
public:
java_vm(JNIEnv* env_, jobject obj_) {
_env = env_;
_obj = obj_;
_self = self();
}
...
event* dispatch(event* e) { java dispatch
call("dispatch",(int)e);
return e;
}
T* operator->() { return _self; }
T* self() {
jfieldID fid = fieldID("_self","I");
return (T*) _env->GetIntField( _obj, fid);
}
void call(const char* md, int i) { // void (*)(int)
jmethodID mid = methodID(md,"(I)V");
_env->CallVoidMethod(_obj, mid, i);
}
private:
JNIEnv* _env;
jobject _obj;
T* _self;
};
Discussion
Architectural patterns and styles
subsections:
When constructing a system, how does one determine an appropriate
style?
There is no simple answer to this question.
According to From technology to style
We distinguish between three different architectural styles:
This distinction is arbitrary, in the sense that other
distinctions are conceivable.
However, the distinction above is well motivated
by the technology matrix introduced in
section Technology,
as reflected in the feature-based description given
below.
Features
Case study -- perspectives in visualization
To determine which architectural style to use,
or which mix of styles,
is to a large extent determined by practical experience.
Nevertheless, at the end of this section,
we will discuss some rules of thumb that may guide you
in the choice of a particular style.
However, first we will look at an example that
illustrates the consequences of the choice of a particular style.
The example comes from
the distributed visualization architecture (DIVA)
that is explained in more detail in
section DIVA.
DIVA is being developed in cooperation
with ASZ/GAK, the largest
social security provider in the Netherlands,
for experimenting with business visualization to support
decision making.
Our case study focuses on how to support
the sharing of perspectives in visualizing shared information.
For example, one of the users discovers
a new way to display information, uncovering aspects
that would otherwise remain hidden.
This new perspective must then be shared with
other users to coerce them, so to speak,
to this new point of view.
What we will look at, here,
is how the choice of a particular style
affects the solution for the sharing of perspectives
problem.
Distributed objects style
New functionality can be added by creating a new object at the server.
In this case, slide Perspectives(a),
the user discovering a new perspective acts as the server.
Then, assuming that the discovery of a new perspective
is somehow announced to the other users,
a user can connect to the server and request
for that particular perspective (1).
Then, a new visualization object is created (2),
which is made accessible to the user requesting for
the new perspective (3).
Downloadable code style
When a new visualization perspective is discovered,
a class is created that can be downloaded by the
interested user, slide Perspectives(b).
The user connects to the server that contains the new visualization class (1),
downloads the class, and instantiates
a new visualization object (2).
Finally, the information is retrieved from
the shared information server and accordingly visualized (3).
Mobile objects style
Similar as in the downloadable code style,
the new visualization perspective
is downloaded from a server to the client, slide Perspectives(c).
However, in this case, when a user requests for a new
perspective (1), it is not a class, but an object,
actually a clone of the object residing at the server,
that is transferred to the client's machine (2).
The clone, which contains all relevant information,
does not have to contact the shared information server
to update the user's visualization with a fresh viewpoint.
Guidelines for selecting a style
Rules of thumb -- selecting an architectural style
Cross-platform development
Platform dependencies form an important category
of architectural constraints.
In particular, the opportunities offered by
one platform may prohibit the deployment of software
on other platforms.
Nowadays, there are a number of (flavors of) competing
platforms, as there are the Unix flavors (of which Linux is
becoming a strong contender) and the Windows family,
including 3.1 (almost extinct), Windows 95, NT, 98
and (in beta release) Windows 2000.
Unix (for example Sun Solaris and SGI IRIX) has by tradition
a strong position in the server market.
However, Windows NT is growing rapidly in importance.
The Windows family, clearly, dominates the (client) desktop market.
Cross-platform development
Research/GNU
Commercial
As we have discussed previously,
many of the open standards, such as OMG CORBA,
and proprietary standards such as Sun Java,
aim at platform independence.
Also, there are numerous GUI toolkits
available that offer platform-independent support.
A possible disadvantage of this approach is that the
platform specific technology can usually not be profited from.
Discussion
Summary
Elements of architecture
Case study -- multimedia feature detection
Crossing boundaries
Architectural patterns and styles
Cross-platform development
Questions
Further reading