Feeds:
Posts
Comments

Archive for March, 2009

Project Bluprint

I am pleased to announce that Project Bluprint is now up and running.  The project web-site is here http://bluprint.sourceforge.net/Bluprint is a pragmatic Agile-MDA code generator.  It is a fuss-free tool supporting code generation from UML modelling tools.  It supports model/code merging features so that engineers can easily extend and maintain generated code.  Bluprint is unique as it has been developed using itself.  From the site you can download the software or get involved as a developer.

Read Full Post »

Bluprint Icon

Bluprint


I just wanted to update you on some of the exciting things that have been happening since my last post. Previously I mentioned that I am incubating an open source software project. That project is called Bluprint. Bluprint is an MDA tool designed to support Agile MDA. Key-points are:

  1. It is built using itself – although to the end user this isn’t too relevant, it does mean it has been put through its paces and can accommodate non-trivial software projects. The current Bluprint software model and code contains over a hundred classes.
  2. It makes very few uses of UML stereotypes. I was aiming to use none, but I had to concede and adopted one, called <<nogen>>. This is used to prevent the generation of code from some parts of the model and is useful when you are modelling third-party APIs.
  3. It supports full lifecycle software development, you can generate code from models, amend the generated code, amend the model and re-generate. All user modifications to the code are preserved because the tool supports intelligent model/code merging. There is no need to use sub-classing for user written extensions or annotations to mark and protect sections of the code.
  4. Bluprint interprets UML models to create Java code currently (but other target languages will be possible in the future). It supports:
    • Classes
    • Interfaces
    • Enumerations
    • Inheritance, both extends and implements
    • Associations
    • Generation of member variables and corresponding accessors.
      • unary members
      • non-unary members, from associations with cardinality greater than one
      • qualified associations
    • Generation of constructors
    • Generation of stub-methods, including throws clauses

Family Tree Example

Let me show you Bluprint in action, to give you a taste of some of Bluprint’s capabilities. It will demonstrate some of Bluprint’s code generation and model/code merging features showing Agile MDA in action.

