Dr. Faisal Akkawi

Northwestern University | Robert Morris College
Operating Systems | Database Organization | Data Structures | Northwestern CMS
Architecture | Demonstration | Documentation | Downloads | Contact Me

Chapter III
Dynamic Weaver Framework


3.0 Overview of the Dynamic Weaver Framework (DWF)

Aspect oriented software programming provides separation of crosscutting concerns across multiple modules in a software application system.These concerns have to be woven together to provide the over all application system. Weaving can be performed either at compile time or at run time.

Aspect oriented software development provides the designer with a mechanism to achieve separation of crosscutting concerns. When these concerns are designed and implemented independently of each other, they can be woven together at compile time or at run time. Having said that, the designer can think about a design without the need to worry about the concerns,seamlessly. Different designers can design different concerns concurrently without the need for the designer to worry about how these concerns will be woven into the software application, because weaving the concern into the application becomes the problem of the aspect weaver. Now these modules or concerns, because they were designed and implemented in isolation of each other and in isolation from the functionality of the system, can be reused in different software application. And that will increase the usability and productivity. Weaving the aspects with the functional components at design or compile time produces one single monolithic application, where we lose control over individual aspects and it becomes impossible to add or remove aspects at run time. Run time weaving gives us the ability to add or remove aspects during the execution phase of the application, it even makes it possible to alter the source code of an individual aspect that being currently used without the need to hold the system down and recompile it again.

Design or compile time weaving is suitable if we know the following. I) What aspects do the designer need at design time? II) Where should they be woven into the application? These considerations add a heavy burden on the designer’s shoulder, if these requirements are satisfied. i) Aspects may be added and /or removed, ii) Modification of the source code of the aspect becomes possible, iii) All aspects of the software application are not identified at design time then run time weaving becomes a must.

By deferring the aspect weaving to run time, we provide the user with greater flexibility in choosing what aspects to weave and at what time. Researchers can use these features to provide intelligent adaptability in the software system

As is shown in Chapter 2, very few aspect oriented software development techniques provide run time weaving. Our adaptive software system uses an intelligent agent to monitor the changes in the environment and from within the application itself, and react to these changes, the intelligent agent will continuously monitor the surrounding environment and the application itself. Depending on the data collected from the system and the surrounding area the agent will force the system to change its behavior in order to adapt to the changing conditions. These changes must be done smoothly where the do not interrupt the system’s behavior.

Real time software systems or mission critical systems cannot be taken down to make minor changes. Building dynamic adaptability into these systems is a necessity and a great challenge for designers.

In this chapter I will describe how run time weaving of aspects can be achieved using an aspect oriented language independent framework. We also will discuss the benefits of building intelligent adaptive systems.

Dynamic Weaver Framework is an aspect oriented language independent framework. It achieves the separation of concerns by separating the properties of the system such as logging, security, scheduling, etc., from the functionality of the system. Then it weaves them together at run time to achieve the overall application system. The Dynamic Weaver Framework employs Java reflection to use the dynamic proxy in order to achieve dynamic adaptability at run time. In this framework, aspects can be added and removed from the system during run time without the need to take the system down to recompile the code. The framework has the ability to attach/detach any aspect to the running system, and establishes communication between any two modules in the system, or redirect communication from one component to the other.

Recognizing crosscutting concerns when building software systems is crucial to the guarantee of design and code reusability. Identifying the micro-architectural elements that constitute the skeleton of the crosscutting concerns is even more important for the design and reusability of these concerns. The aspect oriented programming methodology provides a mechanism for separation of crosscutting concerns across multiple modules in a software application. These crosscutting concerns that we separated at design time have to be woven together to achieve the overall system. The weaving process can be done at either compile time or at run time. Deferring the aspect weaving to run time, gives us great flexibility in choosing the right aspect to be woven. Software systems can exploit this flexibility in providing an intelligent adaptive concurrent software system.

The Dynamic Weaver Framework enables application to adapt to changes at run time, because components and aspects are independent of each other and they are woven at run time.The components and the aspect in Dynamic Weaver Framework must have a predefined interface, but the users are free to change the class implementation at run time

Designers at design time may have no clue to the type and the number of aspects that their application may need, like debugging, security, and logging.The Dynamic Weaver Framework provides the designer with the capability of activating or deactivating aspects at run time so they don’t have to worry about it at design time.

The DWF is an approach by which aspects and components can be woven, altered, or removed dynamically. This approach is a step toward automating the weaving process at runtime. Recognizing the elements of crosscutting concerns during the design phase is essential to ensure the reusability and reconfigurability of the resulting software system, and the DWF is an attempt to provide these desirable properties when crafting the software systems.

The DWF provides us with the capability of adding and removing aspects as well as point cuts during runtime. This capability is the prime factor that enables us to support reconfigurability in order to adapt to environment changes and cope gracefully with the challenges that may have an impact on the performance degradation, safety, and liveliness properties of the running system. Changes to the software systems may affect the structural or behavioral properties.

The Dynamic Weaver Framework has the following advantages:
  1. We achieve a high level of abstraction since the designer makes the programmer’s job easier by reasoning about individual concerns in isolation from each other.
  2. Concern reuse. Separation of crosscutting concerns provides the software with a loose coupling between the different concerns, achieving the usability of a single concern
  3. No restrictions are imposed by the software application specification. In aspect-oriented software development, software applications must define when they are going to be adapted at run time by specifying the join points. In Dynamic Weaver Framework, the software application is adaptable at run time. Its structure can be inspected and dynamically customized, which means that you do not need to previously specify what might be adapted and at what time.
  4. The Dynamic Weaver Framework is a language independent framework. The system or the software application may be programmed using any language
  5. Application concerns are defined at design level and not at the language or the programming language level, which provides a loose coupling between the design and the implementation and that makes an aspect more reusable.
  6. The Dynamic Weaver Framework achieves a full separation between the functional code and the reusable aspects, which avoids the tangling of application source code, achieving ease of maintenance and adaptation of applications to a new aspect.

The Dynamic Weaver Framework is a framework that can be used to develop a concurrent system with dynamic adaptability. It employs Java reflection to use a dynamic proxy, which is the core part of this framework. In this framework, aspects can be added and removed from the system during runtime, so it becomes easy for a system to adapt to a dynamically changing environment. Figure 3.1 shows the class diagram of the DWF and we will demonstrate the above advantages by implementing the classical bounded buffer producers-consumers problem, but first we will describe each class of the DWF and what each method does.

Figure 3.1: Class Diagram of the Dynamic Weaver Framework



Figure 3.2 we illustrates the architectural specifications of the dynamic weaver framework through the use of the bounded buffer example. In general, the aspect-oriented paradigm has the following elements:

  • Aspect: is the modular representation for a cross-cutting concern; a concern may cross-cut one or more components; security, concurrency, logging are examples of cross-cutting concerns.

  • Core Functional Component: the core functional component can be used as a part of fully operational systems.

  • Weaver: is the engine that weaves aspects along with their respective components.

  • Join-point: determines the granularity of the weaving process; and in the DWF it is at the method call level.

  • Point-cut: an aspect may have different implementations for the different methods; a point-cut represents the specific aspect implementation that will be associated with a specific method(s) on the core functional components. For example different security policies are applied to the different methods defined on the same component.

  • Advice: the actual code that will be executed when the control flow reaches the join-points


Figure 3.2: Architectural Specifications of the Dynamic Weaver Framework


