Generating a MEAN-style web application from models (T plus 1 days)

This is the last installment to a series started about two weeks ago. If you remember, I set off to build a code generator that could produce 100% of the code for a business application targeting the MEAN (for Mongo, Express, Node.js and Angular) stack. I am writing this a day after the presentation took place.

So, the big day finally arrived. Unfortunately, the presentation was not one of my best. If my delivery was not that great, to make matters worse, turns out my idea of exposing MDD (with Javascript as target platform) to a crowd seeking wisdom on Javascript didn’t work very well, people didn’t seem be interested in modeling and code generation at all. Something to take into account in the future. Anyways, the slides (in Portuguese) appear below this post.

Also, the code generator was not complete (was anyone else surprised?), so I couldn’t show code running for many features, and instead had to show the generated code (that looked almost right but not quite there yet). I guess that contributed to a less interesting presentation.

On the bright side, a lot of progress on the code generator was made. Take a look at the latest state of the generated code. I am quite proud of what is there now. But there is still more progress to be made until at least the sample applications I have all translate to feature complete and correct MEAN applications.

You will hear more about that here in the near future. Stay tuned.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Generating a MEAN-style web application from models (T minus 3 days)

This is another installment to a short series started about 10 days ago. If you remember, I am building a code generator that can produce 100% of the code for a business application targeting the MEAN (for Mongo, Express, Node.js and Angular) stack.

What happened since the last update

Since the previous post, a lot of progress has been made. Much of the code for a domain model class is being generated (including state machine support), as you can see below (bear in mind there are still quite a few gaps – but I still have 3 days!):

    var EventEmitter = require('events').EventEmitter;        

    /**
     * An issue describes a problem report, a feature request or just a work item for a project. Issues are reported by and
     * assigned to users, and go through a lifecycle from the time they are opened until they are resolved and eventually
     * closed. 
     */
    var issueSchema = new Schema({
        summary : String,
        issueId : Number,
        issueKey : String,
        reportedOn : Date,
        severity : Severity,
        status : Status,
        resolution : Resolution,
        resolvedOn : Date,
        votes : Number,
        commentCount : Number,
        waitingFor : String,
        mine : Boolean,
        free : Boolean,
        description : String
    });
    
    /*************************** ACTIONS ***************************/
    
    /**
     *  Report a new issue. 
     */
    issueSchema.statics.reportIssue = function (project, summary, description, severity) {
        newIssue = new Issue();
        newIssue.summary = summary;
        newIssue.description = description;
        newIssue.severity = severity;
        newIssue.reporter = User.current;
        newIssue.project = project;
        newIssue.userNotifier.issueReported(newIssue.issueKey, summary, description, newIssue.reporter.email);
    };
    
    /**
     *  Release the issue so another committer can work on it. 
     */
    issueSchema.methods.release = function () {
        this.assignee = null;
    };
    
    /**
     *  Assign an issue to a user. 
     */
    issueSchema.methods.assign = function (newAssignee) {
        this.assignee = newAssignee;
    };
    
    /**
     *  Suspend work on this issue. 
     */
    issueSchema.methods.suspend = function () {};
    
    /**
     *  Start/resume work on this issue. 
     */
    issueSchema.methods.start = function () {};
    
    /**
     *  Resolve the issue. 
     */
    issueSchema.methods.resolve = function (resolution) {
        this.resolvedOn = new Date();
        this.resolution = resolution;
    };
    
    /**
     *  Reopen the issue. 
     */
    issueSchema.methods.reopen = function (reason) {
        this.resolvedOn = null;
        this.resolution = null;
        if (reason.notEquals("")) {
            this.comment(reason);
        }
    };
    
    /**
     *  Add a comment to the issue 
     */
    issueSchema.methods.comment = function (text) {
        this.addComment(text, null);
    };
    
    issueSchema.methods.addWatcher = function (userToAdd) {
        this.issuesWatched = userToAdd;
    };
    
    issueSchema.methods.vote = function () {
        this.voted = User.current;
    };
    
    issueSchema.methods.withdrawVote = function () {
        delete this.voted;
    };
    
    /**
     *  Take over an issue currently available. 
     */
    issueSchema.methods.assignToMe = function () {
        this.assignee = User.current;
    };
    
    /**
     *  Take over an issue currently assigned to another user (not in progress). 
     */
    issueSchema.methods.steal = function () {
        this.assignee = User.current;
    };
    
    /**
     *  Close the issue marking it as verified. 
     */
    issueSchema.methods.verify = function () {
    };
    /*************************** QUERIES ***************************/
    
    issueSchema.statics.bySeverity = function (toMatch) {
        return this.model('Issue').find().where('severity').eq(toMatch).exec();
    };
    
    issueSchema.statics.byStatus = function (toMatch) {
        return Issue.filterByStatus(this.model('Issue').find(), toMatch).exec();
    };
    /*************************** DERIVED PROPERTIES ****************/
    
    
    issueSchema.methods.getIssueKey = function () {
        return this.project.token + "-" + this.issueId;
    };
    
    issueSchema.methods.getVotes = function () {
        return  ;
    };
    
    issueSchema.methods.getCommentCount = function () {
        return  ;
    };
    
    issueSchema.methods.getWaitingFor = function () {
        return "" +  + " day(s)";
    };
    
    issueSchema.methods.getMine = function () {
        return User.current == this.assignee;
    };
    
    issueSchema.methods.getFree = function () {
        return this.assignee == null;
    };
    /*************************** PRIVATE OPS ***********************/
    
    issueSchema.methods.referenceDate = function () {
        if (this.resolvedOn == null) {
            return new Date();
        } else  {
            return this.resolvedOn;
        }
    };
    
    issueSchema.statics.filterByStatus = function (issues, toMatch) {
        return issues.where('status').eq(toMatch);
    };
    
    issueSchema.methods.addComment = function (text, inReplyTo) {
        comment = new Comment();
        comment.user = User.current;
        comment.on = new Date();
        comment.commented = text;
        comment.inReplyTo = inReplyTo;
        this.issue = comment;
        this.userNotifier.commentAdded(this.issueKey, comment.user.email, this.reporter.email, text);
    };
    /*************************** STATE MACHINE ********************/
    Issue.emitter.on('resolve', function () {
        if (this.status == 'Open') {
            this.status = 'Resolved';
            return;
        }
        if (this.status == 'Assigned') {
            this.status = 'Resolved';
            return;
        }
    });     
    
    Issue.emitter.on('assignToMe', function () {
        if (this.status == 'Open') {
            this.status = 'Assigned';
            return;
        }
    });     
    
    Issue.emitter.on('assign', function () {
        if (this.status == 'Open') {
            this.status = 'Assigned';
            return;
        }
    });     
    
    Issue.emitter.on('suspend', function () {
        if (this.status == 'InProgress') {
            this.status = 'Assigned';
            return;
        }
    });     
    
    Issue.emitter.on('release', function () {
        if (this.status == 'Assigned') {
            this.status = 'Open';
            return;
        }
    });     
    
    Issue.emitter.on('steal', function () {
        if (this.status == 'Assigned') {
            this.status = 'Assigned';
            return;
        }
    });     
    
    Issue.emitter.on('start', function () {
        if (this.status == 'Assigned') {
            this.status = 'InProgress';
            return;
        }
    });     
    
    Issue.emitter.on('verify', function () {
        if (this.status == 'Resolved') {
            this.status = 'Verified';
            return;
        }
    });     
    
    Issue.emitter.on('reopen', function () {
        if (this.status == 'Verified') {
            this.status = 'Open';
            return;
        }
    });     
    
    var Issue = mongoose.model('Issue', issueSchema);
    Issue.emitter = new EventEmitter();