Imagine we are going to create a program for the creation and management of family trees. In the following example we’ll be able to create people and define their descendants.

  • The Initial Model
    We start by modelling a simple class for representing people in our family tree called Person, which initially looks like this.
    Person class step 1

    Person class step 1


    Bluprint has been developed, initially and tested, with MagicDraw 16.0. The model is created in MagciDraw and exported as an EMF UML 2 (v1.x) XMI file for use by Bluprint. Bluprint is designed to work with any tool that can export UML models in this format.

    After running Bluprint for the first time, the following code is generated. Notice the generation of member variables, accessors and stub methods for main(), print() and the constructor.

    package org.family;
    
    /**
     * Class Person has been generated from a UML model by @author Bluprint.
     * Date: Tue Mar 24 15:18:23 GMT 2009
     */
     
    import java.util.Date;
    import java.lang.String;
    
    
    public class Person {
    
    	private String name;	
    
    	private Date dob;	
    
    
    	public Person(String name) {	
    		// TODO - Auto-generated
    	}
    
    	public static void main(String[] args) {
    		// TODO - Auto-generated
    	}
    
    	public void print() {
    		// TODO - Auto-generated
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String value) {
    		this.name = value;
    	}
    
    	public Date getDob() {
    		return dob;
    	}
    
    	public void setDob(Date value) {
    		this.dob = value;
    	}
    }


  • Edit the Code
    We fill in the details of the stub methods, as follows. For the constructor we just set the name of the Person.

    	public Person(String name) {	
    		this.setName( name );
    	}


    For now the print() method is very simple

    	public void print() {
    		System.out.println( getName() );
    	}


    And in main() we create a single Person object and ask it to print itself

    	public static void main(String[] args) {
    		Person fred = new Person( "Fred" );
    		
    		fred.print();
    	}


    At the moment the model and the code only support the definition of a single stand-alone person. Executing the program we get the following output.

    Fred

  • Amend the Model and Code Merge
    Now we have the basics working, we want to be able to define the descendants of a Person. We amend the model to add a one to many associations from Person to Person. One end represents the parent and the other the descendants. We could have made it many to many, but I wanted to show that Bluprint can handle associations of different cardinalities.
    Person class step 2

    Person class step 2


    We also add a new private method for printing. Note, we don’t need to add this to the model, we could have added it straight to the code, but it is easy to do it here while we are thinking about it.

    We export the model again and run Bluprint against the new model and our original code. Bluprint interprets both and generates the following result for the Person class.

    package org.family;
    
    /**
     * Class Person has been generated from a UML model by @author Bluprint.
     * Date: Tue Mar 24 15:44:16 GMT 2009
     */
     
    import org.family.Person;
    import java.util.Date;
    import java.lang.String;
    import java.util.Vector;
    
    
    public class Person {
    
    	private String name;	
    
    	private Date dob;	
    
    	private Person parent;	
    
    
    	private Vector descendants = new Vector();
    	
    
    	public Person(String name) {	
    		this.setName( name );
    	}
    
    	public static void main(String[] args) {
    		Person fred = new Person( "Fred" );
    		
    		fred.print();
    	}
    
    	public void print() {
    		System.out.println( getName() );
    	}
    
    	private void print(Person person, int depth) {
    		// TODO - Auto-generated
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String value) {
    		this.name = value;
    	}
    
    	public Date getDob() {
    		return dob;
    	}
    
    	public void setDob(Date value) {
    		this.dob = value;
    	}
    
    	public Person getParent() {
    		return parent;
    	}
    
    	public void setParent(Person value) {
    		this.parent = value;
    	}
    
    	public void addDescendant(Person aDescendant) {
    		this.descendants.add( aDescendant );
    	}
    
    	public void removeDescendant(Person aDescendant) {
    		this.descendants.remove( aDescendant );
    	}
    
    	public Person getDescendantAt(int index) {
    		return this.descendants.get( index );
    	}
    
    	public int getDescendantsSize() {
    		return this.descendants.size();
    	}
    }


    Notice the original code changes have been preserved. Bluprint has helpfully added member variables for the parent and the descendants. Some helper methods have been created to allow descendants to be added and removed. In addition members of the descendants collection can also be accessed.

    (The sharp eyed amongst you will notice the Bluprint understands plural names for collections and has created singular forms when adding and removing individual members. So although the Person has many descendants, you add, remove and get individual descendants.)

    A stub method has also been created for the private print() method, which will be used for indenting the name of a person based upon their depth in a descendant tree.

  • Edit the code again
    We add a printIndented() function

    	private void printIndented( String string, int tabs ) {
    		for( int i = 0; i < tabs; i++ ) {
    			System.out.print(  "\t" );
    		}
    		System.out.println( string );
    	}


    We don’t need to add every method to the model since Bluprint is designed to be pragmatic. You can decide what to include in your model and what to leave out as detailed implementation. Bluprint provides reporting so you can identify what has been coded and not included in the model.

    Next we amend our print() methods to look like this

    	public void print() {
    		print( this, 0 );
    	}
    
    	private void print(Person person, int depth) {
    		printIndented( person.getName(), depth );
    
    		for( int i = 0; i < person.getDescendantsSize(); i++ ) {
    			print( person.getDescendantAt( i ), depth + 1 );
    		}
    	}


    Finally we set up some test data in the our main() function

    	public static void main(String[] args) {
    		Person fred = new Person( "Fred" );
    		Person mary = new Person( "Mary ");
    		Person bill = new Person( "Bill" );
    		Person benn = new Person( "Benn" );
    		Person jill = new Person( "Jill" );
    		
    		fred.addDescendant( mary );
    		fred.addDescendant( bill );
    		mary.addDescendant( benn );
    		mary.addDescendant( jill );
    		
    		fred.print();
    	}


    When we run the program we get the following output

    Fred
    	Mary 
    		Benn
    		Jill
    	Bill


    Summary
    The above is a very simple example, but it does demonstrate some of the powerful features of Bluprint including code generation and intelligent model/code merging. Bluprint removes the burden of creating and maintaining a lot of boilerplate code. The engineer can focus on the value add parts of their task which is the coding of the business logic. The power of Bluprint comes into its own for much larger, real world, models containing many classes, interfaces, associations and inheritance.

    Read Full Post »

  • Things have been a little quiet lately in blog-world as I have been incubating an open source model driven project – watch this space. This has been motivated after taking a look at AndroMDA. Although AndroMDA is excellent I have a number of quibbles. Some of these aren’t unique just to AndroMDA.

    1. Lack of ‘eating your own dog food’. If an MDA tool is worth its salt, surely it must be capable of being used to generate itself from a model. None of the tools that I have seen contain a UML model that describes the software that can be used to generate itself. Obviously there needs to be a bootstrapping process, but once the kernel of the tool is written it can be used to further refine and develop the tool.

      It would be a pretty good acid test, since it would prove that the tool is sophisticated and pragmatic enough to develop a moderately complex application. It addition it would be put through its paces as the model for the application would go through multiple edit, generate and test cycles. (Oh did I mention Agile-MDA? MDA doesn’t preclude being used within an agile development project. I’ll expand upon Agile-MDA in a future blog entry if there is interest.).

    2. AndroMDA makes use of stereotypes to help drive the code generation process. For example the classes within a model being used to represent entities, those classes that are to be made persistent, are flagged with the «entity» stereotype. This helps direct the code generator to create the appropriate persistent code. To this end, the model must include the AndroMDA profile containing the valid AndroMDA stereotypes.

      Although the use of stereotypes provides expressiveness, it does mean that there is a deviation between the classes represented in the model and classes that get generated as code. The MDA purists would argue that this level of abstraction and transformation is acceptable.

      I personally prefer adopted a WYSIWIG (what you see is what you get) approach, where the model is a pretty faithful representation of the classes that will be generated. For example it is possible to model the frameworks and APIs that will be exploited by the implementation model. I argue that by modelling the frameworks and APIs you get a better insight into the structure of that which is being extended and used. In this way you can see clearly the relationship between the classes in your model and those of the frameworks that you’ll exploit.

    3. I’m not a fan of using inheritance to separate generated code and developer written code. We all realise that to be pragmatic, that developers will need to write code in addition to that which is generated. UML (+OCL) isn’t practical for detailed program description.
      Tools like AndroMDA allow developer written code to be contained in an ‘implementation’ class that inherits from a generated class. The generated class can be changed and regenerated, without loosing the developer written code in the inherited implementations class.

      Simple Inheritance
      However this creates an artificial separation between the generated and user written code. If I wish to sub-class the generated class, I can, but I will loose the extensions in the developer written implementation class. There are techniques for getting around this, but this is an extra burden which most developers cannot be bothered with.

      The answer I feel is not to use sub-classing, but to allow developer written code and generated code to be contained within the same class. The tool should be able to merge user written code with generated code so that no developer written code is lost. This is also in line with my previous point, that the model represents more closely what is being generated.

    4. Support of OO concepts. Most tools like AndorMDA can generate simple attributes and accessors (getters/setters). For example if I have a Customer class and an attribute called ‘id’ of type long, this will be faithfully generated. However when we start to use more complex modelling constructs such as a 1 to many association between classes or qualified associations these tools do not provide much assistance.

      Customer and Product
      For instance if the afore mentioned Customer class is associated with a Product class via a 1 to many association as shown, most tools will for create something like the following when generating code for Java,

      public Vector products = new Vector();

      Sometimes it may be declared private and a pair of accessors to the products variable will be provided. However in both cases, the implementation generated does not enforce encapsulation, something as all good OO practitioners we know is a good thing and would want.

      What would be better, would be for the collection to be encapsulated and for the following kind of interface with corresponding implementation to be generated,

      public void addProduct( Product aProduct );
      public void removeProduct( Product aProduct );
      public Iterator getProductsIterator();

      And perhaps optionally,

      public int getProductsSize();
      public Product getProductAt( int index );

      Notice that we don’t break encapsulation. We can add and remove Products to the Customer, and we also provide mechanisms for iterating over the Products associated with the Customer.

      In an ideal world we would also like qualified UML associations to also be supported. These are very useful for representing key, value pairs between classes.

      With this, a lot of boilerplate code can be generated removing the burden from the developer and enabling then to concentrate on creating the unique business logic.

    Read Full Post »