Section two provides a detailed explanation of the AspectWeaver class. Section three describes the AspectRespository class and its methods. Section four describes the AspectTable class. Section five describes the Aspect class and its methods. Section six describes the Pointcut class and its methods. Section seven describes the Advice class and its methods. Section eight describes the AdvicePreference class and its methods. Finally, Section nine describes the Agent class and its role.

3.1 AspectWeaver

The AspectWeaver class takes advantage of the dynamic proxy capability in Java 1.3. The framework structure is depicted in the class diagram in Figure 3.1. Each class uses a dynamic proxy class, which represents the aspect weaver class. The dynamic proxy class is responsible for creating that proxy object of the shared object.  System has a number of aspects, and each aspect has a number of point cuts. The join point in our framework is the method call. Each point cut has an advice class that has two methods beforeAdvice() and afterAdvice() that will be executed when control reaches the join points methods. The semantics of aspect, point-cut, and advice are similar to the ones cited in AspectJ.

The AspectWeaver class weaves classes and their perspective aspects at runtime. The AspectWeaver intercepts the message to the component, i.e., the bounded buffer in our case, and redirects it to the AspectRepository. The AspectRepository keeps the information about the aspect(s) (e.g. scheduling, synchronization, security…) to apply and the order in which they have to be executed. The DWF has a loose coupling between the component and the aspects, because the component and the aspects do not have direct reference between them.

The AspectWeaver interacts with the clients (e.g., producers and consumers using the bounded buffer) and does the actual weaving of aspects while the application is running. All communication between the functional components and the aspects of the system will be accomplished through the AspectWeaver class.

As mentioned above the AspectWeaver is a dynamic proxy, which directly interacts with the clients (the clients are the users who want to deposit items into the buffer using the put() method and the consumers who want to retrieve items from the buffer using the get() method) and does the actual weaving of aspects while the application is running.  Figure 3.3 shows the source code implementation of the AspectWeaver class. Whenever a client calls a method on a bounded buffer whether it be a put() or a get(), the AspectWeaver executes the invoke method in the AspectWeaver class.  The invoke method in turn executes the AspectRepository’s beforeAdvice() method.  If the call is successful, the actual operation that is either a put or a get method will be executed.  After this code is done, the AspectWeaver will invoke the afterAdvice() method in the AspectRespository. The role of afterAdvice() and  beforeAdvice()  methods are covered in detail in section x.

public class AspectWeaver implements InvocationHandler {
private AspectRepository _ar;
private Object _buf;

public static Object newInstance(Object buf, AspectRepository ar) {
return Proxy.newProxyInstance(buf.getClass().getClassLoader(),
buf.getClass().getInterfaces(),
new AspectWeaver(buf, ar));
}
private AspectWeaver(Object buf, AspectRepository ar) {
_ar = ar;
_buf = buf;
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
Object result = null;
try {
_ar.beforeAdvice(_buf, m, args);
result = m.invoke(_buf, args);
_ar.afterAdvice(_buf, m, args, result);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
return result;
}
}

Figure 3.3: AspectWeaver.java

Later on in this chapter we demonstrate how this loose coupling can be achieved by considering an example in which we would like to add a debugging concern into the bounded buffer at one time.

In summary, The AspectWeaver class plays two roles in the framework. First, it intercepts every method called by the system and redirects communication to the AspectRepository by executing or invoking the beforeAdvice() method of the AspectRespository. Second, The AspectWeaver class is responsible for weaving aspects with components at run time, as will be explained in detail later on in this chapter.

3.2 AspectRepository


3.2.1 The Role of the AspectRepository

As mentioned in the AspectWeaver class, every method call to the component will be intercepted by the AspectWeaver, the AspectWeaver then delegates the responsibility to the AspectRepository to evaluate a set of conditions by invoking the beforeAdvice() method in the AspectRepository. The AspectRepository will, in turn, evaluate all required aspects of the calling method. Upon successful return of the beforeAdvice() method, a value RESUME will be returned to the AspectWeaver in which the AspectWeaver then invokes the method on the component itself, i.e., the put() or the get() method. Upon the completion of the execution of the method The AspectWeaver will invoke the afterAdvice() method in the AspectRespository.

The AspectRespository has the following information:

  1. AspectTable: AspectTable was implemented using a Vector. It contains all aspects to be employed in the system. The AspectTable contains an index, Aspect name, and the object aspect() itself. AspectTable will be covered in section x.
  2. Current policy name: Current policy name contains the class name of the current scheduling policy.
  3. Queue for bufferHistory: The queue for bufferHistory is not an inherent part of the Dynamic Weaver Framework. It just provides necessary data for displaying graphs on the screens.
  4. Table of available policy names.

The AspectRespository contains the following public methods as shown in Figure 3.4.

import java.lang.reflect.*;

public class AspectRepository {

public void addAspect(String aspect_name, String pointcut_name,String method_name, AdviceIF adv);
public void removeAspect(String aspect_name, String pointcut_name, String method_name);
public void removeAspect(String aspect_name);
public void beforeAdvice(Object obj, Method m, Object[] args);
public void afterAdvice(Object obj, Method m, Object[] args, Object result);
public synchronized void changePolicy(String className) {
-
-
}

Figure 3.4: AspectRepository.java

addAspect(): The addAspect() method in the AspectRepository is used to add an aspect to the system by inserting it into the aspect table as shown in Figure 3.5. Four arguments are required. The first three are used to pinpoint the location of the advice to be added. The fourth argument is the reference to the advice object to be added to the system. The AspectRepository calls the same-named method of the AspectTable. If there already exists an advice at the same join point specified by the first three arguments, the old advice will be replaced with the new one. The client of this method can be an intelligent agent.

public void addAspect(String aspect_name, String pointcut_name, String method_name, AdviceIF advice){
aspectTbl.addAspect(aspect_name, pointcut_name, method_name, advice);
}

Figure 3.5: AspectRepository.addAspect()

removeAspect(): The removeAspect() method of the AspectRepository is for removing a certain advice from the system. Three arguments are required to pinpoint the location of the advice to be removed. The AspectRepository simply calls the same-named method of the AspectTable.

public void removeAspect(String aspect_name, String pointcut_name, String method_name){
aspectTbl.removeAspect(aspect_name, pointcut_name, method_name);
}

Figure 3.6: AspectRepository.removeAspect()

We added two more methods, which are addPolicy(), and removePolicy() to give the user the capability of manually controlling the system if the user decides to disable the agent from the system. The one changing policy method can be called either by the agent or manually by the user, to replace a policy with another.

add Policy (): As shown in the Figure below, the addPolicy() method adds an entry to the policy name table which contains all the class names of the available policies. This policy name table is used to display the available policy names on the screen. The AspectRepository may not be the best place for storing this information. It may be moved to some other place in the future.

private Vector _policies = new Vector();

public void addPolicy(String policyName) {
if (!_policies.contains(policyName))
_policies.add(policyName);
}

Figure 3.7. AspectRepository.addPolicy()

removePolicy(): The removePolicy() method of the AspectRepository does not remove any policy from the system. It only removes an entry from the table that contains the names of the available policies. It is shown in the Figure below.

public void removePolicy(String policyName) {

if (_policies.contains(policyName)) { int i = _policies.indexOf(policyName);
_policies.remove(i);
}
}

Figure 3.8. AspectRepository.removePolicy()

changePolicy() : the changePolicy() method of the AspectRepository gives us the capability to switch between different aspects as shown in Figure 3.9. This method is used by the agent, i.e., if the agent finds that a new policy needs to be loaded into the system, it calls this method. The class name of the new policy is passed to this method. If the current policy is the same as the new one, there is no need to load the new policy. After loading the given policy class, the AspectRepository replaces the current policy with the new one by calling its own addAspect() method. It also updates the name of the current policy appropriately.

public synchronized void changePolicy(String className) { if (className.equals(currPolicyName)) return; AdviceIF newPolicyAdvice = loadAdvice(className); if (newPolicyAdvice == null) return; this.addAspect("SCHED", "DEFAULT", "put", newPolicyAdvice); this.addAspect("SCHED", "DEFAULT", "get", newPolicyAdvice); currPolicyName = className; } Figure 3.9. AspectRepository.changePolicy()

The above five methods addAspect(), removeAspect(), addPolicy(), removePolicy(), and changePolicy() can be called by either. I) the intelligent agent who dynamically and without interacting with the user has the capability of monitoring the system to add and/or remove aspect at run time to adopt to the environmental, or ii) the user to manually add/remove aspects like scheduling, security, logging, etc. 

