Print
Hello World

Introduction

This tutorial will guide you through a simple example on how to create, define and weave an aspect into your application.

You can download source for this example here. If you still haven't done it yet, get the SetPoint binaries at the Downloads section.

Sample application

We first need to create a sample application, into which aspects will later be woven. The application is a very simple console application written in C#. It just asks you for a number, and then prints it to the console (not exactly a Hello World ).

MainClass.cs
using System;
using setPoint.programAnnotation;

namespace HelloWorldAspect {
    class MainClass {
        static void Main() {
            IOManager ioManager = new IOManager();
            ioManager.printToConsole("Enter a number: ");
	    int number = ioManager.readNumber();
	    ioManager.printToConsole("You`ve entered " + number + "\n");			
        }
    }
    
    class IOManager {
        [ProgramAnnotation("semantics://architecture#output")]
	public void printToConsole(string str) {
	    Console.Write(str);
	}

	[ProgramAnnotation("semantics://architecture#input")]
	public int readNumber() {
	    int number = int.Parse(Console.ReadLine());
	    return number;
	}
    }
}
What are those bizarre-looking attributes?

SetPoint allows you to write both good-old AspectJ-type pointcuts (based on syntax) and Semantics-based pointcuts. In order to use the latter, you need to annotate your base code with some kind of semantics information. In this case, we're using design-time information (describing the architecture used for the solution). If you read on, you'll later realize just how intuitive pointcuts look like when based on this kind of annotations.

LENDL

Pointcuts, aspects, advices & the ontologies that will be used for semantics reasoning all live inside a configuration assembly. This is a regular .NET assembly and can therefore be written using any compatible language. But in order to make the configuration process a bit more straightforward, we have created the LENDL language. It's a simple domain-specific declarative programming language that lets you easily define pointcuts, advices, ontologies & aspect interfaces. I know, you still don't get it. Let's see if things become a little clearer by looking at the LENDL code that corresponds to the example:

helloWorldConfig.lendl
/************************************/
/* Logging Aspect                   */
/************************************/

pointcut InputMessages{
    message is [semantics://architecture#input];
}
    
pointcut OutputMessages{        
    message is [semantics://architecture#output];
}
    
aspect LoggingAspect{
    event startLogging;
    event endLogging;
}
    
advice LogEvents : LoggingAspect{       
    trigger startLogging before {InputMessages, OutputMessages};
    trigger endLogging after {InputMessages, OutputMessages};
}

Let us know look at the different things this code does (or even better taking into account it is declarative, asks someone to be done):

Pointcut definition
extracted from helloWorldConfig.lendl
pointcut InputMessages{
    message is [semantics://architecture#input];
}

Pointcuts are basically sets of joinpoints or well defined points during the execution of a program. As such, they can be defined by using either extensional or intensional predicates. We're now defining one such predicate, in order to refer to all methods that have to do with outputting information.

What is the word "message" doing in my pointcut?

Messages are basically a more purist word for what we normally refer to by method. You should be able to find a nicer explanation of why we did that by reading the section on Our joinpoint model.

Writing the aspect

You then have to write the code that will be woven into your application. In this example we've made a simple logging aspect, which writes runtime information to a text file every time it is called.

LoggingAspect.cs
using System;
using System.IO;
using setPoint.messageReifying;
using setPoint.weaving;

namespace LENDLConfiguration {
    public class LoggingAspect : IAspect {
        private DateTime _startTime;        

        public LoggingAspect() {
        }

        public void startLogging(IJoinPoint aJoinpoint) {
            this._startTime = DateTime.Now;         
        }

        public void endLogging(IJoinPoint aJoinpoint) {
            DateTime _endTime = DateTime.Now;           
            string message =
                "-----------------------------------\n" + 
                "Message: " + aJoinpoint.message.uri + "\n" +
                " Sender: " + aJoinpoint.sender.uri + "\n" +
                " Receiver: " + aJoinpoint.receiver.uri + "\n" + 
                " Start: " + this._startTime + " End: " + _endTime;
            writeLineToLog(message);
        }

        private static void writeLineToLog(string message)
        {
            StreamWriter file = new StreamWriter("helloWorldAspect.log", true);
            file.WriteLine(message);
            file.Close();
        }

        public bool mustCancelExecution {
            get { return false; }
        }
    }
}

Defining semantic pointcuts

Semantic pointcuts and ontologies

If you are interested in Semantics-based pointcuts, you'll need to make an ontology. I made a very simple one just for this example. It merely contains a class IO with two derived classes input and output.

In order to graphically write ontologies I strongly recommend using Protégé. This will generate a .owl file.

In the example it was:

helloWorld.owl
<?xml version="1.0"?>
<rdf:RDF
    xmlns="semantics://architecture#"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
    xmlns:owl="http://www.w3.org/2002/07/owl#"
  xml:base="semantics://architecture">
  <owl:Ontology rdf:about=""/>
  <owl:Class rdf:ID="IO"/>
  <owl:Class rdf:ID="output">
    <rdfs:subClassOf rdf:resource="#IO"/>
  </owl:Class>
  <owl:Class rdf:ID="input">
    <rdfs:subClassOf rdf:resource="#IO"/>
  </owl:Class>
</rdf:RDF>

<!-- Created with Protege (with OWL Plugin 2.1, Build 284)  http://protege.stanford.edu -->

Building the example

Compiling .lendl files

In first place it is necesary to compile the helloWorld.lendl file to C# code. This is done with the LENDc.bat included in setpoint distribution. Just copy the file to de LENLDc dir in SetPoint dist, and run the batch file. Then a C# source file is generated. Put this file with our sources.

Building

To build the application you'll need to copy setpoint libs to the bin directory of the example, you'll also need NAnt. A buildfile is provided in the bin directory. Just open a console window, change the current directory to bin and run nant. The default target builds the sample application and makes preweaving. Preweaving is made by an NAnt task for that purpose.

If you only want build only (no preweaving) just type: nant build

Running and Debbugging

Just run HelloWorld.exe and enjoy !

After execution you'll see a text file in the same directory containing the log.

Debbugging

Since SetPoint preweaver removes debbugging simbols during preweaving, it is not possible to debug the application directly.

It is necesary to disasembly the binary and assembly it againg with debug simbols (round trip). Then you can debug the application using ClrDbg. You'll have to debug IL.

How do i do round trip ?

The round trip can be done using ildasm and _ilasm.

There is a target provided in the build file for doing roundtrip (dissasembly, assembly) called round.

Conclusion

In this tutorial you (hopefully) learned to:

  1. Write a simple aspect in C#
  2. Define semantic pointcuts using LENDL
  3. Preweave your application
  4. Building
  5. Running and debugging
Powered by Atlassian Confluence