Skip to content
Kevin Brightwell edited this page Aug 26, 2015 · 1 revision

Aspect-Orientation in Umple

Introduction

There are a lot of research papers about using Aspect-Orientation (AO, which can help manage cross-cutting concerns in software systems. It also has some other capabilities like inter-type declaration which is similar to mixins. At the beginning, We don’t want to concentrate just on benefits of AO because we can easily find lots of papers. We are going to introduce syntaxes for enhanced AO in Umple and will try to describe each sample for easy understanding.

Before falling down on introduction of syntaxes and features, I would like to mention that Aspect-Oriented Programming (AOP) is used for implementation of aspect-oriented programs. However, we can support some of its features by pure object-oriented programming. This approach is not proper because it will lose concept of seamless but sometimes it’s necessary because it’s not possible to use AOP. Moreover, I may indicate some issues in features based on Umple structure which we need to pay attention them in design and code generation.

Feature 1: Simple injection

Suppose, we have a class named C and we are going to inject a code after one of its methods. This operation can be done by the concept of after in Umple however we will extend this structure. We just would like to explain features from simple state to complex one. We also need to define aspect as a structure like class but with some constraints and extensions.

// Example A1
class C { x; }

aspect A {
	after Boolean C.setX(String) = {
		System.out.println("The value changed.");
	}
}

An alternative

// Example A2
// The = has been removed here to be consistent with existing notation
class C { x; }

aspect A {
	after Boolean C.setX(String) {
		System.out.println("The value changed.");
	}
}

Another alternative

// Example A3
// Follow existing Umple conventions we should be able do the following
//V.A. Existing after and before don't work for manual methods (Just work for set and get methods). Therefore, we need to change their definition.
class C { x; }

class C{
  after Boolean setX(String) {
    System.out.println("The value changed.");
  }
}

In the generated code we must pay attention to the last return value. This piece of code must be run before last return value. Of course, if there would be more than one return point we should think more about code generation. The mentioned issue is raised when we are going to implement code based on OO. There is not any issues when we are going to implement this with AOP. Another approach to do this is following:

// Example B1
class C { x; }

aspect A {
	after Boolean C.setX(String) = logging();
	private void logging() {
		System.out.println("The value changed.");
	}
}

Alternative syntax for the above

// Example B2
class C { x; }

aspect A {
	after Boolean C.setX(String) {logging();}
	private void logging() {
		System.out.println("The value changed.");
	}
}

In this approach, Umple’s compile should add automatically method logging to class C and call that method after method setX.

I believe that there are some critical situations in definition of these types of methods but I don’t like to mention them here. For example, a method with return value can completely change the follow of running code.

This can be handled on a temporary basis by injecting a special method that calls the method being modified, gives the special method the same name as the method being modified, and giving the original method a special name. All returns now come back through the same path. This is how we would probably need to handle postconditions too.

We also would be able to use parameters inside of methods which will be run after/before/around of a joinpoint. Look at following sample:

// Example C1
class C {
	x;
}

aspect A {
	after Boolean C.setX(String inX) = {
	System.out.println("The value changed to "+inX+" .");
	}
}

inX must be changed in the generated code based on real name of parameter in class C. This process can be pretty difficult when we use wild cards to select joinpoints. This sophisticated process can be avoided if you use method instead of pure code. Look at for following code:

// Example D1
class C {
	x;
}

aspect A {
	after Boolean C.setX(String inX) = logging(inX);
	private void logging(String data) {
		System.out.println("The value changed to "+data+" .");
	}
}

In this code, we just need to use proper mapping for the parameter of logging and don’t need to explorer all advice code for parameter mapping.

Future 2: Wild card based injection

In order to have full support for crosscutting implementation we need to use wild card based matching to detect necessary join-points. This feature can have lots of discussion but I just would like to use simple samples so that I can build my basic infrastructure for proposal and after that I will be able to extend it.

Suppose that we are going to log all of the set methods in the system without worry about their next coming letters. I knew that this may be done by trace capability of Umple but the logging is a case for better describing. We can consider other concerns like security. We define class keyword to mention that our selected methods don’t depend on the name of class and every class which matches the wild card can be considered as a join-point. Moreover, we can make a joint point for attributes when their value are changed or read but I don’t want to concentrate them in this short proposal. Consider the following code for more explanation:

// Example E1
class C1 {
	x;
}
class C2 {
	y;
} 
aspect A {
	after Boolean class.set*(String inX) = logging(inX);
	private void logging(String data) {
		System.out.println("The value changed to "+data+" .");
	}
}

The issues about implementation of this technique in OO: It’s difficult to figure out the nature of methods. There are some especial commands in AOP for this purpose but I believe that we can solve the problem when we interpreter the code by Umple and generate code. However, this section need more concentration and we will get back to this in the future. There is another sample for usability of wild cards which is following:

// Example F1
class C1 {
	x;
}
class C2 {
	y;
} 
class M {
	w;
}
aspect A {
	after Boolean C*.set*(String inX) = logging(inX);
	private void logging(String data) {
		System.out.println("The value changed to "+data+" .");
	}
}

Feature 3: Adding the same umple code to multiple classes

We can let programmers add attributes (or other elements) to several classes. This allows us to support proper mixins (currently what we call mixins in Umple only allows adding extra stuff to one class). Umple supports a simple version of mixins but with aspects we can get whole power of mixins. In following example, I will show two proposals for adding an attribute to classes. I don’t want to be so strict about syntax because in the future maybe I figure out that which approach is more convenient. I am going to add an Integer value to classes which the first letter of their name is C. Therefore, the first approach as follows:

// Example G1
class C1 {
	x;
}
class C2 {
	y;
} 
class M {
	w;
}
aspect A {

// Disadvantages?
  // += notation can be confused with incrementation in an algorithm
// Advantages?

	// approach 1:
        Integer cnt1 = 0;	
	C* += cnt1;
}

The approach is usable when we want to add traits to classes. I will discuss about this feature later. The second approach is following:

// Example G2
// C1, C2 and M are the same as above.

aspect A {
	// approach 2:
        Integer C*.cnt1 = 0;
}

Here are other approaches to do the above:

// Example G3
// C1, C2 and M are the same as above.
// Disadvantages?
//V.A. This has potentiality for error in modeling  and also don't keep Umple simple. We should use wild card in control of a concept. 
// Advantages?

class C* {
   Integer cnt1 = 0;
}

Another

// Example G4
// C1, C2 and M are the same as above.
// Here we match on classes in an aspect instead of methods
// Disadvantages?
// Advantages?


aspect {
  class C* {
    Integer cnt1 = 0;
  }
}

Another

// Example G5 (essentially same as option 3 with use of traits too -- could be in addition to option 3
// C1, C2 and M are the same as above.
// Disadvantages?
//V.A. like Example G3 
// Advantages?


 trait T {
       Integer cnt1 = 0;
 }

class C* {
   isA T;
 }

Feature 4: Using traits in AO

Traits provide us a nice technique for reusing so we can use traits inside of aspects. This can bring use more reusability. Traits can be used with directive isA and also we can reuse them inside of aspects. This approach can be more applicable we are going to create a product line for a system. We can have different products just by changing aspects. In this approach, I mean software product line, we just need to take into account variation points as join-points. The scenario for the following sample is that we want to provide a counter so that it can count number of instantiation. In this point, I don’t discuss about other solutions which can be used instead of AO+Traits. I am just going to show capabilities and syntaxes.

class C1 {
	x;
}
class C2 {
	y;
} 
class M {
	w;
}

/* 
	Write a counter in order to count number of instantiations based on aspects and traits.
*/
 
trait T1 {
	private static Integer cnt = 0;
	private void counter() {
		cnt++;	
	}
	public int count() {
		return cnt;
	}
}

aspect A {
	C* += T1;
	after C*.constructor() = T1.counter(); 
}

The new keyword inside of aspect is constructor which is used as its general meaning.

Implementation of counter without traits:

class C1 {
	x;
}
class C2 {
	y;
} 
class M {
	w;
}

/* 
	Write a counter in order to count number of instantiations based on aspects.
*/
 
aspect A {
	after C*.constructor() = counter();
	private static Integer C*.cnt = 0;
	private void counter() = {
		cnt++;	
	}
	public Integer C*.count() = {
		return cnt;
	}	
}

Feature 5: Adding association in Aspects

As Umple manages very well associations among classes and also generate automatic methods for them, it would be very useful to use associations in aspects. In the simple form, we can use general form of the association in aspects. However, it would be powerful to use wild card on name of classes and roles. For better understanding, I will implement observer pattern with aspects in Umple. We have several engines and consoles. We are going to monitor engines in the console so engines have the role of subject and consoles have the role of observer. As Umple automatically generate add/remove/get methods for the association, we don’t need to implement those methods in aspects. In AspectJ, developers had to implement all of those methods in the aspect.

class Engine {
	id;
	Integer speed = 0;
}

class Console {
	name;
}

aspect UpdateConsole {
	//instead of association we can add methods manually.
	0..1 Engine subjs -- * Console observers;
	
	after Boolean Engine.SetSpeed() = update(); 
	private void update() {
		for(int i = observers.size(); i > 0; i--)
		observers.get(i - 1).refresh(this);
	}
	
	public void Console.refresh(Engine eng) {
		System.out.println("Console Name: "+getName());
		System.out.println("The speed of engine number "+ eng.getId()+" has changed to: "+eng.getSpeed());	
	}	
}
Clone this wiki locally