beforeAdvice(): the beforeAdvice() method is invoked by the AspectWeaver before the actual operation on the shared object is performed

afterAdvice(): the afterAdvice() method is invoked by the AspectWeaver after the actual operation on the shared object is performed

The above two methods beforeAdvice() and afterAdvice() are covered in more detail elsewhere.

The AspectRespository makes sure that only one task at a time can access each shared object. While a task is performing an operation on the shared object, all the other tasks must stay blocked outside the shared object. After the task is done with the operation, the next task to be unblocked will be selected by the current scheduling policy. Then, the AspectRepository is used to unblock the next task to access the shared object. This synchronization is done by using the synchronized modifier and the two methods wait() and notifyAll() as shown in Figure 3.10 and Figure 3.11 because the two methods and beforeAdvice() and afterAdvice() are synchronized, it is guaranteed that at most one active task can be executing these methods at the same time. Inside the beforeAdvice(), if certain conditions are not met the task will be blocked after the this.wait() method is executed. The blocked tasks will only be awakened by the this.notifyAll() method inside the afterAdvice() method. While this task remains blocked, other tasks can possibly enter the same beforeAdvice() method one at a time. The task will be blocked if certain conditions are not satisfied or it will be allowed access to the shared object.

The task which is allowed access to the shared object will return from the beforeAdvice() method and then execute the operation on the shared object. Eventually, it will finish the operation on the shared object and execute the afterAdvice() method of the AspectRepository. Inside this method the afterAdvice() method of the current scheduling policy is executed and will select the task to be scheduled next. Then the AspectRespository awakens all the tasks by calling this.notifyAll(). The awakened tasks will check if they can be allowed to access the shared object. At most one task can be qualified to access the shared object. And this will repeat forever.

3.2.2 The beforeAdvice() Method of the AspectRepository

Suppose a client wants to execute the put or get method of the bounded buffer. The dynamic proxy which is the AspectWeaver class executes the beforeAdvice() method of the AspectRespository first. Eventually, the beforeAdvice() method will return, and then the actual put or get of the bounded buffer will be invoked. Inside the advice of the AspectRespository, all the beforeAdvice() method of every registered aspect will be invoked. Some of the possible aspects are the synchronization aspects, performance aspects (for task scheduling), logging aspect, and security aspect. In fact, the synchronization aspect must always be considered prior to the performance aspect in order for proper synchronization and task scheduling to be done. Current implementation of the Dynamic Weaver Framework had the synchronization as the first aspect and the preference as the second. Details of how the method of the registered aspect will be invoked are shown in Figure 3.10. Both the beforeAdvice() and the afterAdvice() methods of the AspectRepository are synchronized methods, i.e., at most one thread can execute the code of these methods at one time. If the beforeAdvice() method of any registered aspect returns the constant value of BLOCK, the current task or thread will be enqueued and then blocked by executing this.wait() as shown in the code. The task will be awakened and dequeued by the call this.notifyAll(), that can be found in the afterAdvice of the AspectRespository. If the task is the wanted one it will execute the beforeAdvice() of the AspectRespository and start all over again. Otherwise, it will be enqueued and blocked again. If every aspect in the beforeAdvice() method returns the value RESUME then the calling method will be allowed to enter the bounded buffer. And after completing the task of executing the method on the buffer the AspectWeaver will call the afterAdvice() method in the AspectRespository. The afterAdvice method is described below.

public synchronized void beforeAdvice(Object obj, Method m, Object[] args) {
Task task = new Task(m.getName()); // init task info
int i, rc;
Aspect a;
Preference policyAdvice;
boolean mustWait = false;

/** Invoke every beforeAdvice() method of each aspect.*/

for(i = 0; i < aspectTbl.size(); i++) {
a = aspectTbl.getAspect(i);
mustWait = false;
if (occupied)
mustWait = true;
else {
rc = a.beforeAdvice(obj, m, args);
if (BLOCK == rc)
mustWait = true;
}

while (mustWait) {
policyAdvice = (Preference)
aspectTbl.getAspect("SCHED").getPointcut("DEFAULT").getAdvice("get");
policyAdvice.enqueue(task);

try { this.wait(); } //------------- WAIT
catch(InterruptedException iex) { /* Ignore iex */ }
policyAdvice.dequeue(task);

if (task.equals(taskToBeAwakened)) {
mustWait = false; // Exit this while loop and
i = -1; // Force all the aspects to be checked again.
}
}
}
occupied = true; // Only one task at a time is allowed
// entrance to the bounded buffer.
}

Figure 3.10: AspectRepository's beforeAdvice method



3.2.3 The afterAdvice() Method of the AspectRepository

After the actual put() or get() of the bounded buffer is done, the afterAdvice() method of the AspectRepository will be invoked. Inside the AspectRepository’s afterAdvice, all the afterAdvice() method of every registered aspect will be called. Details of how the methods of the registered aspects will be invoked are shown in Figure 3.11. The scheduling aspect, which deals with the task preference, will pick the task to be allowed the entrance to the bounded buffer next.

public synchronized void afterAdvice(Object obj, Method m, Object[] args, Object result) {

int i, rc;
Aspect a;

/** Invoke every afterAdvice() method of each aspect in reverse order.*/

for(i = aspectTbl.size() - 1; i >= 0 ; i--) {
a = aspectTbl.getAspect(i);
if (a.getName().equals("SCHED")) {
taskToBeAwakened = (Task) a.afterAdvice(obj, m, args, result);
if (taskToBeAwakened != null) {
this.notifyAll(); // <--------------- Wake up.
}
} else {
a.afterAdvice(obj, m, args, result);
}
}
occupied = false;
}

Figure 3.11: AspectRepository's afterAdvice method



3.2.4 Changing the Current Scheduling Policy

Policy changing is required or requested by either the intelligent agent or manually by the user. This is done by calling the changePolicy() method in the AspectRespository. Because this method is synchronized, only one thread can enter this method at a given point of time. If the new policy that is requested to be loaded is the same as the current policy the system will ignore the request. Otherwise, the class loader will load the new policy into the system and update the current policy as shown in Figure 3.12.

public synchronized void changePolicy(String className) {

if (className.equals(currPolicyName)) return;
AdviceIF newPolicyAdvice = loadAdvice(className);
if (newPolicyAdvice == null) return;
this.addAspect("SCHED", "DEFAULT", "put", newPolicyAdvice);
this.addAspect("SCHED", "DEFAULT", "get", newPolicyAdvice);
currPolicyName = className;
}

