ACC Code & Systems
The INFOD Product Family based on ACC
The Objectives of this Research
- Explore the benefits of applying aspect-oriented programming on designing and implementing a large-scale system throughout the entire software developement cycle.
- Identify as many crosscutting concerns as possible in the INFOD system.
- Draw some lessons learned by applying aspect-oriented programming to a large-scale system.
The Overview of the Information Dissemination (INFOD) System
The INFOD System is a system that uses a notification mechanism to make data sources available to consumers. The system monitors
any state change of data that may generate events. The event may then generate messages, which are created by publishers and sent
to consumers. The INFOD system is composed of different INFOD objects: registry, publishers, consumers, subscribers, subscriptions
and vocabularies.
The INFOD system can be found in more details at the following links:
Design and Implementation of the INFOD System
Before identifying aspects in the system, the core of the system must be identified first. The core of the system contains a limited number
of essential functionalities that serve as a base system to allow further customization. By analyzing the functional requirements derived from
the INFOD base specifications, one has come up with a class diagram describing the core of the INFOD system.
After identifying the core of the INFOD system, the next step becomes to identify the aspects from the system. Two approaches have been
used in this research to identify aspects in the INFOD system:
- Use Case Scenarios Approach
- The approach derived from the Horizontal Decomposition Principles
The
Use Case Scenarios approach is discussed in the book, Aspect-oriented Software Development with Use Cases, written by Ivar Jacobsen and Pan-Wei Ng. To become an aspect, it must satify all of the following criteria:
- The requirement is described in either an alternate flow or a sub-flow.
- The requirement is crosscutting.
The other approach is derived from the
Horizontal Decomposition Principles. These principles are discussed in the paper, Resolving Feature Convolution in Middleware Systems in OOPSLA04. To become an aspect, it must satify any of the following criteria:
- The feature must address a crosscutting concern.
- The feature will likely be upgraded or replaced in the future.
- The feature is optional.
By using these two approaches, 12 aspects are successfully identified from the INFOD system:
- Message Filtering
- Drop Unused INFOD elements
- Disable New INFOD elements
- Error Handling
- Attach SOAP Header
- Remove SOAP Header
- Remove INFOD References
- Authorization Failure
- Logging and Tracing
- Remove Namespaces
- Remote Access
- High Performance
The table below shows the number of lines of code in each aspect versus the number of lines of code in the core system.
| Description |
Number of Lines of Code with Comments |
Percentage of Total Number of Lines of Code |
| Core |
2512 |
46.4% |
| System Aspects |
2905 |
53.6% |
| Attach SOAP Header |
128 |
|
| Authorization |
299 |
|
| Remote Access |
75 |
|
| Detach SOAP Header |
101 |
|
| Disable New INFOD Element |
340 |
|
| Error Handling |
722 |
|
| Message Filtering |
538 |
|
| Logging and Tracing |
18 |
|
| Remove Namespace |
72 |
|
| High Performance |
203 |
|
| Remove INFOD References |
217 |
|
| Remove INFOD Unused Element |
192 |
|
| Total |
5417 |
100.0% |
Aspect Example: Attach SOAP Header
Below is a code snippet of one of the aspects identified in the INFOD system.
message around(): execution($ create$ResponseMessage(...))
{
xmlDocPtr bodyDoc;
xmlNodePtr bodyRootNode;
xmlDocPtr soapDoc;
xmlChar * result;
int size;
message response;
response = proceed();
bodyDoc = parse(response);
bodyRootNode = xmlDocGetRootElement(bodyDoc);
soapDoc = createSoapMessage(NULL, bodyRootNode);
xmlDocDumpMemory(soapDoc, &result, &size);
return result;
}
This code snippet shows an advice in the
Attach SOAP Header aspect. This advice is woven into the execution of
create$ResponseMessage(…). When the INFOD core system calls the
create$ResponseMessage(…) in the
Utility Manager, this advice comes to play. It checks to see if the returning message is already wrapped in a SOAP envelope. If not, the aspect creates a SOAP envelope and puts the message in the SOAP envelope.
To illustrate the crosscutting behavior of this aspect, the diagram below shows where this aspect crosscuts the INFOD core system. The horizontal axis lists the name of the INFOD core system files and the vertical axis is the line number in each file. The line within each bar shows the line number at which the aspect cuts across the core system.
Aspect Example: High Performance
This aspect allows the INFOD system to simultaneously process multiple requests by spawning a new process to handle each request. The INFOD registry is loaded in the shared memory region so that it can be shared and accessed by different processes spawned by the INFOD system.
int around(): execution(int main(...))
{
...
shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | IPC_EXCL);
...
shmptr = (char *) shmat(shmid, 0, 0);
...
// initialize shared registry in the shared memory region.
registryDoc = xmlParseFile("infodRegistry.xml");
xmlDocDumpMemory(registryDoc, ®istryStr, &docSize);
strncpy(shmptr, registryStr, strlen(registryStr) + 1);
...
}
The above code snippet shows an advice in the
High Performance aspect. This advice initializes and then loads the INFOD registry into the shared memory region. This advice is woven into the execution of the
main method of the INFOD system and becomes part of the system initialization routine.
const char * around(): call($ insertRegistry(...)) || call($ updateRegistry(...)) ||
call($ dropRegistry(...)) || call($ $Data$Registry(...)) ||
call($ searchRegistry(...)) || call($ getMetaData(...))
{
...
int sem_id = getSemaphoreId();
semoparg.sem_num = 0;
semoparg.sem_op = -1;
semoparg.sem_flg = SEM_UNDO;
if ( semop( sem_id, &semoparg, 1 ) == -1 )
{
printf("failed to decrement semaphore\n");
exit(-1);
}
result = proceed();
semoparg.sem_num = 0;
semoparg.sem_op = 1;
semoparg.sem_flg = SEM_UNDO;
if ( semop( sem_id, &semoparg, 1 ) == -1 )
{
printf("failed to increment semaphore\n");
exit(-1);
}
return result;
}
The above code snippet is another advice found in the
High Performance aspect. This advice uses a semaphore to avoid multiple processes to access the INFOD registry at the same time. This advice is woven into the APIs in the INFOD core system that access the INFOD registry.
Because many APIs in the INFOD system need access to the registry, the
High Performance module is a crosscutting concern in the INFOD system. This crosscutting behavior can be illustrated by the graph below. The horizontal axis lists the name of the INFOD core system files and the vertical axis is the line number in each file. The line within each bar shows the line number at which the aspect cuts across the core system.
INFOD System Product Family
Since the 12 aspects identified in the INFOD system are independent from each other, it is possible to generate a number of different versions of the INFOD system by weaving different aspects into the INFOD core system. As a result, a product family of the INFOD system is generated as follows:
The left side of the diagram shows the mandatory services provided by the INFOD system. These mandatory services form the core of the system. The right side of the diagram shows the optional features that can be activated in the INFOD system and these are the aspects implemented in the system. After doing all the calculations, there are 1024 different configurations of the INFOD system. The calculations are done using the following combination formula.
Aspect-based Unit Test Strategy
With over 1000 configurations, it is almost impossible to manually test the INFOD system. Since each configuration is treated as a new version of the INFOD system, if the system is being tested in the conventional way, it needs more than 1000 buckets of test cases to test the features offered by the core and the aspects. As more aspects are implemented in the system, the number of test cases grows dramatically and they soon become unmanageable.
To overcome this problem, the aspect-oriented approach is applied to the verification testing of this project as well. There are seven managers in the INFOD core system. Since these managers are independent of each other, they can be tested individually. As a result, there is a test case developed for each manager to verify its functionalities. Since these test cases do not know any aspect in the system, they become the core test cases in the verification tests. For each aspect implemented for the system, there is an equivalent test case aspect implemented for the verification tests. When an aspect is woven into the INFOD core system, the associated test case aspect is brought into the core test cases as well. Testing the INFOD system this way does not need to define an explicit set of test cases for each configuration. Depending on which test case aspects are woven into the core test cases, there is a set of test cases generated specifically for testing a particular configuration. The main purpose of these test case aspects is to verify the functionality provided by their associated INFOD system aspect. They do not interact with other test case aspects in the tests.
Unit Test Examples
The following are some examples of the aspect-based unit test cases.
int around(message *response): call(int compare(message, message))
&& args(*response, message)
{
xmlDocPtr doc;
xmlDocPtr newDoc;
xmlNodePtr rootNode;
int size;
xmlXPathObjectPtr target;
xmlNodePtr bodyMsg;
newDoc = xmlNewDoc("1.0");
doc = parse(*response);
rootNode = xmlDocGetRootElement(doc);
if (rootNode->ns != NULL && rootNode->ns->href != NULL &&
strcmp(rootNode->ns->href, soap_ns) == 0 &&
strcmp(rootNode->name, "Envelope") == 0)
{
// Remove the soap header before returning the response.
target = searchDoc(doc, "//soap:Envelope/soap:Body");
size = (target->nodesetval) ? target->nodesetval->nodeNr : 0;
if (size == 1)
{
bodyMsg = target->nodesetval->nodeTab[0];
xmlDocSetRootElement(newDoc, bodyMsg->children);
xmlDocDumpMemory(newDoc, response, &size);
return proceed();
}
}
printf("TEST FAILED - unittest_attachSoap!! This message has no SOAP envelope!\n");
exit(-1);
}
The above code snippet is an
around advice in the verification aspect which is activated when the
Attach SOAP Header aspect is woven into the INFOD system. The
compare(message, message) is a function in the core test cases to verify the actual response with the expected response. This aspect verifies if there is a SOAP header presented in the actual response. If the response does not contain a SOAP header, it terminates the test with a return code of -1. Otherwise, since the core test cases do not expect a SOAP header, this aspect removes the SOAP header before proceeding the call.
message around(message request): (call(message drop$(message)) ||
call(message unregister$(message)) ||
call(message disassociateVocabulary(message))) &&
args(request)
{
...
response = proceed();
root = parse(request);
if (isCascade(root))
{
xpathExpr = findXPath(root);
...
if (size == 1)
{
eprNode = target->nodesetval->nodeTab[0];
eprValue = xmlNodeGetContent(eprNode);
}
if (eprValue != NULL)
{
size = strlen(xpathExpr) + strlen(eprValue) + strlen("[@epr='']") + 10;
xpathExpr2 = (char *) malloc(sizeof(char) * size);
sprintf(xpathExpr2, "%s[@epr='%s']", xpathExpr, eprValue);
}
target = searchRegistry(xpathExpr2);
size = (target->nodesetval) ? target->nodesetval->nodeNr : 0;
if (size > 0)
{
printf("TEST FAILED: unittest_cascade -
The INFOD element is not removed from the registry.\n");
exit(-1);
}
}
root = parse(response);
target = searchDoc(root, "//infod:Status");
size = (target->nodesetval) ? target->nodesetval->nodeNr : 0;
if (size == 1)
{
statusNode = target->nodesetval->nodeTab[0];
xmlNodeSetContent(statusNode, "COMPLETED");
xmlDocDumpMemory(root, &response, &size);
}
return response;
}
The above code snippet shows the
around advice of the verification aspect which is activated when the
Remove INFOD References aspect is woven into the INFOD system. The main purpose of this verification aspect is to find the INFOD elements that reference the removed element. If these elements are found in the registry, it terminates the test and returns a status code of -1.
pointcut verifySOAPPointCut(message request): (execution(message create$(message)) ||
execution(message replace$(message)) ||
execution(message drop$(message)) ||
execution(message register$(message)) ||
execution(message unregister$(message)) ||
execution(message $associate$(message)) ||
execution(message getMetaData(message))) &&
args(request);
message around(message request): verifySOAPPointCut(request)
{
xmlDocPtr doc;
xmlNodePtr rootNode;
doc = parse(request);
rootNode = xmlDocGetRootElement(doc);
if (rootNode->ns != NULL && rootNode->ns->href != NULL &&
strcmp(rootNode->ns->href, soap_ns) == 0 &&
strcmp(rootNode->name, "Envelope") == 0)
{
printf("TEST FAILED! Found SOAP envelope!\n");
exit(-1);
}
else
{
return proceed();
}
}
The above code snippet shows the
around advice of the verification aspect which is activated when the
Remove SOAP Header aspect is woven into the INFOD system. The main purpose of this verification aspect is to verify if a SOAP header is already removed from the request message before it is handled by INFOD system. If it can find a SOAP header in the request message, it terminates the test with a return code of -1.
Downloads
The full implementation of the INFOD system done in this research can be downloaded from the link below. This contains all the source code and the instructions of how to build a family member of the INFOD system and also the instructions of how to run the tests on a family member of the INFOD system. These instructions are in the
README.
- Thesis: Implementation of INFOD System using Aspect-Oriented Programming [ppt,pdf]