|
Comments
|
Today's Top SOA Links
ASP.NET Aspect.NET: Aspect-Oriented Programming for Microsoft.NET in Practice
Aspect-oriented programming for .NET
By: Vladimir Safonov
Jul. 28, 2005 04:00 PM
– Scanning
– searching the appropriate (potential) join points within the target assembly, based on the weaving rules defined in the aspect. The aspect editor allows the user to browse these potential joinpoints in the source code and agree or disagree with any of them. – Weaving the aspect actions to the joinpoints found by the scanning phase and finally clarified by the user. Our implementation of aspect weaving, instead of using the somewhat limited features of .NET reflection, is based on the state-of-the-art multitargeted compiler development environment Microsoft Phoenix (see Reference #7). Phoenix, in particular, provides powerful and comfortable features of analyzing, updating, and creating .NET assemblies and is considered by Microsoft as a basis for all of its own and third-party oncoming compiler-related projects. Our group was the first outside of Microsoft to start using Phoenix in September 2003. Before Phoenix had been made available within the framework of Microsoft Early Adoption and Academic Programs, a lot of researchers had to develop their own different and limited tools for handling .NET assemblies.
Example of Aspect.NET Use Let’s consider an example of applying Aspect.NET for business software development. We’ve taken a simple example and will not explain the details of implementation in order to emphasize the advantages of applying AOP at the user level. Suppose a customer needs to develop a simple software system of processing orders that supports debit and credit operations for the clients. In addition, the system is to collect time statistics of the use of it by all the clients. In such system, the Client class could be designed as shown on the Listing 2. The ordering system of the customer (OrderSystem) could use it in the way shown in Listing 3. Analysis of customer projects has shown that collecting time statistics can be implemented by the total time of the StartOrderProcessing() method execution, since that method is responsible for processing orders of the clients and related calculations. If we apply to the customer projects the AspectProfiler aspect (see Listing 1), we’ll get the desirable implementation of collecting time statistics (we simplify in this example what may be actually needed in real projects). That aspect prescribes to invoke the ActionBeforeCall() and ActionAfterCall() actions, accordingly, before and after any call of StartOrderProcessing(). On weaving the AspectProfiler aspect into the OrderSystem customer project as described above, when executing the generated assembly OrderSystem.exe, we can get on our console the following output: AspectProfiler: Executing time of Orders.OrderSystem.StartOrderProcessing: Bob: account=500 John: account=-150Suppose the number of customer’s clients has increased to such an extent that the ordering system can no longer cope with their service. As the result of analyzing the time statistics, to improve performance, we decide to run each call of the ProcessOperation() method of the Client class in a separate thread, since it contains time-consuming requests to databases, so their concurrent execution could dramatically improve the performance. To preserve the application logic, on finishing the order processing method, we should wait for all such threads to terminate. The AsyncCallAspect aspect (see Listing 4) can perform this task very well. It keeps the pool of threads started in its WrapMethodWithAsyncCall() action and waits for each of them to terminate in its TotalJoining() action. The WrapMethodWithAsyncCall() action is performed instead of the ProcessOperation() method and uses, for invoking the method in a separate thread, the reference to the target object of the Client class (e.g., John or Bob) and the name of the method to be called (e.g., Orders.Client.ProcessOperation). If we now run the generated assembly after weaving the AspectProfiler and AsyncCallAspect aspects, our output to console will be different: AspectProfiler: Executing time of Orders.OrderSystem.StartOrderProcessing: Bob: account=500 John: account=-150 Once the customer noticed that a negative balance is shown on the accounts of his clients he decided to change his credit policy. Now we are to implement a mechanism of transactions. The execution of the StartOrderProcessing() method will be considered as a separate transaction that should be rolled back in the result of one of its credit operations CreditOperation() because the balance on the client’s account is negative. One of the possible solutions of this problem can be weaving two more aspects – CreditAbilityAspect and TransactionalAspect. The CreditOverflow- Assertion action of the CreditAbilityAspect action is invoked upon finishing CreditOperation() and throws an exception in case, as the result if its execution, the Client.Account field gets negative. The AroundTransaction action of the TransactionalAspect aspect is performed instead of the StartOrderProcessing method and calls the method inside, after wrapping it into a try/catch block for handling a possible exeption thrown by the credit operation. This action also implements all of the transactional mechanisms, from saving the state of the system before starting a transaction, till rollback whan it failed (see Listing 5). Please note how the CreditAbilityAspect aspect accesses the context of weaving, and and how the AroundTransaction action of the TransactionalAspect aspect performs the call of the method to which the action is woven. Now the execution of the resulting assembly leads to the following result: Rolling back transactions... AspectProfiler: Executing time of Orders.OrderSystem.StartOrderProcessing: Bob: account=250 John: account=100 Figure 2 shows the result of weaving of all of the four aspects into OrderSystem project. Each aspect is visualized by its own color. This is just a simple example of how the requirements to a software system can evolve in the process of its development. In this case, more and more requirements were added that hadn’t been taken into account in the initial design, and that were not related to the business logic of the application. We managed to take all of these requirements into account without adding any line of code into the initial project that remained as simple as it was and that remained open to all subsequent changes. What we got is a library of aspects that could be successfully applied to other projects, just by clarifying the behavior and changing the weaving rules. As another example, the design-by-contract software technology (Eiffel: www.eiffel.com) is intended to improve software quality, but its straightforward use may decrease readability of the source codes. To avoid the latter shortcoming, one can just develop a couple of design-by-contract aspects or configure some ready ones, if any. Advantages of Using AOP and Aspect.NET and Its Perspectives After the given introduction to the Aspect.NET technology, let’s summarize some of its advantages, along with to those already mentioned above.
Conclusion Due to the development of the Microsoft.NET platform, the developers get more and more opportunities that were unavailable before. AOP lets a software developer concentrate mostly on the business logic of his or her project, by implementing auxiliary features as special kinds of reusable modules – aspects. Interaction of aspects to the core code of the project is defined in a high-level metalanguage that does not depend on the project implementation language. These loosely coupled software components can be developed independently. Keeping in mind the tendency toward distributed software system development, using AOP thusly can greatly decrease the cost of software products. Whereas for the Java platform there are a lot of powerful AOP tools, we expect that, due to further development of Microsoft Phoenix and Visual Studio.NET, more and more AOP systems will be also developed for .NET and integrated to the oncoming versions of Visual Studio. Our Aspect.NET tool is one of the first such systems, discovering for .NET software developers the magic world of AOP. The working prototype of the Aspect.NET tool and its documentation is available upon request from the authors and will soon be available at the Microsoft Developer’s Network Curriculum Repository (www.msdnaa.net/curriculum). In order to function, Aspect.NET requires that Whidbey beta2 and the Phoenix RDK (Microsoft Phoenix home page: http://research.microsoft.com/phoenix – available from Microsoft under the terms and conditions of the Phoenix Academic Program) be preinstalled. References
Listing 1: An example of a profiling aspect. Metalanguage constructs are in bold. Please note that, for weaving the aspect into different kinds of join points and according to different kinds of rules, the %action declaration should be updated only. %aspect AspectProfiler public class AspectProfiler {
static private DateTime StartTime; static private DateTime EndTime;
{StartTime = DateTime.Now; }
{EndTime = DateTime.Now; }
%action %before %call *OrderSystem.StartOrderProcessing public static void ActionBeforeCall(string methname) {AspectProfiler.StartTimeUpdate(); }
public static void ActionAfterCall(string methname) {AspectProfiler.EndTimeUpdate(); if (StartTime.Ticks != 0) { TimeSpan tsp = EndTime.Subtract(StartTime); System.Console.WriteLine(“AspectProfiler: Executing time of {0}: {1} milliseconds”, methname, tsp.Ticks/10000);} }
Listing 2: The Client class of the ordering system //Client.cs class Client {public string Name; public double Account;
{ Name = _Name; Account = _Account; }
{Account -= Amount; ProcessOperation(); // Perform time consuming further processing }
{Account += Amount; ProcessOperation(); // Perform time consuming further processing }
{Thread.Sleep(2000); // Emulation of processing }
Listing 3: The OrderSystem class of the ordering system //Program.cs class OrderSystem {public Hashtable ActiveClients;
static void Main(string[] args) {Hashtable ActiveClientsTable = new Hashtable(); ActiveClientsTable.Add(“John”, new Client(“John”, 100)); ActiveClientsTable.Add(“Bob”, new Client(“Bob”, 250)); OrderSystem os = new OrderSystem(ActiveClientsTable); os.StartOrderProcessing(); //Printing somehow ActiveClients table on the console... }
{Client John = (Client)ActiveClients[“John”]; Client Bob = (Client)ActiveClients[“Bob”]; John.CreditOperation(250); Bob.DebitOperation(250); } }// OrderSystem Listing 4: The AsyncCallAspect %aspect AsyncCallAspect public class AsyncCallAspect {%modules static private ArrayList ThreadsPool = null;
%action %instead %call *Client.ProcessOperation static public void WrapMethodWithAsyncCall (string FullMethodName, Object target) { if (ThreadsPool == null) ThreadsPool = new ArrayList();
FullMethodName.Substring(FullMethodName.LastIndexOf(“.”)+1); MethodInfo mi = target.GetType().GetMethod(ShortMethodName); ThreadStart ts = (ThreadStart)ThreadStart.CreateDelegate(typeof(ThreadStart), target, mi); Thread thread = new Thread(ts); ThreadsPool.Add(thread); thread.Start(); } %action %after %call *OrderSystem.StartOrderProcessing static public void TotalJoining() {if (ThreadsPool != null) {foreach (Thread t in ThreadsPool) t.Join(); } } }// AsyncCallAspect Listing 5: CreditAbilityAspect and TransactionalAspect %aspect CreditAbilityAspect public class CreditAbilityAspect {%rules %action %after %call *Client.CreditOperation public static void CreditOverflowAssertion (string FullMethodName, Object target) {Type TargetType = target.GetType(); double ClientAccount = (double) TargetType.GetField(“Account”).GetValue(target); if(ClientAccount < 0) throw new Exception(“Account exceeded!”); } }// CreditAbilityAspect
public class TransactionalAspect {%modules static protected void BackupSystemState(Object target) {//Store somehow this.ActiveClients table }
{//Copy previously stored ActiveClients to this.ActiveClients }
%action %instead %call *OrderSystem.StartOrderProcessing public static void AroundTransaction(string FullMethodName, Object target) {string ShortMethName = FullMethodName.Substring(FullMethodName.LastIndexOf(“.”)+1); Type TargetType = target.GetType(); // ... Saving state of the system for further recovering BackupSystemState(target); try {TargetType.GetMethod(ShortMethName).Invoke(target, null); //...Commit transaction } catch (Exception e) //...Rolling back transactions
Console.WriteLine(“Rolling back transactions...”); RecoverSystemState(target); } } }// TransactionalAspect Reader Feedback: Page 1 of 1
Your Feedback
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
|
SYS-CON Featured Whitepapers
Most Read This Week |
|||||||||||||||||||||||||||||||||||||||