private AdviceIF loadAdvice(String className) {
AdviceIF advice = null;
try {
Class myClass = Class.forName(className);
advice = (AdviceIF) myClass.newInstance();
System.out.println("Loading Advice <" + className + "> in AspectRepository");
} catch (Exception ex) {
System.out.println("Error loading Advice <" + className + "> in AspectRepository");
}
return advice;
}

Figure 3.12: Changing the Policy



3.3 Tasks and TaskQueue

The Aspect Repository needs to be tailored to each specific application. For the purpose of mutual exclusion and condition synchronization to the shared object, the AspectRepository keeps track of each task that accesses the shared object and maintains queues for tasks. The number of task queues depends on the application. In our producer-consumer example, two task queues are needed. One is for producers and the other is for consumers. 

A task is different from a thread or a process. In the Dynamic Weaver Framework, a task is an object that is created when a thread enters the beforeAdvice() method of the AspectRepository as shown in Figure 3.10. It is destroyed when the thread exits the afterAdvice() method of the AspectRepository, although it is not shown explicitly in Figure 3.5. The task contains only the minimal information that is needed for task scheduling – task id, the name of the bounded buffer’s method to be invoked, and the arrival time of the task. Figure 3.13 shows the details of the Task class.

import java.util.Date;

public class Task {

private String _task_id = null;
private String _method_name = null; // name of the shared obj's method invoked by this task
private Date _arrivalTime = null; // This will be used for FIFO ordering

Task(String method_name) {
_method_name = method_name;
_task_id = Thread.currentThread().getName();
_arrivalTime = new Date();
}

public String getName() { return _task_id; }
public String getMethodName() { return _method_name; }
public Date getArrivalTime() { return _arrivalTime; }
public String toString() { return _task_id; }
}

Figure 3.13: Task.java

As explained previously, for this bounded buffer/ producer consumer problem, two task queues are used. The two task queues are located in the scheduling advice objects. When considered separately from each other, each queue is maintained on a First-In First-Out basis regardless of the scheduling policies considered in this example program. Those policies are FIFO, Prefer-Get, and Prefer-Put. But when we consider the two queues together, they are not always treated on a FIFO basis. Queue is implemented as a Vector. When a aask is enqueued to a TaskQueue, it is always added to the end of the queue. When the dequeue method() is called, always the first Task in the queue is removed from the queue.

import java.util.Vector;
import java.util.Date;

public class TaskQueue {

private Vector _tasks = new Vector();
private String _name;

TaskQueue(String name) { _name = name; }
public boolean isEmpty() { return _tasks.size() == 0; }
public int size() { return _tasks.size(); }
public void enqueue(Task task) { _tasks.add(task); }

public Task dequeue() {
if (_tasks.size() == 0) return null;
return (Task) _tasks.remove(0);
}

public boolean dequeue(Task t) {
if (_tasks.size() == 0) return false;
return _tasks.remove(t);
}

public Date getArrivalTimeOfFirstTask() {
if (_tasks.size() == 0) return null;
return ((Task) _tasks.firstElement()).getArrivalTime();
}
}

Figure 3.14: TaskQueue.java



3.4 History Queue

In order to be able to display a graph that shows the number of tasks in each queue and the number of items in the bounded buffer over a period of time, we need to keep those numbers somewhere inside the system. The AspectRepository is the most convenient place for it. The buffer history data are stored in a queue named HistoryQueue and the AspectRepository extends Observable. Every time a task accesses the shared object, the relevant information is enqueued to the HistoryQueue, and the Observers will be notified to refresh the screen to show the current contents of HistoryQueue. Although the HistoryQueue is contained in the AspectRepository, it is not an inherent part of the Dynamic Weaver Framework. It is just here to store data for graphic display. Figure 3.15 and 3.16 show the HistoryElem and HistoryQueue classes.

public class HistoryElem {
public int prod_Q_len = 0; /* number of producers in queue */
public int cons_Q_len = 0; /* number of consumers in queue */
public int item_cnt = 0; /* number of items in buffer */
public int policy = 0; /* scheduling policy code */
}

Figure 3.15: Task.java



import java.util.Vector;

public class HistoryQueue {
private Vector _history_P = new Vector(); /* producer */
private Vector _history_C = new Vector(); /* consumer */
private Vector _history_I = new Vector(); /* item count */
private Vector _history_S = new Vector(); /* scheduling policies */

HistoryQueue() { }

public synchronized boolean isEmpty()
// Returns true if the queue is empty.
return _history_P.size() == 0;
}

public synchronized void enqueue(int p_cnt, // num of producers
int c_cnt, // num of consumers
int i_cnt, // num of items
String policyName // policy
) {
if (_history_P.size() > 1000) dequeue();

_history_P.add(new Integer(p_cnt));
_history_C.add(new Integer(c_cnt));
_history_I.add(new Integer(i_cnt));
_history_S.add(policyName);
}

public synchronized void dequeue() {
if (_history_P.size() == 0) return;
_history_P.remove(0);
_history_C.remove(0);
_history_I.remove(0);
_history_S.remove(0);
}

public synchronized int size() {
// Returns the size of the queue.
return _history_P.size();
}

public synchronized void clear() {
_history_P.clear();
_history_C.clear();
_history_I.clear();
_history_S.clear();
}

public int getPcnt(int index) { return ((Integer) _history_P.get(index)).intValue(); }
public int getCcnt(int index) { return ((Integer) _history_C.get(index)).intValue(); }
public int getIcnt(int index) { return ((Integer) _history_I.get(index)).intValue(); }

public String getPolicy(int index) { return (String) _history_S.get(index); }

public Vector getHistoryP() { return _history_P; }
public Vector getHistoryC() { return _history_C; }
public Vector getHistoryI() { return _history_I; }
public Vector getHistoryS() { return _history_S; }
}

Figure 3.16: Task.java



3.5 AspectTable

The AspectTable is implemented using a Vector and it is present in the AspectRespository class. It contains all aspects that have been registered in the framework. The AspectTable is contained in the AspectRepository, thus it will be only used by the AspectRepository. Initially the AspectTable is empty; you load the AspectTable with an aspect by calling the addAspect() method in the AspectRespository. The addAspect Table will insert any aspect name that you provide to the system into the AspectTable along with an index and an object that contains the actual aspect. Similarly removing an aspect from the AspectTable can be done by calling the removeAspect() method in the AspectRepository class. As shown in Figure 3.17, the AspectTable contains three columns; the first column is the index column. The index column allows us to search and retrieve an aspect by a unique index, the second column contains the name of the aspect and that allows us to search for an aspect by a particular name. And that might seem to a user to be a duplicate information, but we will show later in this section that the AspectRepository class may need to search the AspectTable looking for aspects by their index, where the aspect class might search the AspectTable looking for a specific aspect based on its name. The third column contains a hash table that maintains the actual aspect. The AspectTable has the following methods as shown in Figure 3.17.