This is just for one of the entity classes defined in the ShipIt example application, here is an excerpt of the original model that covers the Issue entity:

(* 
    An issue describes a problem report, a feature request or just a work item for a project.
    Issues are reported by and assigned to users, and go through a lifecycle from the time 
    they are opened until they are resolved and eventually closed.
*)
class Issue

    attribute summary : String;

    derived id attribute issueId : Integer;

    derived attribute issueKey : String := {
        self.project.token + "-" + self.issueId
    };

    attribute labels : Label[*];

    attribute project : Project;

    port userNotifier : UserNotifier;

    readonly attribute reportedOn : Date := { Date#today() };

    readonly attribute reporter : User;

    readonly attribute assignee : User[0, 1];

    attribute severity : Severity := Major;

    attribute status : Status;

    readonly attribute resolution : Resolution[0, 1];

    readonly attribute resolvedOn : Date[0, 1];

    readonly attribute comments : Comment[*];

    attribute watchers : User[*];

    derived attribute votes : Integer := {
        self<-VotedIssues->voters.size()
    };

    derived attribute commentCount : Integer := { self.comments.size() };

    derived attribute waitingFor : String := {
        "" + self.reportedOn.differenceInDays(self.referenceDate()) + " day(s)"
    };

    private derived attribute mine : Boolean := {
        User#current == self.assignee
    };

    private derived attribute free : Boolean := { self.assignee == null };

    private query referenceDate() : Date;
    begin
        if (self.resolvedOn == null) then
            return Date#today()
        else
            return self.resolvedOn;
    end;

    attribute description : Memo;

    (* Report a new issue. *)
    static operation reportIssue(project : Project, summary : String, description : Memo, 
            severity : Severity := Normal)
        precondition Must_be_logged_in { User#provisioned };
    begin
        var newIssue;
        newIssue := new Issue;
        newIssue.summary := summary;
        newIssue.description := description;
        newIssue.severity := severity;
        newIssue.reporter := User#current;
        newIssue.project := project;
        send IssueReported(
            issueKey := newIssue.issueKey, 
            summary := summary, 
            description := description, 
            userEmail := newIssue.reporter.email) to newIssue.userNotifier;
    end;

    static query bySeverity(toMatch : Severity) : Issue[*];
    begin
        return Issue extent.select((i : Issue) : Boolean {
            i.severity == toMatch
        });
    end;

    private static query filterByStatus(issues : Issue[*], toMatch : Status) : Issue[*];
    begin
        return issues.select((issue : Issue) : Boolean {
            issue.status == toMatch
        });
    end;

    static query byStatus(toMatch : Status) : Issue[*];
    begin
        return Issue#filterByStatus(Issue extent, toMatch);
    end;

    (* Release the issue so another committer can work on it. *)
    operation release()
        precondition { self.mine };
    begin
        self.assignee := null;
    end;

    (* Assign an issue to a user. *)
    operation assign(newAssignee : User)
        precondition { self.mine or self.free };
    begin
        self.assignee := newAssignee;
    end;

    (* Suspend work on this issue. *)
    operation suspend()
        precondition { self.mine };

    (* Start/resume work on this issue. *)
    operation start()
        precondition { self.mine };

    (* Resolve the issue. *)
    operation resolve(resolution : Resolution := Fixed)
        precondition { self.mine or self.free };
    begin
        self.resolvedOn := Date#today();
        self.resolution := resolution;
    end;

    (* Reopen the issue. *)
    operation reopen(reason : Memo);
    begin
        self.resolvedOn := null;
        self.resolution := null;
        if (reason != "") then
            self.comment(reason);
    end;

    (* Add a comment to the issue *)
    operation comment(text : Memo);
    begin
        self.addComment(text, null);
    end;

    private operation addComment(text : Memo, inReplyTo : Comment);
    begin
        var comment;
        comment := new Comment;
        comment.user := User#current;
        comment.\on := Date#today();
        comment.commented := text;
        comment.inReplyTo := inReplyTo;
        link IssueComments(issue := self, comments := comment);
        send CommentAdded(
            issueKey := self.issueKey, 
            author := comment.user.email, 
            userEmail := self.reporter.email, 
            comment := text) to self.userNotifier;
    end;

    operation addWatcher(userToAdd : User);
    begin
        link WatchedIssues(issuesWatched := self, watchers := userToAdd);
    end;

    operation vote()
        precondition { not (User#current == null) }
        precondition { ! self.mine }
        precondition {
            ! self<-VotedIssues->voters.includes(User#current)
        };
    begin
        link VotedIssues(voted := self, voters := User#current);
    end;

    operation withdrawVote()
        precondition { not (User#current == null) }
        precondition {
            self<-VotedIssues->voters.includes(User#current)
        };
    begin
        unlink VotedIssues(voted := self, voters := User#current);
    end;

    (* Take over an issue currently available. *)
    operation assignToMe()
        precondition { User#current.committer }
        precondition { not self.mine };
    begin
        self.assignee := User#current;
    end;

    (* Take over an issue currently assigned to another user (not in progress). *)
    operation steal()
        precondition { User#current.committer }
        precondition { not (self.mine) };
    begin
        self.assignee := User#current;
    end;

    (* Close the issue marking it as verified. *)
    operation verify();
    begin
    end;

    statemachine Status

        initial state Open
            transition on call(resolve) to Resolved;
            transition on call(assignToMe) to Assigned;
            transition on call(assign) to Assigned;
        end;

        state InProgress
            transition on call(suspend) to Assigned;
        end;

        state Assigned
            transition on call(release) to Open;
            transition on call(resolve) to Resolved;
            transition on call(steal) to Assigned;
            transition on call(start) to InProgress;
        end;

        state Resolved
            transition on call(verify) to Verified;
        end;

        state Verified
            transition on call(reopen) to Open;
        end;

    end;

end;

More examples here.

Xtend is still making the difference

What an awesome language for writing code generators Xtend is. See, for example, how simple is the code to implement state machine support in Javascript, including guard conditions on transitions, and automatically triggering of on-entry and on-exit behavior:

    def generateStateMachine(StateMachine stateMachine, Class entity) {
        val stateAttribute = entity.findStateProperties.head
        if (stateAttribute == null) {
            return ''
        }
        val triggersPerEvent = stateMachine.findTriggersPerEvent
        triggersPerEvent.entrySet.map[generateEventHandler(entity, stateAttribute, it.key, it.value)].join('\n')
    }
    
    def generateEventHandler(Class entity, Property stateAttribute, Event event, List triggers) {
        val modelName = entity.name;     
        '''
        «modelName».emitter.on('«event.generateName»', function () {
            «IF (triggers.exists[(it.eContainer as Transition).guard != null])»
            var guard;
            «ENDIF»
            «triggers.map[generateHandlerForTrigger(entity, stateAttribute, it)].join('\n')»
        });     
        '''
    }
    
    def generateHandlerForTrigger(Class entity, Property stateAttribute, Trigger trigger) {
        val transition = trigger.eContainer as Transition
        val originalState = transition.source
        val targetState = transition.target
        '''
        «transition.generateComment»if (this.«stateAttribute.name» == '«originalState.name»') {
            «IF (transition.guard != null)»
            guard = «generatePredicate(transition.guard)»;
            if (guard()) {
                «generateStateTransition(stateAttribute, originalState, targetState)»
            }
            «ELSE»
            «generateStateTransition(stateAttribute, originalState, targetState)»
            «ENDIF»
        }'''
    }
    
    def generateStateTransition(Property stateAttribute, Vertex originalState, Vertex newState) {
        '''
        «IF (originalState instanceof State)»
            «IF (originalState.exit != null)»
            (function() «generateActivity(originalState.exit as Activity)»)();
            «ENDIF»
        «ENDIF»
        this.«stateAttribute.name» = '«newState.name»';
        «IF (newState instanceof State)»
            «IF (newState.entry != null)»
            (function() «generateActivity(newState.entry as Activity)»)();
            «ENDIF»
        «ENDIF»
        return;
        '''
    }
    
    def generateName(Event e) {
        switch (e) {
            CallEvent : e.operation.name
            SignalEvent : e.signal.name
            TimeEvent : '_time'
            AnyReceiveEvent : '_any'
            default : unsupportedElement(e)
        }
    }

Next steps

Sorry if I am being a bit too terse. Will be glad to reply to any questions or comments, but time is running out fast and there is lots to be done still. For the next update, I hope to have Express route generation for the REST API and hopefully will have started on test suite generation. Let’s see how that goes!

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Checking the current state of a UML state machine

In Cloudfier, we use UML as the core language for building business applications. UML is usually well-equipped for general purpose business domain-centric application modeling, but that doesn’t mean it always does everything needed out of the box.

Case at hand: assuming one is developing an expense reporting application and modeled an expense’s status as a state machine (in TextUML):

class Expense
    /* ... */
    attribute status : Status;
    operation review();
    operation approve();
    operation reject();
    operation submit();

    statemachine Status
        initial state Draft
            transition on call(submit) to Submitted;
        state Submitted
            transition on call(approve) to Approved
            transition on call(reject) to Rejected
            transition on call(review) to Draft;
        terminate state Approved;
        terminate state Rejected;        
    end;
end;

How do you model the following in UML?

Show me all expenses that are waiting for approval.

Turns out there is no support in UML for reasoning based on the current state of a state machine.

Creative modeling

So, what do you do when UML does not have a language element that you need? You extend it, in our case, using a stereotype applicable to the LiteralNull metaclass (in TextUML):

stereotype VertexLiteral extends LiteralNull
    property vertex : Vertex;
end;

So, a vertex literal is a value specification, more specifically, a variant of LiteralNull, that can refer to a Vertex, which is a metaclass that represents the states (including pseudo-states) in a state machine.

Notation, notation

In terms of notation, I chose to make State/Vertex literals look like enumeration literals: Status#Approved or Status#Draft. So, back to the original question, this is how you could model a query that returns all expenses that are in the Submitted state:

    static operation findAllSubmitted() : Expense[*];
    begin 
        return Expense extent.select ((e : Expense) : Boolean {
            return e.status == Status#Submitted
        });
    end;

If you are thinking to yourself: I didn’t know UML had queries or closures!?, well, it usually doesn’t. See the posts on SQL queries in UML and Closures in UML for some background on this.

Note also that if you wanted to refer to the symbol Status from a class different than the one enclosing it you will need to qualify it (i.e. Expense::Status#Submitted).

Show me more!

You can run the Expenses application showing state machines and state-based queries in Cloudfier right now (login is “guest” or any of the employee names you will see later).

The entire Expenses sample application (currently 150 lines of generously spaced TextUML) is available on BitBucket. You can also easily check it out into Cloudfier so you can run your own copy of the application on the web (there is nothing to install). Give it a try!

What do you think?

Your feedback (questions, support or criticism) to any of the ideas presented in this post is very welcome.

UPDATE: I started a thread on the subject on the UML Forum group, and turns out you can do this kind of reasoning in OCL, but indeed, not in UML itself. Well, now you can.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

MDD meets TDD (part II): Code Generation

Here at Abstratt we are big believers of model-driven development and automated testing. I wrote here a couple of months ago about how one could represent requirements as test cases for executable models, or test-driven modeling. But another very interesting interaction between the model-driven and test-driven approaches is test-driven code generation.

You may have seen our plan for testing code generation before. We are glad to report that that plan has materialized and code generation tests are now supported in AlphaSimple. Follow the steps below for a quick tour over this cool new feature!

Create a project in AlphaSimple

First, you will need a model so you can generate code from. Create a project in AlphaSimple and a simple model.


package person;

enumeration Gender 
  Male, Female
end; 

class Person
    attribute name : String; 
    attribute gender : Gender; 
end;

end.

Enable code generation and automated testing

Create a mdd.properties file in your project to set it up for code generation and automated testing:


# declares the code generation engine
mdd.target.engine=stringtemplate

# imports existing POJO generation template projects
mdd.importedProjects=http://cloudfier.com/alphasimple/mdd/publisher/rafael-800/,http://cloudfier.com/alphasimple/mdd/publisher/rafael-548/

# declares a code generation test suite in the project
mdd.target.my_tests.template=my_tests.stg
mdd.target.my_tests.testing=true

# enables automated tests (model and templates)
mdd.enableTests=true

Write a code generation test suite

A code generation test suite has the form of a template group file (extension .stg) configured as a test template (already done in the mdd.properties above).

Create a template group file named my_tests.stg (because that is the name we declared in mdd.properties), with the following contents:


group my_tests : pojo_struct;

actual_pojo_enumeration(element, elementName = "person::Gender") ::= "<element:pojoEnumeration()>"

expected_pojo_enumeration() ::= <<
enum Gender {
    Male, Female
}
>>

A code generation test case is defined as a pair of templates: one that produces the expected contents, and another that produces the actual contents. Their names must be expected_<name> and actual_<name>. That pair of templates in the test suite above form a test case named “pojo_enumeration”, which unsurprisingly exercises generation of enumerations in Java. pojo_enumeration is a pre-existing template defined in the “Codegen – POJO templates” project, and that is why we have a couple of projects imported in the mdd.properties file, and that is why we declare our template suite as an extension of the pojo_struct template group. In the typical scenario, though, you may would have the templates being tested and the template tests in the same project.

Fix the test failures

If you followed the instructions up to here, you should be seeing a build error like this:



Line	File		Description
3	my_tests.stg	Test "pojo_enumeration" failed: [-public -]enum Gender {n Male, Femalen}

which is reporting the code generated is not exactly what was expected – the template generated the enumeration with an explicit public modifier, and your test case did not expect that. Turns out that in this case, the generated code is correct, and the test case is actually incorrect. Fix that by ensuring the expected contents also have the public modifier (note that spaces, newlines and tabs are significant and can cause a test to fail). Save and notice how the build failure goes away.

That is it!

That simple. We built this feature because otherwise crafting templates that can generate code from executable models is really hard to get right. We live by it, and hope you like it too. That is how we got the spanking new version of the POJO target platform to work (see post describing it and the actual project) – we actually wrote the test cases first before writing the templates, and wrote new test cases whenever we found a bug – in the true spirit of test-driven code generation.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Can you tell this is 100% generated code?

Can you tell this code was fully generated from a UML model?

This is all live in AlphaSimple – every time you hit those URLs the code is being regenerated on the fly. If you are curious, the UML model is available in full in the TextUML’s textual notation, as well as in the conventional graphical notation. For looking at the entire project, including the code generation templates, check out the corresponding AlphaSimple project.

Preconditions

Operation preconditions impose rules on the target object state or the invocation parameters. For instance, for making a deposit, the amount must be a positive value:


operation deposit(amount : Double);
precondition (amount) { return amount > 0 }
begin
    ...
end;

which in Java could materialize like this:


public void deposit(Double amount) {
    assert amount > 0;
    ...
}

Not related to preconditions, another case assertions can be automatically generated is if a property is required (lowerBound > 0):


public void setNumber(String number) {
    assert number != null;
    ...
}

Imperative behavior

In order to achieve 100% code generation, models must specify not only structural aspects, but also behavior (i.e. they must be executable). For example, the massAdjust class operation in the model is defined like this:


static operation massAdjust(rate : Double);
begin
    Account extent.forEach((a : Account) { 
        a.deposit(a.balance*rate) 
    });
end;

which in Java results in code like this:


public static void massAdjust(Double rate) {
    for (Account a : Account.allInstances()) {
        a.deposit(a.getBalance() * rate);
    };
}

Derived properties

Another important need for full code generation is proper support for derived properties (a.k.a. calculated fields). For example, see the Account.inGoodStanding derived attribute below:


derived attribute inGoodStanding : Boolean := () : Boolean { 
    return self.balance >= 0 
};

which results in the following Java code:


public Boolean isInGoodStanding() {
    return this.getBalance() >= 0;
}

Set processing with higher-order functions

Any information management application will require a lot of manipulation of sets of objects. Such sets originate from class extents (akin to “#allInstances” for you Smalltalk heads) or association traversals. For that, TextUML supports the higher-order functions select (filter), collect (map) and reduce (fold), in addition to forEach already shown earlier. For example, the following method returns the best customers, or customers with account balances above a threshold:


static operation bestCustomers(threshold : Double) : Person[*];
begin
    return
        (Account extent
            .select((a:Account) : Boolean { return a.balance >= threshold })
            .collect((a:Account) : Person { return a->owner }) as Person);
end;        

which even though Java does not yet support higher-order functions, results in the following code:


public static Set<Person> bestCustomers(Double threshold) {
    Set<Person> result = new HashSet<Person>();
    for (Account a : Account.allInstances()) {
        if (a.getBalance() >= threshold) {
            Person mapped = a.getOwner();
            result.add(mapped);
        }
    }
    return result;
}

which demonstrates the power of select and collect. For an example of reduce, look no further than the Person.totalWorth attribute:


derived attribute totalWorth : Double := () : Double {
    return (self<-PersonAccounts->accounts.reduce(
        (a : Account, partial : Double) : Double { return partial + a.balance }, 0
    ) as Double);
};  

which (hopefully unsurprisingly) maps to the following Java code:


public Double getTotalWorth() {
    Double partial;
    partial = 0;
    for (Account a : this.getAccounts()) {
        partial = partial + a.getBalance();
    }
    return partial;
}

Would you hire AlphaSimple?

Would you hire a developer if they wrote Java code like AlphaSimple produces? For one thing, you can’t complain about the guy not being consistent. :) Do you think the code AlphaSimple produces needs improvement? Where?

Want to try by yourself?

There are still some bugs in the code generation that we need to fix, but overall the “POJO” target platform is working quite well. If you would like to try by yourself, create an account in AlphaSimple and to make things easier, clone a public project that has code generation enabled (like the “AlphaSimple” project).

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

MDD meets TDD: mapping requirements as model test cases

Executable models, as the name implies, are models that are complete and precise enough to be executed. One of the key benefits is that you can evaluate your model very early in the development life cycle. That allows you to ensure the model is generally correct and satisfies the requirements even before you have committed to a particular implementation platform.

One way to perform early validation is to automatically generate a prototype that non-technical stakeholders can play with and (manually) confirm the proposed model does indeed satisfy their needs (like this).

Another less obvious way to benefit from executable models since day one is automated testing.

The requirements

For instance, let’s consider an application that needs to deal with money sums:

  • REQ1: a money sum is associated with a currency
  • REQ2: you can add or subtract two money sums
  • REQ3: you can convert a money sum to another currency given an exchange rate
  • REQ4: you cannot combine money sums with different currencies

The solution

A possible solution for the requirements above could look like this (in TextUML):

package money;

class MixedCurrency
end;

class Money
  attribute amount : Double;
  attribute currency : String;
  
  static operation make(amount : Double, currency : String) : Money;
  begin 
      var m : Money;
      m := new Money;
      m.amount := amount;
      m.currency := currency;
      return m;
  end;
  
  operation add(another : Money) : Money;
  precondition (another) raises MixedCurrency { return self.currency = another.currency }
  begin
      return Money#make(self.amount + another.amount, self.currency);
  end;
  
  operation subtract(another : Money) : Money;
  precondition (another) raises MixedCurrency { return self.currency = another.currency }  
  begin
      return Money#make(self.amount - another.amount, self.currency);      
  end;
  
  operation convert(anotherCurrency : String, exchangeRate : Double) : Money;
  begin
      return Money#make(self.amount * exchangeRate, anotherCurrency);
  end;  
end;
        
end.

Now, did we get it right? I think so, but don’t take my word for it.

The proof

Let’s start from the beginning, and ensure we satisfy REQ1 (a money sum is a pair <amount, currency>:

[Test]
operation testBasic();
begin
    var m1 : Money;
    m1 := Money#make(12, "CHF");
    Assert#assertEquals(12, m1.amount);
    Assert#assertEquals("CHF", m1.currency);
end;

It can’t get any simpler. This test shows that you create a money object providing an amount and a currency.

Now let’s get to REQ2, which is more elaborate – you can add and subtract two money sums:

[Test]
operation testSimpleAddAndSubtract();
begin
    var m1 : Money, m2 : Money, m3 : Money, m4 : Money;
    m1 := Money#make(12, "CHF");
    m2 := Money#make(14, "CHF");

    m3 := m1.add(m2);    
    Assert#assertEquals(26, m3.amount);
    Assert#assertEquals("CHF", m3.currency);
    
    /* if m1 + m2 = m3, then m3 - m2 = m1 */
    m4 := m3.subtract(m2);
    Assert#assertEquals(m1.amount, m4.amount);
    Assert#assertEquals(m1.currency, m4.currency);
end;

We add two values, check the result, them subtract one of them from the result and expect the get the other.

REQ3 is simple as well, and specifies how amounts can be converted across currencies:

[Test]
operation testConversion();
begin
    var m1 : Money, result : Money;
    m1 := Money#make(3, "CHF");
    result := m1.convert("USD", 2.5);
    Assert#assertEquals(7.5, result.amount);
    Assert#assertEquals("USD", result.currency);
end;

We ensure conversion generates a Money object with the right amount and the expected currency.

Finally, REQ4 is not a feature, but a constraint (currencies cannot be mixed), so we need to test for rule violations:

[Test]
operation testMixedCurrency();
begin
    try
        Money#make(12, "CHF").add(Money#make(14, "USD")); 
        /* fail, should never get here */
        Assert#fail("should have failed");
    catch (expected : MixedCurrency)
        /* success */
    end;
end;

We expect the operation to fail due to a violation of a business rule. The business rule is identified by an object of a proper exception type.

There you go. Because we are using executable models, even before we decided what implementation platform we want to target, we already have a solution in which we have a high level of confidence that it addresses the domain-centric functional requirements for the application to be developed.

Can you say “Test-driven modeling”?

Imagine you could encode all non-technical functional requirements for the system in the form of acceptance tests. The tests will run against your models whenever a change (to model or test) occurs. Following the Test-Driven Development approach, you alternate between encoding the next requirement as a test case and enhancing the model to address the latest test added.

Whenever requirements change, you change the corresponding test and you can easily tell how the model must be modified to satisfy the new requirements. If you want to know why some aspect of the solution is the way it is, you change the model and see the affected tests fail. There is your requirement traceability right there.

See it by yourself

Would you like to give the mix of executable modeling and test-driven development a try? Sign up to AlphaSimple now, then open the public project repository and clone the “Test Infected” project (or just view it here).

P.S.: does this example model look familiar? It should – it was borrowed from “Test Infected: Programmers Love Writing Tests“, the classical introduction to unit testing, courtesy of Beck, Gamma et al.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Help wanted: Converting closure-based iterators into plain loops

Smalltalk, Ruby, Groovy and other languages allow one to implement loops using closures. But so does TextUML/UML. Given the primary use case of TextUML/UML is to generate code, one thorny question is how to generate code from a UML model using closures for implementing loops through collections into a language, like Java or C, just as one would normally write loops over collections in those closure-free languages.

Here are some examples of how to translate from closure-based loops (in TextUML, but the specific syntax shouldn’t matter) to ordinary loops (in Java, but again, syntax specifics shouldn’t matter):

forEach

In TextUML


self->units.forEach((u : Unit) { 
    link ProjectUnits(project := clone, units := u.clone()) }
);


In Java


for (Unit u : this.getUnits()) { 
    clone.addUnits(u.clone());
}

select

In TextUML


return Project extent.select((p : Project) : Boolean { return p.shared });

In Java


Set<Project> result = new HashSet<Project>();
for (Project p : Project.allInstances()) { 
    if (p.isShared()) {
        result.add(p);
    }
} 
return result;

collect

In TextUML


return Project extent.collect((p : Project) : User { return p->owner });

In Java


Set<User> result = new HashSet<User>();
for (Project p : Project.allInstances()) { 
    User owner = p.getOwner();
    result.add(owner);
} 
return result;

count

In TextUML


return Project extent.count((p : Project) : Boolean { return p.shared });

In Java


int count = 0;
for (Project p : Project.allInstances()) { 
    if (p.isShared()) {
        count++;
    }
} 
return count;

In AlphaSimple, we got much of what is needed above in place. There are though some additional challenges posed by the need of chaining those collection primitives, and the need for mapping the data flow that chains them together to an unchained form, using local variables in the target language. These last two aspects have been keeping me awake at night. If you feel like throwing a light (with strategies, references) on how to address that, by all means go for it, it is pretty dark in here right now… :)

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Productivity of UML

Found an old discussion on the MDSN site about a study on the productivity of UML, brought up by the DSM folks. You can see some of the common caveats raised in this comment by MetaCase’s Steve Kelly. Please read his points and come back here.

I actually didn’t notice it was an old thread and replied to it. Call me cheap, but I hate perfectly good arguments going to waste on a dead thread, so I am recycling my original response (now deleted) here as a blog post.

1) repeat with me, UML is not a graphical language – it has a graphical notation, but others are allowed. Criticism of UML as a whole based on the productivity issues around the graphical notation is cherry picking or (at best) a misinformed opinion. If you don’t like the default notation, create one (like we did!) to suit your taste (and it will still be UML). The specs are public, and there are good open source implementations of the metamodel, that are used by many tools.

2) you don’t need to give up on the semantics of UML to map a modeled class to multiple artifacts. That is just plain OO design mapping to real-world implementation technologies. UML is an OO language first and foremost.

3) There is no need to mix languages, UML has support for both structural and behavioral modeling (since 2002!). Action languages are not (or don’t have to be) “other languages” – but just a textual notation on top of the existing abstract syntax and semantics. That is not a marketing ploy, incorporating elements of the Shlaer-Mellor approach was just a sound strategic decision that made UML much better.

4) Annotations (or stereotypes) is an established (see C#, Java) and cost effective way of tailoring a general purpose language to one’s needs. Not everything calls for a DSL. Both approaches have pros and cons, one has to pick what is best for the situation at hand.

5) All the stories of failure or limited success with generating code from UML models I heard or read are caused by the decision of ignoring behavioral modeling in UML and doing partial code generation. That is a losing proposition, no matter the modeling language. Again, just like the notation issue, analyzing UML productivity based exclusively on those narrow minded cases is at best spreading misinformation. Kudos to MetaCase for promoting full code generation, that is the way to go. But full code generation is not an exclusivity of DSL, the Executable UML folk (and other modeling communities) have been doing it successfully for a long time as well.

Can we move away from the pissing contest between modeling approaches? That got old ages ago. There are way more commonalities than differences between DSM and executable modeling with GPLs like UML, productivity gains included. There is room for both approaches, and it would not be wise to limit oneself to one or another.

What is your opinion? Are you still using old school UML and limiting yourself to generating stubs? Why on earth haven’t you moved to the new world of executable models yet?

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Model-driven Development with Executable UML models

Last November I did a lecture on Model-driven Development with Executable UML models to a class of Software Engineering undergrad students at UVic. Here are the slides:

I think it gives a good summary of my views on model driven development (with Executable UML or not):

  • even though problem domains are typically not very complex, enterprise software is complex due to the abundance of secondary crosscutting concerns (persistence, concurrency, security, transactions etc)
  • there are two dominant dimensions in enterprise software: business domain concerns and technological concerns
  • they are completely different in nature (change rate, abstraction level) and require different approaches (tools, skills, reuse)
  • MDD is a strategy that handles well that divide: models address business domain concerns, PIM->PSM transformation addresses technological concerns
  • brainstorming, communication, documentation and understanding (rev. engineering) are not primary goals of MDD – to produce running code in a productive and rational way is
  • models in MDD must be well-formed, precise, complete, executable, technology independent
  • graphical representations are not suitable for executable modeling (textual notations are much better)
  • diagrams != models, text != code (that would look good on a t-shirt!)

I guess those who know me won’t have seen anything new above (these ideas make the very foundations of the TextUML Toolkit and AlphaSimple).

Do you agree with those positions?

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

TextUML Toolkit 1.6 declared!

The TextUML Toolkit version 1.6 has been released. It is the same RC1 build mentioned here a week ago. The listing on the Eclipse Marketplace has been updated, so in addition to the regular update site (http://abstratt.com/update/), if you are using Eclipse 3.6, you can get it even more conveniently using the brand new Eclipse Marketplace Client.

Take a look at the new notation features:

  • preconditions on operations
operation withdraw(amount : Real);
precondition { amount > 0 and amount < self.balance }
begin
    self.balance := self.balance - amount;
end;
  • derived properties
reference employees : Employee[*]

/* calculated field */
derived attribute employeeCount : Integer := ():Integer { return self->employees.size() };
  • initial values on properties
attribute available : Boolean := true;

You can also try these new features online on AlphaSimple. Sign up or start a guest session to create, validate and run your models on the spot, there is nothing to install!

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Doing away with custom UML metaclasses in TextUML

The TextUML Toolkit has since release 1.2 had a metamodel extension package (inaptly named ‘meta’). This metamodel extension package defined new metaclasses not available in UML such as:

  • closure – an activity that has another activity as context
  • conversion action – an action that flows an input directly as output just changing the type
  • literal double – a literal value for double precision numeric values
  • signature – a classifier that contained parameters, the type of a closure
  • meta value specification – a value specification for meta references
  • collection literals – a value specification that aggregates other value specifications

Turns out extending the UML metamodel by definining new packages and metaclasses is a bad idea. Some reasons (yes, I am feeling ‘bullety’):

  • it is non-standard
  • other UML tools cannot read instances of your metaclasses, some won’t read your models at all if they have *any* unknown metaclasses
  • there is little documentation on how to maintain these kinds of metamodel extensions
  • since it is not the mainstream approach, we are bound to encounter more issues

Because of that, release 1.6 will rely exclusively on profiles and stereotypes for extending the UML metamodel.

What to expect

For conventional users of the Toolkit, this change might possibly go unnoticed, barring any potential bugs introduced in the process.

People using the built-in base package and the base_profile will be directly affected. The elements in these models are still provided, by they are now in the new mdd_extensions profile, or one of the new mdd_types, mdd_collections and mdd_console packages.

We apologize for any inconvenience these changes might bring, but we strongly believe they are required to make the TextUML Toolkit a better product. If you have any trouble moving to 1.6 (to be released later this month), make sure to hit the user forums or report issues.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Can TextUML be implemented the generative way (with Xtext or EMFText)?

I have been trying to figure out whether the TextUML notation for action semantics can be dealt with properly by tools such as Xtext and EMFText (class models and state machines should be fine). For example, given this structural model fragment:

class Advertisement 
    attribute summary : String;
    attribute description : String; 
    attribute keywords : Keyword[*]; 
    attribute category : Category;  
    operation addKeyword(keywordName : String);
    static operation findByCategoryName(catName : String) : Advertisement[*];
end;

association AdvertisementKeyword
    role Advertisement.keywords;
    role advertisement : Advertisement;
end;

class Keyword specializes Object
    attribute name : String;
end;

class Category specializes Object
    attribute name : String;
    attribute description : String;
end;

association AdvertisementCategory
    role Advertisement.category;
    role ad : Advertisement[*];
end;

Notice the Advertisement class declares two operations. Their behaviour in TextUML could be written as:

    operation Advertisement.addKeyword;
    begin
        var newKeyword : Keyword; 
        newKeyword := new Keyword;
        newKeyword.name := keywordName;
        link AdvertisementKeyword(keywords := newKeyword, advertisement := self);
    end;

    operation Advertisement.findByCategoryName;
    begin  
        return Advertisement extent.select(
            (a : Advertisement) : Boolean {
                return a->AdvertisementCategory->category.name = catName; 
            }
        );
    end;

Note that TextUML allows the behavior to be specified inline when declaring an operation in a class, or in separate, as above (that explains the lack of parameters, modifiers etc).

In the resulting UML model, the behaviour of Advertisement.addKeyword would roughly map to this (using a textual pseudo-notation for UML activities that is hopefully more readable than raw XMI):

activity(name: "addKeyword") {
    structuredActivityNode {
        variable(name: "newKeyword", type: #String)
        writeVariable(variable: #newKeyword, value: createObject(class: #Keyword))
        writeAttribute(
            attribute: #Keyword.name, 
            target: readVariable(variable: #newKeyword), 
            value: readVariable(variable: #keywordName)
        )
        createLink(
            association: #AdvertisementKeyword, 
            end1: #AdvertisementKeyword.keyword, 
            end1Value: readVariable(variable: #newKeyword), 
            end2: #AdvertisementKeyword.advertisement, 
            end2Value: readSelf()
        )
    }
}

and the behaviour Advertisement.findByCategoryName would map to this:

activity(name: "findByCategoryName") {
    structuredActivityNode {
        // implicit variable for return value
        variable(name: "@result", type: #Advertisement, upperBound: *)
        // implicit variable for parameter value
        variable(name: "catName", type: #String)
        writeVariable(
            variable: #@result, 
            value: callOperation(
                operation: #Advertisement.select, 
                target: readExtent(class: #Advertisement),
                filter: metaValue(#@findByCategoryName_closure1)
            )
        )
    }
}

// a closure is an activity that has a reference to a context activity
closure(name: "@findByCategoryName_closure1") {
        // implicit variable for return value
        variable(name: "@result", type: #Boolean)
        // implicit variable for parameter value
        variable(name: "a", type: #Advertisement)
        writeVariable(
            variable: #@result, 
            value: callOperation(
                operation: #Object.equals,
                // variables from the context activity are available here
                target: readVariable(variable: #catName)
                args: readAttribute(attribute: #Category.name, target: readLink(
                    association: #AdvertisementCategory,
                    fedEnd: #Advertisement.ad,
                    fedEndValue: readVariable(variable: #a),
                    readEnd: #Advertisement.category
                ))
            )          
        )
}

Note that UML does not have closures, this is an extension to the UML metamodel which I wrote about here before.

Some background on the metaclasses involved: ReadVariableAction, CreateObjectAction, CreateLinkAction, ReadExtentAction etc are all action metaclasses. Actions are the building blocks for modeling activity behaviour in UML.

The million dollar question is: can Xtext and EMFText handle more complex textual notations like this? Is this out of the happy path? Has anyone done something similar? I am under the impression I could use Xtext or EMFText better if I used them based on a intermediate metamodel for behavior that would be closer to the concrete syntax (to get all the IDE bells and whistles for free) and then transformed that to UML in a separate step.

If you have the answers for any of these questions (or even if you have comments and questions of your own), please chime in.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

SQL queries in UML

I strongly believe queries are an essential part of a domain model. As such, in our quest to have (UML) models that can fully (yet abstractly) describe object models for the common enterprise applications, we cannot leave out first class support for queries.

But how do you do queries in UML? The obvious answer seems to be OCL, but that is not the approach I am taking as OCL and UML have serious interoperability/duplication issues. Instead, I took the  middleweight extension approach.

First, we model a protocol for manipulating collections of objects (showing only a subset here):

class Collection specializes Basic
  operation includes(object : T) : Boolean;
  operation isEmpty() : Boolean;
  operation size() : Integer;
  operation exists(predicate : {(:T) : Boolean}) : Boolean;
  operation any(predicate : {(:T) : Boolean}) : T;
  operation select(filter : {(:T) : Boolean}) : T[*];
  operation collect(mapping : {(:T) : any}) : any[*];
  operation forEach(predicate : {(:T)});
  operation union(another : T[*]) : T[*];
  (...)
end;

That protocol is available against any collection of objects, which in UML can be obtained by navigating an association, reading an attribute, invoking an operation, obtaining the extent of a class (remember Smalltalk’s allInstances), anything where the resulting value has multiplicity greater than one.

Note most of the operations in the Collection protocol take blocks/closures as arguments. Closures are used in this context to define the filtering criterion for a select, or the mapping function for a collect.

For instance, for obtaining all accounts that currently do not have sufficient funds, this method would do it:

static operation findNSFAccounts() : Account[*];
begin
    return Account extent.select(
        (a : Account) : Boolean {return a.balance < 0}
    );
end;

Note the starting collection is the extent of the Account class. That is very similar to what is done in the context of query languages for object-oriented databases, such as OQL or JDOQL. We then filter the class extent by selecting only those accounts that have a negative balance, by passing a block to the select operation.

When mapping that behavior to SQL, we could end up with a query like this:

select _account_.* from Account _account_ where _account_.balance < 0

Another example: we want to obtain all customers with a balance above a given amount, let’s say, to send them a letter to thank them for their business. The following method specifies that logic:

static operation findBestCustomers(minBalance : Real) : Customer[*];
begin
    return (Account extent.select(
          (a : Account) : Boolean { return a.balance >= minBalance }
    ).collect(
          (a : Account) : Customer { return a->AccountOwner->owner }
    ) as Customer);
end;

Note that we start off with the extent of Account class, filter it down to the accounts with good balance using select, and then map from that collection to a collection with the respective account owners by traversing an association using collect.

If that was going to be mapped to SQL, one possible mapping would be:

select _customer_.* from Account _account_
    inner join Customer _customer_
        on  _account_._accountID_ = _customer_._customerID_
    where _account_.balance >= ?

Much of this can be already modeled if you try it out with the TextUML Toolkit 1.2. But, you might ask, once you model that, what can you do with UML models containing queries like the ones shown here?

Since the models are complete (include structure and behavior), you can:

  1. Execute them. Imagine writing automated tests against your models, or letting your customer play with them before you actually start working on the implementation.
  2. Generate complete code. The generated code will include even your custom queries, not only those basic ones (findAll, findByPK) code generators can usually produce for you.

If you would like to see tools that support that vision, keep watching this blog.

So, what is your opinion?
Do you see value in being able to specify queries in your models? Is this the right direction? What would you do differently?

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Closures in UML? Extending the metamodel with the TextUML Toolkit

UML is known to be a huge language, and that has two problems: it is too complex, having way more features than most applications will ever need, and can still be insufficient, as no single language will ever cover everybody’s needs.

In the article “Customizing UML: Which Technique is Right for You?”, James Bruck and Kenn Hussey (both from the UML2 team) do a great job at covering the several options for extending (or restricting) UML (James also made a related presentation at last year’s EclipseCon, together with Christian Damus, of Eclipse OCL fame). Cutting to the chase, these are the options they identify:

  • using keywords (featherweight extensions)
  • using profiles, stereotypes and properties/tagged values (lightweight extensions)
  • extending the metamodel by specializing the existing metaclasses (middleweight extensions)
  • using package merges to select the parts of UML you need (heavyweight extensions)

Each option has its own strengths and weaknesses, as you can see in the referred article/presentation. At this time, the TextUML notation supports two of those approaches: profiles and metamodel extensions.

Adding closures to UML

Even though profiles are the most popular (and recommended) mechanism for extending UML, it is not enough in some cases/applications. That has been the case in the TextUML Toolkit, for instance, when implementing closures in the TextUML action language (yes, the Toolkit eats its own dog food).

According to the wikipedia entry, “a closure is a function that is evaluated in an environment containing one or more bound variables. When called, the function can access these variables.

It really makes sense to (meta) model a closure as some kind of UML activity, which is basically a piece of behavior that can be fully specified in UML. Methods, for instance, are better modeled in UML as activities. Activities are composed of activity nodes and actions, which are similar to blocks of code and instructions, respectively.

The only thing that is missing in the standard Activity metaclass is the ability for a closure to have an activity node from another activity as context, so it can access context’s local variables. So here is a possible (meta) modeling of closures in UML using the TextUML syntax:

[Standard::Metamodel]
model meta;

apply Standard;

(*
  A closure is a special kind of activity that has another
  activity's activity node as context. A closure might
  reference variables declared in the context activity node.
*)
[Standard::Metaclass]
class Closure specializes uml::Activity
  (* The activity node that provides context to this closure. *)
  reference contextNode : uml::StructuredActivityNode;
end;

end.

Or, for those of you who prefer a class diagram (courtesy of the EclipseGraphviz integration):
Closure as a UML metamodel extension

Note a model contributing language extensions must be applied the Standard::Metamodel stereotype, and each metaclass must be assigned the stereotype Standard::Metaclass.

Of course, there is no point in being able to metamodel closures, if there is no way to refer to them. We need a kind of type that we can use to declare variables and parameters that can hold references to closures. That also means we need to be able to invoke/dereference a closure reference, and there is no support for referring to metamodel elements in the UML action language. That means we need more language extensions. But I will leave that to another post. My goal here was to show how simple it is to create a simple UML metamodel extension with the TextUML Toolkit.

What about you, have you ever needed to extend UML using a metamodel extension? What for?

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

Executable models with TextUML Toolkit 1.2 M1

The first milestone build of the next TextUML Toolkit release is now available from the milestone update site. This preview build is the first to include support for modeling behavior using action semantics. I hinted at this capability here before, and I plan to cover action semantics in the TextUML notation in following posts. But most people will get the gist of the notation from the examples below:

Here is an example of a UML model of an Account class represented in TextUML:

model bank;

import base;

class Account
    attribute accountNumber : String;
    attribute balance : Integer;

    operation withdraw(amount : Integer);
    begin
        self.balance := self.balance - amount;
    end;    

    operation deposit(amount : Integer);
    begin
        self.balance := self.balance + amount;
    end;

    static operation newAccount(number : String,owner : Client): Account;
    begin
        var newAccount : Account;
        newAccount := new Account;
        newAccount.accountNumber := number;
        newAccount.balance := 0;
        link ClientAccount(owner := owner, accounts := newAccount);
        return newAccount;
    end;
end;

class Client specializes Object
    attribute name : String;
    static operation newClient(name : String): Client;
    begin
        var newClient : Client;
        newClient := new Client;
        newClient.name := name;
        return newClient;
    end;
end;

association ClientAccount
    navigable role owner : Client[1];
    navigable role accounts : Account[0, *];
end;

end.

And here an example of a test driver ‘program’ (note the use of a closure for looping through a collection of objects):

package test;

apply base_profile;

import bank;
import base;

class TestDriver
  [entryPoint]
  static operation run();
  begin
      var john : Client, mary : Client,
             account1 : Account, account2 : Account, account3 : Account;
      john := Client#newClient("John Doe");
      mary := Client#newClient("Mary Doe");

      Console#writeln("Created: ".concat(john.name));

      account1 := Account#newAccount("1234-1", john);
      account2 := Account#newAccount("1238-2", mary);
      account3 := Account#newAccount("2231-7", john);

      Console#writeln("account owner: ".concat(account1->ClientAccount->owner.name));
      Console#writeln("account number: ".concat(account1.accountNumber));
      Console#writeln("initial balance is: ".concat(account1.balance.toString()));

      account1.deposit(2000);
      Console#writeln("after deposit, balance is: ".concat(account1.balance.toString()));

      account1.withdraw(500);
      Console#writeln("after withdrawal, balance is: ".concat(account1.balance.toString()));

       /* Now show information for all accounts. */
  	Account extent.forEach(
          (a : Account) {
              Console#writeln("*******");
              Console#writeln("Owner: ".concat(a->ClientAccount->owner.name));
              Console#writeln("Number: ".concat(a.accountNumber));
              Console#writeln("Balance: ".concat(a.balance.toString()));
          }
      );
  end;
end;

end.

And here is the output of the test driver program, produced by running it on the Libra UML runtime (not part of the TextUML Toolkit):

Created: John Doe
account owner: John Doe
account number: 1234-1
initial balance is: 0
after deposit, balance is: 2000
after withdrawal, balance is: 1500
*******
Owner: Mary Doe
Number: 1238-2
Balance: 0
*******
Owner: John Doe
Number: 1234-1
Balance: 1500
*******
Owner: John Doe
Number: 2231-7
Balance: 0

Alternatively, one could have generated 100% of the code for a target platform of choice, given that all the information necessary for that is in the model. Note that code generation is not part of the TextUML Toolkit.

Do you want to give it a try? Install M1 from the milestone update site. You can also fetch the example projects from here.

It is certainly a rough stone. I am counting on the community feedback to figure out what areas need to be smoothed first. Please provide your feedback or ask questions on the TextUML Toolkit forums.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

What can UML do for you?

Do you know what UML can do for you? I mean, did you know that UML models can actually do things?

One of the least known features of UML is that you can model detailed imperative behavior. The UML “instruction set” can do things like:

  • create and destroy objects
  • create and destroy links (associations) between objects
  • read and write attributes and local variables
  • invoke operations and functions
  • throw and catch exceptions
  • conditional statements
  • loops

That is quite amazing, isn’t? And all that while still preserving a high level of abstraction. Such capability is generally referred to as ‘action semantics’. Action semantics provides the basic framework for executability in UML and has been there for quite a while now. It was originally added to the spec, first as patch, in UML 1.5 (2003), and then more seamlessly integrated into UML 2.0 and following spec releases.

Action Semantics and TextUML

An even more well-kept secret is that the TextUML notation supports UML action semantics and thus the creation of fully executable UML models. This support is not yet shipped as part of the TextUML Toolkit, but will be in the next release. Meanwhile, if you want to give it a try or take a closer look, you will have to grab the source from the SVN repository.

I plan to go into more details in the near future, but just to wet your appetite, here is one example of an executable UML model described in the TextUML notation:

package hello;

apply base_profile;
import base;

class HelloWorld
    [entryPoint]
    static operation hello();
    begin
        Console#writeln("Hello, World");
    end;
end;

end.

Cool, isn’t? If you had a UML runtime, this model could be executed even before you made a decision about what platform to target. Also, if your code generator were action semantics aware, you could trigger code generation for the target platform(s) of choice, with the key difference that you could achieve (or get very close) to full code generation, as the model now also describes behavior. No more of that monkey business of having to edit the generated code and manually fill in all those /* IMPLEMENT ME! */ methods.

Do you think this has value? Would you want to work with a tool that supported that? I am really keen on knowing your opinion.

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter

OMG issues RFP: concrete syntax for UML action semantics

This is actually old news for many people, but recently I learned (by pure chance) that the OMG issued a RFP for a “Concrete Syntax for a UML Action Language”. Letters of intent are due on December 8th. Submissions, one year after. OMG members only need apply (Aww…). I wonder if anyone in the Eclipse Modeling project is involved in submitting a proposal. Anyone?

Soapbox: since version 1.5, UML has had support for algorithmic behavior specification, commonly called action semantics. It is still hardly used, and most people that consider they know a lot of UML have never noticed it. Some people believe that the lack of an official concrete syntax for action semantics is a barrier to adoption. You see, the OMG defined the semantics and an abstract syntax, but left concrete syntax as an exercise for tool vendors.

As I wrote here before, I don’t really see the value in the OMG godfathering one concrete syntax over all others. Of course, we are talking here about a syntax for human beings, not for tools. Tools certainly don’t need a human-readable concrete syntax, a standard binary or XML format will do. The problem is: we all have our own preferences for what makes a good syntax, and there is no single syntax that will make everybody happy, so we are bound to have multiple concrete syntaxes anyway. We all like interoperability between tools, but when it comes to sugar, we like choice.

But maybe I am wrong. Maybe an OMG-blessed C-like concrete syntax for UML is all that is missing for Executable UML to become mainstream in the software development community. Go figure, we are an amusing bunch. Personally, I don’t care that much. I have been a Java developer for around 12 years now, so I can certainly stand another C-like syntax. We are not talking about a language anyway, it is just a syntax for an existing language, and syntax, a bit like UI, is inherently disposable, if you take it away, the real stuff is still there.

One clear positive outcome of the RFP is that submitters must provide, along with the proposal for a concrete syntax, any changes to fUML* that would be required to support such action language. That will probably help closing some gaps in the UML specification that make it hard to execute if you are stuck with the standard.

There are many other interesting bits in the proposal, but I will leave a more detailed analysis to a future post.

* the Executable UML Foundation Submission says:“Foundational UML Subset (fUML) is that subset of UML required to write ‘programs’ in UML”

Email this to someoneShare on FacebookShare on LinkedInShare on Google+Tweet about this on Twitter