CSC/ECE 517 Summer 2008/wiki1 5 bk: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
 
(105 intermediate revisions by 2 users not shown)
Line 1: Line 1:
==Introduction==
==Introduction==
Hooks and aspect-oriented programming are both techniques which allow cross-cutting of code to take place.  I will start by explaining these concepts in detail and then proceed to look at two implementations of the aspect-oriented approach for both Ruby and Java.  A comparison of the two implementations will also be provided.  Example code for will help to clarify certain points.  I will also address why one would choose one approach over the other with respect to hooking and aspect-oriented programming.
[http://en.wikipedia.org/wiki/Hooking Hooks] and [http://en.wikipedia.org/wiki/Aspect-oriented_programming aspect-oriented programming] are two techniques which allow code to be modified on a large scale with minimal effort.  Both approaches endeavour to provide a single point of modification in order to eliminate the need to make subtle changes throughout the code base.  I will start by explaining these concepts in detail and then proceed to look at two implementations using aspect-oriented tools for both Ruby and Java.  The example code will help to clarify certain points.  I will also address how hooking is different from aspect-oriented programming as well as discuss the differences between AspectR and AspectJ, the AOP tools provided for Ruby and Java.


==Hooking Concept==
==Hooking Concept==
The hooking mechanism in Ruby allows functional events inside a running program to be identified while providing an action that will occur upon detection of the specified event.  In this way, hooks serve as a detector of actions within the code, when event x is encountered, y will be performed.  Examples where such a technique would prove useful include:
The hooking mechanism in Ruby allows events inside a running program to be identified while providing an action that will occur upon detection of the specified event.  In this way, hooks serve as a detector of actions within the code, when event x is encountered, y will be performed.  It is important to note that while hooking is possible in Ruby, it is not supported in Java.  A few examples of where such a technique would prove useful include:
*Recording when or how many times a method is called.
*Recording when or how many times a method is called
*Recording when an object is created.
*Recording when an object is created
*Debugging problematic code.
*Debugging problematic code
*Enhancing functionality of existing code [http://en.wikipedia.org/wiki/Hooking]


===Examples of Hooking (citation)===
===Example of Hooking System Calls===
====Hooking System Calls====
The following is an example of a hook being inserted to perform an action when the program makes a system call.  In order for this to occur, an alias must be created so that the original functionality can still be accessed.  In this case, ''original_system'' is aliased to the unaltered ''system''.  Once this is done, we can freely modify the definition of ''system'' to perform just about any operation desired.  This is similar to the way in which a nefarious hook, known as a [http://en.wikipedia.org/wiki/Rootkit rootkit], would work by still performing the intended operation of the code, but also appending something extra in the process...  In our current non-malicious case, the ''system'' method has been modified to print the command being executed ("ls -al" in this case), the timestamp, and then finally executing the original instruction and sending the output to the command line [Thomas].
The following is an example of a hook being inserted to perform a certain action when the program makes a system call.  In order for this to occur, an alias must be created so that the original functionality can still be accessed.  In this case, ''original_system'' is aliased to the unaltered ''system''.  Once this is done, we can freely modify the definition of ''system'' to perform just about any operation.  This is similar to the way in which a nefarious hook, known as a [http://en.wikipedia.org/wiki/Rootkit rootkit], would work by still performing the intended operation of the code, but also appending something extra in the process...  In our current non-malicious case, the ''system'' method has been modified to print the command being executed ("ls -al" in this case), the timestamp, and then finally executing the original instruction and sending the output to standard output.


   module Kernel
   module Kernel
Line 35: Line 35:
   -rw-r--r--  1 brking brking  287 2008-06-02 21:19 moreHooking.rb
   -rw-r--r--  1 brking brking  287 2008-06-02 21:19 moreHooking.rb


====Hooking a Method of a Class====
===Example of Hooking a Class Method===
Similarly, a method could be hooked to perform additional tasks upon calling the method.  The method would still carry out its original functionality, but the new instructions would precede or follow.  In this example, the ''puts'' method of the ''IO'' class is hooked in order to print an additional statement to standard output every time ''puts'' is called.  As in the previous example, the original method is aliased so that it can still be referenced after making enhancements.   
Similarly, a method could be hooked to perform additional tasks upon calling the method [Thomas].  The method would still carry out its original functionality, but the new instructions would precede or follow.  In this example, the ''puts'' method of the ''IO'' class is hooked in order to print an additional statement to standard output every time ''puts'' is called.  As in the previous example, the original method is aliased so that it can still be referenced after making enhancements.  It is important to note that if you accidentally used '':puts'' for the additional statement instead of '':original_puts'', you would create an infinite loop calling the modified ''puts'' [Thomas].


   class IO
   class IO
Line 53: Line 53:


==Aspect-Oriented Programming Concept==
==Aspect-Oriented Programming Concept==
Aspect-oriented programming deals with abstracting operations in code which are independent of the main purpose or function of the code.  As an illustration, a program may have two completely unrelated classes, an automobile class and a plant class.  Setting aside for a moment trying to conceive of an application where the two would reside together, there could potentially be similar data desired from objects of both classes.  For example, it might be worthwhile knowing the time in which each object gets instantiated, logging events on both objects, or even telling when they pass out of scope,  These three commonalities are associated with each object of the class, but have nothing to do specifically with the functionality of the object.  Clearly there are many other transcending pieces of information that could be attributed to either class.
Aspect-oriented programming deals with abstracting operations in code which are independent of the main purpose or function of the code.  As an illustration, a program may have two completely unrelated classes, an automobile class and a plant class.  Setting aside for a moment trying to conceive of an application where the two would reside together, there could potentially be similar data or operations desired from objects across both classes.  For example, it might be worthwhile knowing the time in which each object gets instantiated, logging events on both objects, or even telling when they pass out of scope,  These three commonalities are associated with each object of the class, but have nothing to do specifically with the functionality of the object.  Clearly there are many other transcending operations that could be performed on either class.


In the aspect-oriented vernacular, code written to obtain this common data is said to be cross-cutting in that it applies to multiple facets of the code, but does not specifically deal with the ''business'' or [http://en.wikipedia.org/wiki/Core_concern core concerns] of the code.  This cross-cutting code is also known as ''advice''. The specific places in the code where it is determined that ''advice'' is needed is referred to as a ''pointcut'' or ''jointpoint''From there, the ''aspect'' embodies the combination of the ''jointpoint'' and ''advice'' for use by the program.  In the previous example, there would be ''aspects'' for timestamps, event logging, and scope tracking [http://www.oracle.com/technology/oramag/oracle/04-sep/o54aop.html].  It is important to note that the creation of ''aspects'' leaves the original underlying class intact and simply provides a wrapper which will encompass it.  This ability to work on existing code without altering has the added advantage of making it more adaptable to future changes and enhancement requests later in the application's life cycle.  Aspect-orientation does not in any way undermine object-orientation.  Aspect-oriented programming complements object-oriented programming in that OOP governs top-down relationships while AOP governs left-right relationships within the application [http://www.oracle.com/technology/oramag/oracle/04-sep/o54aop.html].
In the aspect-oriented vernacular, this notion of functionality which transcends individual classes is known as a [http://en.wikipedia.org/wiki/Crosscutting_concerns cross-cutting concern].  In this way, it applies to multiple facets of the code, but does not specifically deal with the ''business'' or [http://en.wikipedia.org/wiki/Core_concern core concerns] of the code.  [http://en.wikipedia.org/wiki/Join_point Jointpoints] or pointcuts are the specific locations in the code where a cross-cutting of core concerns takes place.  It is at these points, using aspect-oriented tools, that [http://en.wikipedia.org/wiki/Advice_in_aspect-oriented_programming advice] will be inserted.  ''Advice'' is simply a function or group of functions for performing a task at this crossroads in the codeThe [http://en.wikipedia.org/wiki/Aspect_(computer_science) aspect] embodies the combination of the ''jointpoint'' and ''advice'' within the program.  In the previous examples mentioned, there would be ''aspects'' for timestamps, event logging, and scope tracking [http://www.oracle.com/technology/oramag/oracle/04-sep/o54aop.html].  It is important to note that the creation of ''aspects'' leaves the original underlying class intact and simply provides a wrapper which will encompass it.  This ability to work on existing code without alteration has the added advantage of making it more adaptable to future changes and enhancement requests later in the application's life cycle.  Aspect-orientation does not in any way undermine object-orientation.  Aspect-oriented programming complements object-oriented programming in that OOP governs top-down relationships while AOP governs left-right relationships within the application [http://www.oracle.com/technology/oramag/oracle/04-sep/o54aop.html].


===Why Use Hooks Instead of AOP?===
==Aspect-Oriented Programming Implementations==
Given the nature of the aspect-oriented approach, it is easy draw parallels with hooking techniquesRuby hooks can accomplish the same objective as an aspect-oriented tool.  The primary aspect-oriented tools for Ruby and Java are [http://aspectr.sourceforge.net/ AspectR] and [http://www.eclipse.org/aspectj/ AspectJ] respectively.
A non-aspect-oriented implementation of something as simple as logging would require logging statements to be manually inserted wherever needed.  If you were looking for a return value of every method in every class to be output to a log file, this would become tedious, as a logging statement would have to be positioned prior to the return of each method call in the programIn addition to the insertion, if anything about the way logging was handled were changed, then each logging statement would have to also be changed individually in an error prone manner [http://www.developer.com/java/other/article.php/10936_3109831_1].  By identifying logging as a cross-cutting concern and assigning ''advice'' to address it, only the ''advice'' ever need be changed.  The advice will reside completely separate from the original code which will keep the application from being riddled with logging statements.  The primary aspect-oriented tools for Ruby and Java are [http://aspectr.sourceforge.net/ AspectR] and [http://www.eclipse.org/aspectj/ AspectJ] respectively.  Below are samples provided to illustrate the aspect-oriented approach for logging in both AspectR and AspectJ.


===AspectR versus ApectJ===
Basic Code Structure of AspectJ Aspect:
AspectR and AspectJ both facilitate the use of aspect-oriented programming in their respective languagesHow they go about doing it does differWith AspectJ, the AOP code is preprocessed prior to being compiled into bytecodeEssentially there are two compilations. With AspectR, the AOP wrapping is performed at runtime.
 
  aspect theAspect {
    pointcut joinPoints() : within("ClassHere"); # Specify the class for the advice to be applied to
    before() : joinPoints() {
      System.out.println("Jointpoint before triggering action in class."); # Statement printed before trigger
    }
    after() : joinPoints() {
      System.out.println("Jointpoint after triggering action in class"); # Statement printed after trigger
    }
  }
 
Basic Code Structure of AspectR Aspect:
 
  class theAspect < AspectR::Aspect
    def before_jointPoint(method, object, exit, *args)
      puts "Jointpoint before triggering action in class." # Statement printed before trigger
    end
    def after_jointPoint(method, object, exit, *args)
      puts "Jointpoint after triggering action in class." # Statement printed after trigger
    end
  end
 
The above code demonstrates the basic structure of the aspect definitions in AspectR and AspectJ [http://www.cs.wustl.edu/~mdeters/seminar/fall2002/notes/0918.html#Trace_v2][http://paginas.fe.up.pt/~ei01036/artigos/aop.pdf].  The following examples and output will demonstrate the code's actual usage.
 
===Example Using AspectR===
The following is a sample implementation of AspectR used for logging [http://aspectr.sourceforge.net/].  AspectR integrates fairly cleanly with standard Ruby conventions.  The aspect classes, in this case ''logger'', are subclassed from the ''Aspect'' class.  Within the user-defined aspect classes, the advice methods can be definedThe advice methods in this example are ''log_enter'' and ''log_exit''Advice methods in AspectR share the form:
 
def someAdviceMethod(method, object, exitstatus, *args)
 
Looking at each of the parameters, ''Method'' is the method to be wrapped (regular language expressions can be used here) and ''object'' is the object which will receive the method callExit status and additional arguments can also be specified [http://aspectr.sourceforge.net/]
 
  include AspectR
  class Logger < Aspect
    def tick; "#{Time.now.strftime('%Y-%m-%d %X')}"; end
    def log_enter(method, object, exitstatus, *args)
      $stderr.puts "#{tick} #{self.class}##{method}: args = #{args.inspect}"
    end
    def log_exit(method, object, exitstatus, *args)
      $stderr.print "#{tick} #{self.class}##{method}: exited "
      if exitstatus.kind_of?(Array)
        $stderr.puts "normally returning #{exitstatus[0].inspect}"
      elsif exitstatus == true
        $stderr.puts "with exception '#{$!}'"
      else
        $stderr.puts "normally"
      end
    end
  end
  if $0 == __FILE__
    class SomeClass
      def some_method
        sleep 1
        puts "Hello!"
        [:t, "sd"]
      end
      def some_other_method(*args)
        raise NotImplementedError
      end
    end
    Logger.new.wrap(SomeClass, :log_enter, :log_exit, /some/)
    SomeClass.new.some_method
      begin
        SomeClass.new.some_other_method(1, true)
      rescue Exception
    end
  end
 
Here is the '''very''' verbose output from the AspectR logger.  If you look at the end of the trace, you will see the output of "Hello!".
 
  /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
  /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
  class_eval '
          def some_other_method(*args,&block)
            return (__aop__-605699858_105538(*args,&block)) unless Aspect.dispatch?
            begin
            exit_status = nil
            self.class.__aop_call_advice(:PRE, 'some_other_method', self, exit_status,*args,&block)
            exit_status = []
            return (exit_status.push(__aop__-605699858_105538(*args,&block)).last)
            rescue Exception
            exit_status = true
            raise
            ensure
            self.class.__aop_call_advice(:POST, 'some_other_method', self, exit_status,*args,&block)
            end
          end
        '
  /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
  /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
  /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
  /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
  class_eval '
          def some_method(&block)
            return (__aop__-605699858_105378(&block)) unless Aspect.dispatch?
            begin
            exit_status = nil
    self.class.__aop_call_advice(:PRE, 'some_method', self, exit_status,&block)
    exit_status = []
    return (exit_status.push(__aop__-605699858_105378(&block)).last)
    rescue Exception
    exit_status = true
    raise
    ensure
    self.class.__aop_call_advice(:POST, 'some_method', self, exit_status,&block)
    end
  end
'
  /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
  /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
  '''Hello!'''


===Example Using AspectJ===
The following is a sample implementation of AspectJ used for logging [http://www.developer.com/java/other/article.php/10936_3109831_1].  As in the AspectR example, upon closer examination it is fairly easy to follow what is taking place.  Immediately within the TraceAspect ''aspect'', the Java logger is initialized and the ''pointcut'' traceMethods are defined.  The "!within(TraceAspect)" tells the code to not perform the logging trace within the ''aspect'' code since it will be performing it everywhere else as indicated by the wildcards.  TraceMethods will reach every method and output the timestamp, class and method name, along with an indication that the method is being "entered."


public aspect ExampleAspect {
  import java.util.logging.*;
     before() : execution(void Account.credit(float)) {
  import org.aspectj.lang.*;
         System.out.println("About to perform credit operation");
  public aspect TraceAspect {
     private Logger _logger = Logger.getLogger("trace");
    pointcut traceMethods()
      : execution(* *.*(..)) && !within(TraceAspect);
    before() : traceMethods() {
      Signature sig = thisJoinPointStaticPart.getSignature();
      _logger.logp(Level.INFO, sig.getDeclaringType().getName(),
         sig.getName(), "Entering");
     }
     }
    declare parents: Account implements BankingEntity;
  }
    declare warning : call(void Persistence.save(Object))
 
        : "Consider using Persistence.saveOptimized()";
Notice the familiar class-like structure of the aspect as laid out in AspectJ.  Appearances can be deceiving. While having much in common with classes, there are also differences as pointed out by the AspectJ developers.
}


Similarities include [http://www.eclipse.org/aspectj/doc/released/faq.html]:
*Aspects have type
*Aspects can extend classes and other aspects
*Aspects can be abstract or concrete
*Non-abstract aspects can be instantiated
*Aspects can have static and non-static state and behavior
*Aspects can have fields, methods, and types as members
*The members of non-privileged aspects follow the same accessibility rules as those of classes


Differences include [http://www.eclipse.org/aspectj/doc/released/faq.html]:
*Aspects can additionally include as members pointcuts, advice, and inter-type declarations;
*Aspects can be qualified by specifying the context in which the non-static state is available
*Aspects can't be used interchangeably with classes
*Aspects don't have constructors or finalizers, and they cannot be created with the new operator; they are automatically available as needed.
*Privileged aspects can access private members of other types


The following simple driver program demonstrates the use of the above aspect:


public class Hello {
  public class Tester {
     public static void main(String[] args) {
     public void doSomething() {
         System.out.println("Hello, world!");
         System.out.print("Test Output.");
     }
     }
}
  }
 
  public static void main(String[] args) {
    Tester sample = new Tester();
    sample.doSomething();     
  }
 
  Output:
  Jun 10, 2008 12:16:16 AM Tester doSomething
  INFO: Entering
  Test Output.
 
==Conclusions==
With a good understanding of the concepts of native hooking and aspect-oriented programming in place, we can now answer two important questions.  The first being what are the differences between AspectR and AspectJ and how do these differences make programming in one more convenient than the other.  The second deals with how native hooking provided by Ruby differs from a formal aspect-oriented programming tool.
 
===AspectR versus ApectJ===
AspectR and AspectJ both facilitate the use of aspect-oriented programming in their respective languages.  How they go about doing so does differ.  Beneath the surface in AspectJ, the AOP code is preprocessed prior to being compiled into bytecode.  So, essentially there are two compilations that must occur [Feldt].  In the case of AspectR, the AOP wrapping is performed dynamically at runtime, which does create additional overhead, but adds flexibility.  In addition, as mentioned above, aspects in AspectR are truly classes while aspects in AspectJ are implemented as an extension of the language.  By using native techniques, you can accomplish a great deal without having to learn additional syntax [http://www.brainbell.com/tutorials/java/Applying_Some_Structure.htm].  This demonstrates the power and dynamic nature of Ruby in that the AOP can be performed using its existing implementation, but the extended nature of AspectJ allows for optimizations which do not affect the performance of the underlying application [http://paginas.fe.up.pt/~ei01036/artigos/aop.pdf].  In addition, when defining aspects, AspectR supports the use of regular expressions but AspectJ has its own specialized syntax.
 
Another important factor to consider is that AspectR is still in its infancy, as it is an alpha release, and is therefore not as well developed as AspectJ, at least at this point in time.  The creators of AspectR admit that it is currently missing minor features present in AspectJ [http://aspectr.sourceforge.net/].  However, many of these minor shortcomings can be programmed around using the dynamic nature of Ruby [http://paginas.fe.up.pt/~ei01036/artigos/aop.pdf].  It is important to note that while AspectR may be lacking in some of the features present in AspectJ, its code base is considerably smaller at around 300 lines [http://www.brainbell.com/tutorials/java/Applying_Some_Structure.htm].  General consensus among Ruby developers is that the somewhat slow development of AOP tools for Ruby is due to the ability of the language to implement a great deal of AOP functionality natively.  The native structure of AspectR makes aspect-oriented programming far more convenient than with AspectJ.
 
===Hooks versus Aspect-Oriented Programming Tools===
Returning to the subject of hooking, given the nature of the aspect-oriented approach, it is easy to draw parallels with hooking techniques.  In addition to interceptors [http://aralbalkan.com/336] and [http://en.wikipedia.org/wiki/Mixins mixins], Ruby hooks manage to cover a significant amount of what is present in separate AOP tools with the native Ruby object-oriented implementation [http://www.brainbell.com/tutorials/java/Applying_Some_Structure.htm].  Hooking in Ruby allows the injection of code for key events, but it does not allow for the specification of pointcuts as provided by aspect-oriented tools [http://www.brainbell.com/tutorials/java/Applying_Some_Structure.htm][http://aspectr.sourceforge.net/].  Also, aspects with AspectR do not require editing of the class, and a single aspect has the ability to affect different classes in different ways [http://www.eclipse.org/aspectj/doc/released/faq.html].  Recall in the earlier hooking example that the ''puts'' method had to be aliased and then modifications made within the method to achieve the desired effect.  It is easy to see how hooking could create points within the code which would require maintenance during the lifecycle of the application, which is what we are trying to get away from.  When using the formal AOP tool, no modification of the class is required, it is only necessary to extend AspectR and wrap the class for action to be performed upon.  As previously mentioned, while not as complete as an AOP tool, Ruby offers the facilities to come fairly close with very little modification.


===Resources for Additional Information===
==References and Resources for Additional Information==
*Programming Ruby: The Pragmatic Programmers' Guide, Thomas
*Ruby Developer's Guide, Feldt et al
*[http://aosd.net/ Aspect-Oriented Software Development]
*[http://www.eclipse.org/aspectj/doc/released/faq.html AspectJ FAQ]
*[http://aspectr.sourceforge.net/ AspectR Homepage]
*[http://www.eclipse.org/aspectj/ AspectJ Homepage]
*[http://paginas.fe.up.pt/~ei01036/artigos/aop.pdf Aspect-Oriented Programming - Comparing Programming Languages]
*[http://www.developer.com/java/other/article.php/3109831 Simplify Your Logging with AspectJ]
*[http://www.cs.wustl.edu/~mdeters/seminar/fall2002/notes/0918.html#Trace_v2 Sample AspectJ Implementations]
*[http://aralbalkan.com/336 AOP vs. The Interceptor vs. The Template]

Latest revision as of 00:29, 12 June 2008

Introduction

Hooks and aspect-oriented programming are two techniques which allow code to be modified on a large scale with minimal effort. Both approaches endeavour to provide a single point of modification in order to eliminate the need to make subtle changes throughout the code base. I will start by explaining these concepts in detail and then proceed to look at two implementations using aspect-oriented tools for both Ruby and Java. The example code will help to clarify certain points. I will also address how hooking is different from aspect-oriented programming as well as discuss the differences between AspectR and AspectJ, the AOP tools provided for Ruby and Java.

Hooking Concept

The hooking mechanism in Ruby allows events inside a running program to be identified while providing an action that will occur upon detection of the specified event. In this way, hooks serve as a detector of actions within the code, when event x is encountered, y will be performed. It is important to note that while hooking is possible in Ruby, it is not supported in Java. A few examples of where such a technique would prove useful include:

  • Recording when or how many times a method is called
  • Recording when an object is created
  • Debugging problematic code
  • Enhancing functionality of existing code [1]

Example of Hooking System Calls

The following is an example of a hook being inserted to perform an action when the program makes a system call. In order for this to occur, an alias must be created so that the original functionality can still be accessed. In this case, original_system is aliased to the unaltered system. Once this is done, we can freely modify the definition of system to perform just about any operation desired. This is similar to the way in which a nefarious hook, known as a rootkit, would work by still performing the intended operation of the code, but also appending something extra in the process... In our current non-malicious case, the system method has been modified to print the command being executed ("ls -al" in this case), the timestamp, and then finally executing the original instruction and sending the output to the command line [Thomas].

 module Kernel
   alias_method :original_system, :system
   def system(*args)
     puts "Your program executed the \"#{args.join(', ')}\" command." 
     puts "It was executed at: " + `date`
     puts "The command output the following: "
     original_system(*args)
   end
 end
 
 system("ls -al")
 Output:
 Your program executed the "ls -al" command.
 It was executed at: Mon Jun  2 21:19:27 EDT 2008
 The command output the following: 
 total 20
 drwxr-xr-x  2 brking brking 4096 2008-06-02 21:19 .
 drwxr-xr-x 18 brking brking 4096 2008-06-02 21:19 ..
 -rw-r--r--  1 brking brking  134 2008-06-01 22:01 fib.rb
 -rw-r--r--  1 brking brking  235 2008-06-02 20:29 hooking.rb
 -rw-r--r--  1 brking brking  287 2008-06-02 21:19 moreHooking.rb

Example of Hooking a Class Method

Similarly, a method could be hooked to perform additional tasks upon calling the method [Thomas]. The method would still carry out its original functionality, but the new instructions would precede or follow. In this example, the puts method of the IO class is hooked in order to print an additional statement to standard output every time puts is called. As in the previous example, the original method is aliased so that it can still be referenced after making enhancements. It is important to note that if you accidentally used :puts for the additional statement instead of :original_puts, you would create an infinite loop calling the modified puts [Thomas].

 class IO
   alias_method :original_puts, :puts
   def puts(*args)
     original_puts(*args)
     original_puts("You didn't ask for permission to speak!")
   end
 end
 puts "Hi there."
 Output:
 Hi there.
 You didn't ask for permission to speak!

Aspect-Oriented Programming Concept

Aspect-oriented programming deals with abstracting operations in code which are independent of the main purpose or function of the code. As an illustration, a program may have two completely unrelated classes, an automobile class and a plant class. Setting aside for a moment trying to conceive of an application where the two would reside together, there could potentially be similar data or operations desired from objects across both classes. For example, it might be worthwhile knowing the time in which each object gets instantiated, logging events on both objects, or even telling when they pass out of scope, These three commonalities are associated with each object of the class, but have nothing to do specifically with the functionality of the object. Clearly there are many other transcending operations that could be performed on either class.

In the aspect-oriented vernacular, this notion of functionality which transcends individual classes is known as a cross-cutting concern. In this way, it applies to multiple facets of the code, but does not specifically deal with the business or core concerns of the code. Jointpoints or pointcuts are the specific locations in the code where a cross-cutting of core concerns takes place. It is at these points, using aspect-oriented tools, that advice will be inserted. Advice is simply a function or group of functions for performing a task at this crossroads in the code. The aspect embodies the combination of the jointpoint and advice within the program. In the previous examples mentioned, there would be aspects for timestamps, event logging, and scope tracking [2]. It is important to note that the creation of aspects leaves the original underlying class intact and simply provides a wrapper which will encompass it. This ability to work on existing code without alteration has the added advantage of making it more adaptable to future changes and enhancement requests later in the application's life cycle. Aspect-orientation does not in any way undermine object-orientation. Aspect-oriented programming complements object-oriented programming in that OOP governs top-down relationships while AOP governs left-right relationships within the application [3].

Aspect-Oriented Programming Implementations

A non-aspect-oriented implementation of something as simple as logging would require logging statements to be manually inserted wherever needed. If you were looking for a return value of every method in every class to be output to a log file, this would become tedious, as a logging statement would have to be positioned prior to the return of each method call in the program. In addition to the insertion, if anything about the way logging was handled were changed, then each logging statement would have to also be changed individually in an error prone manner [4]. By identifying logging as a cross-cutting concern and assigning advice to address it, only the advice ever need be changed. The advice will reside completely separate from the original code which will keep the application from being riddled with logging statements. The primary aspect-oriented tools for Ruby and Java are AspectR and AspectJ respectively. Below are samples provided to illustrate the aspect-oriented approach for logging in both AspectR and AspectJ.

Basic Code Structure of AspectJ Aspect:

 aspect theAspect {
   pointcut joinPoints() : within("ClassHere"); # Specify the class for the advice to be applied to 
   before() : joinPoints() {
     System.out.println("Jointpoint before triggering action in class."); # Statement printed before trigger
   }
   after() : joinPoints() {
     System.out.println("Jointpoint after triggering action in class"); # Statement printed after trigger
   }
 }

Basic Code Structure of AspectR Aspect:

 class theAspect < AspectR::Aspect
   def before_jointPoint(method, object, exit, *args)
     puts "Jointpoint before triggering action in class." # Statement printed before trigger
   end
   def after_jointPoint(method, object, exit, *args)
     puts "Jointpoint after triggering action in class." # Statement printed after trigger
   end
 end

The above code demonstrates the basic structure of the aspect definitions in AspectR and AspectJ [5][6]. The following examples and output will demonstrate the code's actual usage.

Example Using AspectR

The following is a sample implementation of AspectR used for logging [7]. AspectR integrates fairly cleanly with standard Ruby conventions. The aspect classes, in this case logger, are subclassed from the Aspect class. Within the user-defined aspect classes, the advice methods can be defined. The advice methods in this example are log_enter and log_exit. Advice methods in AspectR share the form:

def someAdviceMethod(method, object, exitstatus, *args)

Looking at each of the parameters, Method is the method to be wrapped (regular language expressions can be used here) and object is the object which will receive the method call. Exit status and additional arguments can also be specified [8]

 include AspectR
 class Logger < Aspect
   def tick; "#{Time.now.strftime('%Y-%m-%d %X')}"; end
   def log_enter(method, object, exitstatus, *args)
     $stderr.puts "#{tick} #{self.class}##{method}: args = #{args.inspect}"
   end
   def log_exit(method, object, exitstatus, *args)
     $stderr.print "#{tick} #{self.class}##{method}: exited "
     if exitstatus.kind_of?(Array)
       $stderr.puts "normally returning #{exitstatus[0].inspect}"
     elsif exitstatus == true
       $stderr.puts "with exception '#{$!}'"
     else
       $stderr.puts "normally"
     end
   end
 end
 if $0 == __FILE__
   class SomeClass
     def some_method
       sleep 1
       puts "Hello!"
       [:t, "sd"]
     end
     def some_other_method(*args)
       raise NotImplementedError
     end
   end
   Logger.new.wrap(SomeClass, :log_enter, :log_exit, /some/)
   SomeClass.new.some_method
     begin
       SomeClass.new.some_other_method(1, true)
     rescue Exception
   end
 end

Here is the very verbose output from the AspectR logger. If you look at the end of the trace, you will see the output of "Hello!".

 /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
 /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
 class_eval '
         def some_other_method(*args,&block)
           return (__aop__-605699858_105538(*args,&block)) unless Aspect.dispatch?
           begin
            exit_status = nil
            self.class.__aop_call_advice(:PRE, 'some_other_method', self, exit_status,*args,&block)
            exit_status = []
            return (exit_status.push(__aop__-605699858_105538(*args,&block)).last)
           rescue Exception
            exit_status = true
            raise
           ensure
            self.class.__aop_call_advice(:POST, 'some_other_method', self, exit_status,*args,&block)
           end
         end
       '
 /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
 /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
 /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
 /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
 class_eval '
         def some_method(&block)
           return (__aop__-605699858_105378(&block)) unless Aspect.dispatch?
           begin
            exit_status = nil

self.class.__aop_call_advice(:PRE, 'some_method', self, exit_status,&block) exit_status = [] return (exit_status.push(__aop__-605699858_105378(&block)).last) rescue Exception exit_status = true raise ensure self.class.__aop_call_advice(:POST, 'some_method', self, exit_status,&block) end end '

 /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
 /usr/local/lib/site_ruby/aspectr.rb:229: warning: Object#id will be deprecated; use Object#object_id
 Hello!

Example Using AspectJ

The following is a sample implementation of AspectJ used for logging [9]. As in the AspectR example, upon closer examination it is fairly easy to follow what is taking place. Immediately within the TraceAspect aspect, the Java logger is initialized and the pointcut traceMethods are defined. The "!within(TraceAspect)" tells the code to not perform the logging trace within the aspect code since it will be performing it everywhere else as indicated by the wildcards. TraceMethods will reach every method and output the timestamp, class and method name, along with an indication that the method is being "entered."

 import java.util.logging.*;
 import org.aspectj.lang.*;
 public aspect TraceAspect {
   private Logger _logger = Logger.getLogger("trace");
   pointcut traceMethods()
     : execution(* *.*(..)) && !within(TraceAspect);
   before() : traceMethods() {
     Signature sig = thisJoinPointStaticPart.getSignature();
     _logger.logp(Level.INFO, sig.getDeclaringType().getName(),
       sig.getName(), "Entering");
   }
 }

Notice the familiar class-like structure of the aspect as laid out in AspectJ. Appearances can be deceiving. While having much in common with classes, there are also differences as pointed out by the AspectJ developers.

Similarities include [10]:

  • Aspects have type
  • Aspects can extend classes and other aspects
  • Aspects can be abstract or concrete
  • Non-abstract aspects can be instantiated
  • Aspects can have static and non-static state and behavior
  • Aspects can have fields, methods, and types as members
  • The members of non-privileged aspects follow the same accessibility rules as those of classes

Differences include [11]:

  • Aspects can additionally include as members pointcuts, advice, and inter-type declarations;
  • Aspects can be qualified by specifying the context in which the non-static state is available
  • Aspects can't be used interchangeably with classes
  • Aspects don't have constructors or finalizers, and they cannot be created with the new operator; they are automatically available as needed.
  • Privileged aspects can access private members of other types

The following simple driver program demonstrates the use of the above aspect:

 public class Tester {
   public void doSomething() {
       System.out.print("Test Output.");
   }
 }
 
 public static void main(String[] args) {
   Tester sample = new Tester();
   sample.doSomething();       
 }
 Output:
 Jun 10, 2008 12:16:16 AM Tester doSomething
 INFO: Entering
 Test Output.

Conclusions

With a good understanding of the concepts of native hooking and aspect-oriented programming in place, we can now answer two important questions. The first being what are the differences between AspectR and AspectJ and how do these differences make programming in one more convenient than the other. The second deals with how native hooking provided by Ruby differs from a formal aspect-oriented programming tool.

AspectR versus ApectJ

AspectR and AspectJ both facilitate the use of aspect-oriented programming in their respective languages. How they go about doing so does differ. Beneath the surface in AspectJ, the AOP code is preprocessed prior to being compiled into bytecode. So, essentially there are two compilations that must occur [Feldt]. In the case of AspectR, the AOP wrapping is performed dynamically at runtime, which does create additional overhead, but adds flexibility. In addition, as mentioned above, aspects in AspectR are truly classes while aspects in AspectJ are implemented as an extension of the language. By using native techniques, you can accomplish a great deal without having to learn additional syntax [12]. This demonstrates the power and dynamic nature of Ruby in that the AOP can be performed using its existing implementation, but the extended nature of AspectJ allows for optimizations which do not affect the performance of the underlying application [13]. In addition, when defining aspects, AspectR supports the use of regular expressions but AspectJ has its own specialized syntax.

Another important factor to consider is that AspectR is still in its infancy, as it is an alpha release, and is therefore not as well developed as AspectJ, at least at this point in time. The creators of AspectR admit that it is currently missing minor features present in AspectJ [14]. However, many of these minor shortcomings can be programmed around using the dynamic nature of Ruby [15]. It is important to note that while AspectR may be lacking in some of the features present in AspectJ, its code base is considerably smaller at around 300 lines [16]. General consensus among Ruby developers is that the somewhat slow development of AOP tools for Ruby is due to the ability of the language to implement a great deal of AOP functionality natively. The native structure of AspectR makes aspect-oriented programming far more convenient than with AspectJ.

Hooks versus Aspect-Oriented Programming Tools

Returning to the subject of hooking, given the nature of the aspect-oriented approach, it is easy to draw parallels with hooking techniques. In addition to interceptors [17] and mixins, Ruby hooks manage to cover a significant amount of what is present in separate AOP tools with the native Ruby object-oriented implementation [18]. Hooking in Ruby allows the injection of code for key events, but it does not allow for the specification of pointcuts as provided by aspect-oriented tools [19][20]. Also, aspects with AspectR do not require editing of the class, and a single aspect has the ability to affect different classes in different ways [21]. Recall in the earlier hooking example that the puts method had to be aliased and then modifications made within the method to achieve the desired effect. It is easy to see how hooking could create points within the code which would require maintenance during the lifecycle of the application, which is what we are trying to get away from. When using the formal AOP tool, no modification of the class is required, it is only necessary to extend AspectR and wrap the class for action to be performed upon. As previously mentioned, while not as complete as an AOP tool, Ruby offers the facilities to come fairly close with very little modification.

References and Resources for Additional Information