public class AspectTable {
private Vector _aspects = new Vector();
public boolean hasAspect(String aspect_name);
public Aspect getAspect(String aspect_name);
public Aspect getAspect(int index) {
public boolean addAspect(String aspect_name, String pointcut_name, String method_name,
AdviceIF advice){
private void removeAspect(String aspect_name);
public void removeAspect(String aspect_name, String pointcut_name, String method_name);
}

Figure 3.17: The AspectTable Class

hasAspect(): the haveAspect() method of the AspectTable returns whether a particular aspect is currently present in the aspect table or not. This particular method will be called by the addAspect() method in the AspectRespository to find out if an aspect exists. If it does it will ignore the method, else it will add the aspect to the aspect table as shown in Figure 3.18

public boolean hasAspect(String aspect_name) {

int i;

if (_aspects == null) return false;
if (_aspects.isEmpty()) return false;
for(i = 0; i < _aspects.size(); i++) {
AspectIF a = (AspectIF) _aspects.get(i);
if (a.getName().equals(aspect_name))
return true;
}
return false;
}

Figure 3.18: AspectTable.hasAspect()

getAspect(): The AspectRepository uses the getAspect() method of the AspectTable to retrieve the actual aspect from the AspectTable. There are two getAspect() methods in the AspectTable class. One requires an index to be passed as an argument it returns a reference to the aspect in the specific index position from the aspect table. If the index value is illegal, it returns null. The other requires an aspect name. It returns a reference to the aspect with the given aspect name from the aspect table. It returns null if the specified aspect does not exist.

public Aspect getAspect(int index) {

int i;
if (_aspects == null) return null;
if (_aspects.isEmpty()) return null;
if (_aspects.size() <= index || index < 0) return null;
return (Aspect) _aspects.get(index);
}

public Aspect getAspect(String aspect_name) {
int i;

if (_aspects == null) return null;
if (_aspects.isEmpty()) return null;
for(i = 0; i < _aspects.size(); i++) {
Aspect a = (Aspect) _aspects.get(i);
if (a.getName().equals(aspect_name))
return a;
}
return null;
}

Figure 3.19: AspectTable.getAspect()

addAspect(): The addAspect method of the AspectTable class is used to insert a particular piece of advice into the system. This method is used by the AspectRepository. This public method receives three arguments – the three of them are required to locate the exact position where the advice is to be added. The fourth one is a reference to the advice object. If the aspect with the given aspect_name does not exist, a new one will be created and added to the aspect table. If the pointcut with the given pointcut_name is not registered, a new one will be created and added to the pointcut table inside the given aspect object. If the reference to an advice for the given method_name inside the given pointcut of the given aspect is already there, the reference will be overwritten with the new advice reference.

public boolean addAspect(String aspect_name, String pointcut_name, String method_name, AdviceIF advice){

Aspect a = this.addAspect(aspect_name);
Pointcut p = a.addPointcut(pointcut_name);
p.addAdvice(method_name, advice);
return true;
}

Figure 3.20: AspectTable.addAspect()

removeAspect(): The role of this method in the AspectTable is to remove an aspect of the given name from the aspect table. Three arguments are passed to this method. They represent the name of the aspect, the name of the pointcut, and the name of the method resprectively. The specific advice of the given aspect, point cut, and the method is removed.

public void removeAspect(String aspect_name, String pointcut_name, String method_name){

Aspect a = this.getAspect(aspect_name);
if (a == null) {
System.out.println("No such aspect found : " + aspect_name);
return;
}

Pointcut p = a.getPointcut(pointcut_name);
if (p == null) {
System.out.println("No such pointcut found : " + pointcut_name);
return;
}

AdviceIF adv = p.getAdvice(method_name);
if (adv == null) {
System.out.println("No such method found : " + method_name);
return;
}

p.removeAdvice(method_name);
}

Figure 3.21: AspectTable.removeAspect()

AspectRepository contains the AspectTable which has all the registered aspects in it. Initially, the table is empty, i.e. no aspect is registered. Adding and removing advice from a certain aspect can be done by calling the corresponding methods of the AspectTable. Figure 3.22 shows the conceptual structure inside the AspectTable. When the addAspect() of the AspectRepository is called, an aspect object, a pointcut object, and a joinpoint are needed to store a reference to the specified advice. If they are already there, they will be used as they are. If not, each new object will be created appropriately. When the removeAspect() method of the AspectRepository is called, the join point entry at the specific location will be set to null. Please note that no object along the way from the AspectTable to the join point will be actually removed.



Figure 3.22: How the Advice Is Registered

So, the real job of managing the aspects is delegated to the AspectTable, a summary of which is shown in Figure 3.23.

public class AspectTable {

private Vector _aspects = new Vector(); // contains instances of Aspect.
public int size();
public boolean hasAspect(String aspect_name);
public Aspect getAspect(String aspect_name);
public Aspect getAspect(int index);
public Aspect addAspect(String aspect_name);
public boolean addAspect(String aspect_name, String pointcut_name, String method_name, AdviceIF advice);
public void removeAspect(String aspect_name, String pointcut_name, String method_name);
}

Figure 3.23: AspectTable’s Public Methods



3.6 Aspects

An AspectTable can contain multiple Aspect objects. And each Aspect can contain multiple Pointcuts.

We represent an aspect object as a hash table that exist in the three columns of the AspectTable. Every time we add an aspect to the AspectTable we end up instantiating a hash Table and inserting it into a new row in the AspectTable inside the AspectRepository. And each aspect can contain multiple Pointcuts. Every Pointcut may contain one or more join points. The aspect contains the following methods.

addPointcut(): The method addPointcut() of the Aspect class is used for adding a new advice. The Aspect class has two public addPointcut() methods. The first one adds a pointcut with the given pointcut_name to the pointcut table. If the pointcut is a new one, it is added to the pointcut table. It returns the pointcut with the given pointcut_name. The second method uses the first one. It requires three arguments – String pointcut_name, String method_name, and AdviceIF advice. In the second addPointcut() method, the advice for the given pointcut and method is added to the current aspect object. If the pointcut with the given pointcut_name is not registered, a new one will be created and added to the pointcut table inside the aspect. If the reference to the advice for the given method_name inside the given pointcut of the given aspect is already there, the reference will be overwritten with the new advice reference.

public Pointcut addPointcut(String pointcut_name) {

if (!_pointcuts.containsKey(pointcut_name)) {
_pointcuts.put(pointcut_name, new Pointcut(pointcut_name));
System.out.println("Pointcut added: " + pointcut_name);
}
return (Pointcut) _pointcuts.get(pointcut_name);
}

public void addPointcut(String pointcut_name, String method_name, AdviceIF advice){
Pointcut p = this.addPointcut(pointcut_name);
p.addAdvice(method_name, advice);
}

Figure 3.24: Aspect.addPointcut()

removePointcut(): The removePointcut() method of the Aspect class removes the advice from a given Aspect. It receives a String that contains the name of the pointcut to be removed. If the Aspect contains the given pointcut, the point cut is removed. Otherwise, nothing happens.

public void removePointcut(String pointcut_name){

if (_pointcuts.containsKey(pointcut_name)) {
_pointcuts.remove(pointcut_name);
}
}

Figure 3.25: Aspect.removePointcut()

beforeAdvice(): The beforeAdvice()method of Aspect is invoked inside the AspectRepository’s method with the same name. The beforeAdvice() requires three arguments – Object obj, Method m, and Object[] args. The first argument, obj, is a reference to the shared object, for example, the bounded buffer. The second argument, m, is the shared object's method to be invoked if all the beforeAdvice() is successful. The final argument, args, is an array that contains all the arguments passed to the method m.

public int beforeAdvice(Object obj, Method m, Object[] args){

if (_pointcuts.containsKey("DEFAULT")) {
Pointcut p = (Pointcut) _pointcuts.get("DEFAULT");
return p.beforeAdvice(obj, m, args);
}

return AspectRepository.RESUME;
}

Figure 3.26: Aspect.beforeAdvice()

afterAdvice(): if you need to add text and code, the afterAdvice() method of Aspect is called by the AspectRepository’s afterAdvice() method. It requires four arguments. The first three of them are the same as the arguments in the beforeAdvice() method explained above. The last argument, Object result, is the Object value returned by the shared object’s method, for example, put() or get().

public Object afterAdvice(Object obj, Method m, Object[] args, Object result){

if (_pointcuts.containsKey("DEFAULT")) {
Pointcut p = (Pointcut) _pointcuts.get("DEFAULT");
return p.afterAdvice(obj, m, args, result);
}

return null;
}

Figure 3.27: Aspect.afterAdvice()



import java.lang.reflect.*; import java.util.Hashtable; import java.util.Enumeration;

public class Aspect {

private String _aspect_name;
public Hashtable _pointcuts = new Hashtable();

public Aspect(String name) {

_aspect_name = name;
}

public String getName() { return _aspect_name; }
public Pointcut getPointcut(String pc_name) {

/**Returns the pointcut with the pc_name from the pointcuts table. Returns null if the pointcut does not exist.*/

return (Pointcut) _pointcuts.get(pc_name);
}

public Pointcut addPointcut(String pointcut_name) {
/**Adds a pointcut with the given pointcut_name to the pointcut table. If the pointcut is a new one, it is added to the pointcut table. Returns the pointcut with the given pointcut_name.*/

if (!_pointcuts.containsKey(pointcut_name)) {
_pointcuts.put(pointcut_name, new Pointcut(pointcut_name));
System.out.println("Pointcut added: " + pointcut_name);
}
return (Pointcut) _pointcuts.get(pointcut_name);
}

public void addPointcut(String pointcut_name, String method_name, AdviceIF advice){

Pointcut p = this.addPointcut(pointcut_name);
p.addAdvice(method_name, advice);
}

public void removePointcut(String pointcut_name){

if (_pointcuts.containsKey(pointcut_name)) {
_pointcuts.remove(pointcut_name);
}
}

public int beforeAdvice(Object obj, Method m, Object[] args){

//System.out.println("Aspect.beforeAdvice()");

if (_pointcuts.containsKey("DEFAULT")) {
Pointcut p = (Pointcut) _pointcuts.get("DEFAULT");
return p.beforeAdvice(obj, m, args);
}
return AspectRepository.RESUME;
}
public Object afterAdvice(Object obj, Method m, Object[] args, Object result){

if (_pointcuts.containsKey("DEFAULT")) {
Pointcut p = (Pointcut) _pointcuts.get("DEFAULT");
return p.afterAdvice(obj, m, args, result);
}
return null;
}

}

Figure 3.28: Aspect.java

As shown in Figure 3.28, each Aspect object has a number of Pointcut objects and a name for itself. The three arguments passed in the beforeAdvice() method are references to the shared object, the Method object, and the array of arguments of the specific shared object's method being called. All these arguments are to be used inside the current beforeAdvice() method or to be passed on to the Pointcut.beforeAdvice() method. Most of the time, not all of these arguments are used. Nevertheless, they need to be passed to the beforeAdvice() method because some of the advices may utilize those information. For example, logging advice needs to know all the data passed to the shared object. The afterAdvice() method has all the same parameters as those of beforeAdvice() method. Additionally, it has an argument defined as an Object result. This represents the result of a specific shared method's method.

3.7 Pointcuts

An Aspect can contain multiple Pointcut objects. And each Pointcut can contain multiple Advice. The method addAdvice() is used for adding new advice. The removeAdvice() method removes advice from a given Pointcut. The beforeAdvice() and afterAdvice() methods are invoked by the corresponding Aspect’s methods of the same name.

The Class Pointcut is shown in Figure 3.29.

Three arguments passed in the beforeAdvice() method are a reference to the shared object, the Method object, and the array of arguments to the specific shared object's method being called. All these arguments are to be passed on to the beforeAdvice() method of the advice. Four arguments passed in the afterAdvice() method will also be passed on to the afterAdvice() of the advice.

import java.lang.reflect.*;
import java.util.Hashtable;
import java.util.Enumeration;

public class Pointcut {

private String _pointcut_name;

public Hashtable _advices = new Hashtable();
public Pointcut(String name) { _pointcut_name = name; }
public String toString() { return _pointcut_name;}
public String getName() { return _pointcut_name;}

public AdviceIF getAdvice(String method_name) {

/**Returns the advice for the given method from the advices table. Returns null if the advice does not exist.*/

return (AdviceIF) _advices.get(method_name);
}

public void addAdvice(String method_name, AdviceIF advice){

/**An advice for the given method_name is added to the current pointcut object. If the advice for the given method_name is already there, it will be replaced with the new one.*/

if (_advices.containsKey(method_name)) {
_advices.remove(method_name);
}
_advices.put(method_name, advice);
}

public void removeAdvice(String method_name) {
/**Removes the advice of the given method_name from the advices table.*/

if (_advices.containsKey(method_name)) {
_advices.remove(method_name);
}
}

public int beforeAdvice(Object obj, Method m, Object[] args){
/**Invokes the beforeAdvice() of the relevant advice.*/

AdviceIF adv = (AdviceIF) _advices.get(m.getName());
if (adv == null)
return AspectRepository.RESUME;
return adv.beforeAdvice(obj, m, args);
}

public Object afterAdvice(Object obj, Method m, Object[] args, Object result){
/**Invokes the afterAdvice() of the relevant advice.*/

AdviceIF adv = (AdviceIF) _advices.get(m.getName());
if (adv != null)
return adv.afterAdvice(obj, m, args, result);
return null;
}

}

Figure 3.29: Pointcut.java



3.8 Advices

The actual behavior of each aspect is provided by an object whose interface is defined by AdviceIF. They will be woven during runtime by the dynamic proxy, i.e., AspectWeaver. The interface for Advice is shown in Figure 3.30.

import java.lang.reflect.*; public interface AdviceIF { public String getAspectName(); public int beforeAdvice(Object obj, Method m, Object[] args); public Object afterAdvice(Object obj, Method m, Object[] args, Object result); }

Figure 3.30: AdviceIF.java

Shown below is the implementation of the synchronization advice for the bounded buffer's get() method. The beforeAdvice() method returns one of the integer constants defined in the AspectRepository; RESUME, BLOCK, and ABORT.

import java.lang.*;
import java.util.*;
import java.lang.Thread;
import java.lang.reflect.Method;

public class Adv_SyncGet implements AdviceIF {
public int beforeAdvice(Object obj, Method m, Object[] args) {
BoundedBuffer bb = (BoundedBuffer) obj;
if (bb.getCnt() > 0 ) {
// System.out.println(t + " gets dispatched.");
return AspectRepository.RESUME;
} else {
// System.out.println(t + " gets blocked.");
return AspectRepository.BLOCK;
}
}

public Object afterAdvice(Object obj, Method m, Object[] args, Object result) {
return null;
}

public String getAspectName() { return "SYNC"; }
}

Figure 3.31: Adv_SyncGet.java

Figure 3.32 shows the implementation of the synchronization advice for the bounded buffer's put() method.

import java.lang.*;
import java.util.*;
import java.lang.Thread;
import java.lang.reflect.Method;

public class Adv_SyncPut implements AdviceIF {

public int beforeAdvice(Object obj, Method m, Object[] args) {
BoundedBuffer bb = (BoundedBuffer) obj;
if (bb.getCnt() < bb.getSize() ) {
// System.out.println(t + " gets dispatched.");
return AspectRepository.RESUME;
} else {
// System.out.println(t + " gets blocked.");
return AspectRepository.BLOCK;
}
}

public Object afterAdvice(Object obj, Method m, Object[] args, Object result) {
return null;
}

public String getAspectName() { return "SYNC"; }
}

Figure 3.32: Adv_SyncPut.java



3.9 Scheduling Advice

Dynamic Weaver Framework handles the scheduling aspects, enabling dynamically changing current scheduling policies. In this bounded buffer producer-consumer example, three policies are currently available - FIFO, PreferGet, and PreferPut. The advice for each of these preferences must maintain task queues for scheduling. And these task queues must be visible to all of the scheduling advice routines. For this reason, an abstract class needs to be defined as shown in Figure 3.33, and all the scheduling advice routines must inherit this abstract class.

import java.lang.Thread;
import java.util.Date;
import java.lang.reflect.Method;

/**Abstract class for all the preference advice classes like Adv_PreferFIFO, Adv_PreferGet, and Adv_PreferPut, etc.*/

public abstract class Preference {
static protected TaskQueue _prodQ = new TaskQueue("put"); // Queue for producers
static protected TaskQueue _consQ = new TaskQueue("get");; // Queue for consumers
abstract public String getPolicyName();

static public int getQsize(String method_name) {
/**do we need to use Hashtable for queues?*/

if (method_name.equals("put"))
return _prodQ.size();
else if (method_name.equals("get"))
return _consQ.size();
else
return 0;
}

static public void enqueue(Task t) {
if (t.getMethodName().equals("put"))
_prodQ.enqueue(t);
else if (t.getMethodName().equals("get"))
_consQ.enqueue(t);
else
System.out.println("Error in enqueue");
}

static public void dequeue(Task t)
if (t.getMethodName().equals("put"))
_prodQ.dequeue(t);
else if (t.getMethodName().equals("get"))
_consQ.dequeue(t);
else
System.out.println("Error in dequeue");
}
}

Figure 3.33: Preference.java

Shown in Figure 3.34 is the implementation of a scheduling policy, which prefers ‘GET’.

The beforeAdvice() method does nothing particular and there is no difference between different scheduling policies. It simply returns RESUME. The actual specific scheduling policy is implemented in the afterAdvice() method. This method selects the task to be unblocked based on the policy, and then returns the reference to the task. AspectRepository will eventually use the return value from this afterAdvice() method to unblock a task.

import java.lang.Thread;
import java.lang.reflect.Method;

public class Adv_PreferGet extends Preference implements AdviceIF {
public String getAspectName() { return "SCHED"; }
public String getPolicyName() { return "PreferGet"; }

public int beforeAdvice(Object obj, Method m, Object[] args) {
//System.out.println(" PGET scheduler.beforeAdvice" + Thread.currentThread().getName());
return AspectRepository.RESUME;
}

/** Picks a task to be unblocked. Returns a reference to the task picked.*/

public Object afterAdvice(Object bb, Method m, Object[] args, Object result) {
Task _picked = null;
//System.out.println(" PGET scheduler.afterAdvice" + Thread.currentThread().getName());

int item_cnt = ((BoundedBuffer) bb).getCnt();
int buf_size = ((BoundedBuffer) bb).getSize();

if (_prodQ.isEmpty() && _consQ.isEmpty()) {
_picked = null;
return _picked;
}

if (item_cnt == buf_size && _consQ.size() > 0) {
// buffer is full and consumer is waiting
_picked = _consQ.dequeue(); // pick a consumer.
return _picked;
}

if (item_cnt == 0 && _prodQ.size() > 0) {
// buffer empty and producer is waiting
_picked = _prodQ.dequeue(); // pick a producer.
return _picked;
}

if (_consQ.isEmpty()) {
_picked = _prodQ.dequeue();
return _picked;
}

if (_prodQ.isEmpty()) {
_picked = _consQ.dequeue();
return _picked;
}

// both consumers and producers are waiting
// Prefer Get
_picked = _consQ.dequeue(); // Prefer consumers.
return _picked;
}

}

Figure 3.34. Adv_PreferGet.java



3.10 Agents and the Policy Choice

There are many ways to engineer intelligence into agents, though the best known ones are data mining techniques and Q-learning. Data mining based on the Bayesian algorithm has been used successfully in detecting network intrusion.

In our research, we found that data mining based on the Bayesian algorithm is the most appropriate technique to engineer intelligence into the agents of concurrent real-time systems, since agents need an initial training data set in order to formulate the knowledge base. Once the real-time system starts, the agents will receive a periodic heartbeat about system overloadedness, resource utilization, average queue lengths, and an estimated upcoming average response time. Every time the agent receives a new heartbeat, it updates its knowledge base, calculates the new estimates of parameters that have an impact on performance and reliability. For instance, based on the queue lengths the intelligent agent will base their decision on whether the scheduling policy should be a FIFO, prefer put, or a prefer get policy.

The following table illustrates the different parameters that are used in the bounded buffer example, where the buffer class represents a concurrent passive object with three methods readBufSize, putItem, and getItem. Since these methods can be activated concurrently, there is a potential for priority inversion. And if the buffer is more than ninety percent full, we need to prefer the getItem method calls to the putItem method calls. We would like to have the capability to one policy to others based on the data that agents gather about the running systems. In Table 1, we can see that whenever the average getItem is greater than some threshold, the agent will trigger an alarm in order to enable the prefer put policy; the threshold is either predefined or can be calculated periodically based on data in the knowledge base.

Time
Waiting itemPut
Waiting itemGet
Waiting readBufSize
Scheduling Policy
t0
10
5
1
FIFO
t1
15
8
2
PP
t2
17
8
5
PP
t3
10
35
3
PG
t4
10
35
2
PG
t5
30
10
1
PP
t6
10
5
1
FIFO

t7

18
12
0
FIFO
Figure 3.35

The agent uses the data received through the real-time heartbeats in order to update its knowledge base and request the aspectRepository to reconfigure the system if it predicts that performance degradation; violation of fairness guarantees, or jeopardizing reliability is imminent. There are different classes that represent the different scheduling policies. Having represented the different aspects in isolated classes, the agents can easily instruct the aspectRepository to change, delete, or add policies without the need to stop the running system. Also new decisions can be created, and new training data can be given to the agents without the need to reengineer the concurrent real-time system.

Agent is an observer to the AspectRepository. So, agent is informed of the current state of the buffer and task queues from the AspectRepository - i.e. the number of items in the buffer, number of tasks in the two task queues. Based on this information and the knowledge learned from the training data, agent can choose which policy is the best one and changes the policy according to the result. as shown in the Figure 3.36.

import java.util.Observer;
.
.

public class AgentDialog extends JDialog implements Observer {
.
.
private String[] policyNames = { "A_PreferFIFO", "A_PreferGet", "A_PreferPut" };
.
.

// implementation of the Observer interface --- public void update()

private static int myCnt = 0;

public void update(Observable o, Object arg) {
myCnt++;
if (myCnt > 10) {
myCnt = 0;
int x = myPolicyChooser.getBestPolicy(curr_n_items, curr_n_puts, curr_n_gets);
AspectRepository ar = ProdConsApp.getAspectRepository();
ar.changePolicy( policyNames[x] );
}
}

private PolicyChooser myPolicyChooser = new PolicyChooser(arrFIFO, arrPGET, arrPPUT);
}

Figure 3.36: AgentDialog.java


Agent uses the Bayesian algorithm for the decision making. Part of the actual implementation of the Bayesian algorithm for this system is shown in Figure 3.37.

import java.util.Arrays;
import java.lang.Math;

public class PolicyChooser {
private int FIFO_item_arr [], FIFO_gets_arr [], FIFO_puts_arr [];
private int PGET_item_arr [], PGET_gets_arr [], PGET_puts_arr [];
private int PPUT_item_arr [], PPUT_gets_arr [], PPUT_puts_arr [];

private int FIFO_cnt, PGET_cnt, PPUT_cnt;
private double FIFO_freq, PGET_freq, PPUT_freq;

// means
private double FIFO_item_avg, FIFO_gets_avg, FIFO_puts_avg;
private double PGET_item_avg, PGET_gets_avg, PGET_puts_avg;
private double PPUT_item_avg, PPUT_gets_avg, PPUT_puts_avg;

// stdevs
private double FIFO_item_std, FIFO_gets_std, FIFO_puts_std;
private double PGET_item_std, PGET_gets_std, PGET_puts_std;
private double PPUT_item_std, PPUT_gets_std, PPUT_puts_std;

// partial probabilities
private double FIFO_item_p, FIFO_gets_p, FIFO_puts_p;
private double PGET_item_p, PGET_gets_p, PGET_puts_p;
private double PPUT_item_p, PPUT_gets_p, PPUT_puts_p;

//
private double FIFO_prob_temp;
private double PGET_prob_temp;
private double PPUT_prob_temp;

// Final Probabilities
private double FIFO_prob_final;
private double PGET_prob_final;
private double PPUT_prob_final;

// Constructor
public PolicyChooser(int arrFIFO[][], int arrPGET[][], int arrPPUT[][]) {
setData(arrFIFO, arrPGET, arrPPUT);
}

static final int POLICY_FIFO = 0;
static final int POLICY_PGET = 1;
static final int POLICY_PPUT = 2;

public int getBestPolicy(int item, int puts, int gets) {
.
.

if (FIFO_prob_final >= PPUT_prob_final && FIFO_prob_final >= PGET_prob_final)
return POLICY_FIFO;

if (PPUT_prob_final >= FIFO_prob_final && PPUT_prob_final >= PGET_prob_final)
return POLICY_PPUT;

return POLICY_PGET;
}

public void setData(int arrFIFO[][], int arrPGET[][], int arrPPUT[][]) {
.
.
}
.
.

Figure 3.37: PolicyChooser.java



4.0 Demo Clients of DWF

4.1 Populating Threads for Demonstration

Creating an infinite number of client threads will make the demonstration more like real situation, but it could be hard to manage. So, as an easy alternatve, finite number of threads are employed to run repeatedly.

import java.lang.Thread;

public class MyDemo extends Thread {
private BoundedBufferIF _buf;
MyDemo(BoundedBufferIF buf) {_buf = buf;}

public void run() {
final int max_threads = 50;
MyConsumer[] cons = new MyConsumer[max_threads];
MyProducer[] prod = new MyProducer[max_threads];
for (int i = 0; i < max_threads; i++) {
cons[i] = new MyConsumer (_buf);
cons[i].setName("consumer " + i);
cons[i].set_seed(i);
prod[i] = new MyProducer (_buf);
prod[i].setName("producer " + i);
prod[i].set_seed(i + max_threads);
}

for (int i = 0; i < max_threads; i++) {
cons[i].start();
prod[i].start();
}
}
}

Figure 3.38: MyDemo.java



4.2 Producers and Consumers Threads

Each thread runs with random pause to mimic the real situation.

import java.lang.Character;
import java.util.Random;

public class MyProducer extends java.lang.Thread {
private BoundedBufferIF _buf;
MyProducer(BoundedBufferIF buf) { _buf = buf; }
public void run() {
String s;
int i = 0;

System.out.println (this.getName() + " is running.");
while (true) {
try {
Thread.sleep((int)(myRandom.nextDouble() * 6000+100)); // WAS r*1500 + 200
}
catch (InterruptedException ie) {}
s = "P" + (char) (65 + Math.random() * 20); // Produce a character.
_buf.put(s);
Thread.yield();
i++; if (i > 10) { Thread.yield(); i = 0; }
}
}}
}

Figure 3.39: MyProducer.java



4.3 Displaying History of Buffer and Queues

HistoryQueue contains the simple history of the queues and buffer.

import java.util.Vector;

public class HistoryQueue {
private Vector _history_P = new Vector(); /* producer */
private Vector _history_C = new Vector(); /* consumer */
private Vector _history_I = new Vector(); /* item count */
private Vector _history_S = new Vector(); /* scheduling policies */

HistoryQueue() { }
public synchronized boolean isEmpty() { .. }
public synchronized void enqueue(int p_cnt, // num of producers
int c_cnt, // num of consumers
int i_cnt, // num of items
int s_policy // policy ) { ... }
public synchronized void dequeue() { ... }
public synchronized int size() { ... }
public synchronized void clear() { ... }
public int getPcnt(int index) { ... }
public int getCcnt(int index) { ... }
public int getIcnt(int index) { ... }
public int getPolicy(int index) { ... }
}

Figure 3.40: HistoryQueue.java

HistoryQueue will be updated everytime the put() or get() method call is done. All the observers will be notified of the change by the notifyObservers() as shown in Figure 3.41.

.
.
private HistoryQueue _history = new HistoryQueue();

.
.

public synchronized void afterAdvice(Object obj, Method m, Object[] args, Object result) {
.
.

_history.enqueue(_prod_Q.size(),
_cons_Q.size(),
((BoundedBuffer) obj).getCnt(),
((A_Prefer_IF)_policy).getPolicyName());
setChanged();
notifyObservers(_history);
}

Figure 3.41: Notifying the observers of the changes (AspectRepository.java)

ProdConsFrame is one of the observers to the AspectRepository. It has the update() method implemented in it as shown in Figure 3.42.

import java.util.Vector;
import java.util.Observer;
import java.util.Observable;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
//import com.borland.jbcl.layout.*;

public class ProdConsFrame extends JFrame implements Observer {
// This frame is the observer to the aspect repository.
.
.

// implementation of the Observer interface
public void update(Observable o, Object arg) {
HistoryQueue hq = (HistoryQueue) arg;
lineGraphProducer.redraw(hq.getHistoryP(),
hq.getHistoryS(),
ProdConsApp.getMyProducerCnt());
lineGraphConsumer.redraw(hq.getHistoryC(),
hq.getHistoryS(),
ProdConsApp.getMyConsumerCnt());
lineGraphBuffer.redraw(hq.getHistoryI(),
hq.getHistoryS(),
ProdConsApp.getMyBufferSize());

}
.
.

Figure 3.42: Drawing the graphs in ProdConsFrame.java

Because the line graphs are to be very frequently updated, the drawing routine itself needs to be efficient enough so there is no flickering on the screen or pausing of the running processes. For this purpose, the line panel extends the JPanel and the paint() method is overridden as shown in Figure 3.43.

import java.util.Vector;
import java.awt.*;
import javax.swing.JPanel;

public class MyLineGraphPanel extends JPanel {
public MyLineGraphPanel() {
super();
}

public void paint(Graphics g) {
.
.

super.paint(g);
.
.
}

.
.
}

Figure 3.43: Actual Drawing of a Line Graph (MyLineGraphPanel.java)











Figure 47: Collaboration Diagram: Invoke a Method




NorthwesternAbout Northwestern SCS | About Robert Morris | Contact Me