Upcoming: Kirra, a language-independent API for business applications

What do NakedObjects, Apache Isis, Restful Objects, OpenXava, JMatter, Tynamo, Roma, and Cloudfier have in common?

These frameworks and platforms allow developers to focus on expressing all they know about a business domain in the form of a rich domain model. And they all support or enable automatically generating interfaces based on the application’s domain model that make the entire functionality of the application accessible to end users, without requiring any effort on designing a user interface. They can also often auto-generate a functional (usually REST) API for non-human actors.

However, each of those frameworks/platforms implement automatic UI or API generation independently, against their own proprietary metamodels – for each UI and API technology supported. So while Cloudfier supports a Qooxdoo client, Isis supports a Wicket viewer and a JQuery viewer, OpenXava seems to have a JQuery/DWR UI and so on.

This is the motivation for Kirra: Kirra aims to decouple the interface renderers from the technologies used for creating domain-driven applications, promoting the proliferation of high-quality generic UI and API renderers that can be used across domain-driven development frameworks, or even if your application is not built with a domain-driven framework.

But what is Kirra?

Kirra is a minimalistic language-independent API specification to expose functionality of a business application in a business and technology agnostic way.

Essentially, Kirra provides a simple model for exposing metadata and data for business applications, no matter how they were implemented, enabling generic clients that have full access to the functionality exposed by those applications.

Watch this space for more details and the first release, planned for later this month.

EmailFacebookLinkedInGoogle+Twitter

On automatically generating fully functional mobile user interfaces

An upcoming feature in Cloudfier is the automatic generation of fully functional user interfaces that work well on both desktop:
 
Screenshot from 2014-01-29 13:57:47
 
and mobile browsers:

Screenshot_2014-01-29-13-45-01 Screenshot_2014-01-29-13-44-53
 
This is just a first stab, but is already available to any Cloudfier apps (like this one, try logging in as user: test@abstratt.com, senha: Test1234). Right now the mobile UI is read-only, and does not yet expose actions and relationships as the desktop-oriented web UI does. Watch this space for new developments on that.

The case against generated UIs

Cloudfier has always had support for automatic UI generation for desktop browsers (RIA). However, the generated UI had always been intended as a temporary artifact, to be used only when gathering initial feedback from users and while a handcrafted UI (that accesses the back-end functionality via the automatically generated REST API) is being developed (or in the long term, as a power-user UI). The reason is that automatically generated user-interfaces tend to suck, because they don’t recognize that not all entities/actions/properties have the same importance, and that their importance varies between user roles.

Don’t get me wrong, we strongly believe in the model-driven approach to build fully functional applications from a high-level description of the solution (executable domain models). While we think that is the most sane way of building an application’s database, business and API layers (and that those make up a major portion of the application functionality and development costs), we recognize user interfaces must follow constraints that are not properly represented in a domain model of an application: not all use cases have the same weight, and there is often benefit in adopting metaphors that closely mimic the real world (for example, an audio player application should mimic standard controls from physical audio players).

If model-driven development is to be used for generating user interfaces, the most appropriate approach for generating the implementation of such interfaces (and the interfaces only) would be to craft UI-oriented models using a UI modeling language, such as IFML (although I never tried it). But even if you don’t use a UI-oriented modeling tool, and you build the UI (and the UI only) using traditional construction tools (these days that would be Javascript and HTML/CSS) that connect to a back-end that is fully generated from executable domain models (like Cloudfier supports), you are still much but much better off than building and maintaining the whole thing the traditional way.

Enter mobile UIs

That being said, UIs on mobile devices are usually much simpler than corresponding desktop-based UIs because of the interaction, navigation and dimension constraints imposed by mobile devices, resulting in a UI that shows one application ‘screen’ at a time, with hierarchical navigation. So here is a hypothesis:

Hypothesis: Mobile UIs for line-of-business applications are inherently so much simpler than the corresponding desktop-based UIs, that it is conceivable that generated UIs for mobile devices may provide usability that is similar to manually crafted UIs for said devices.

What do you think? Do you agree that is a quest worth pursuing (and with some likelihood of being proven right)? Or is the answer somehow obvious to you already? Regardless, if you are interested or experienced in user experience and/or model-driven development, please chime in.

Meanwhile, we are setting off to test that hypothesis by building full support for automatically generated mobile UIs for Cloudfier applications. Future posts here will show the progress made as new features (such as actions, relationships and editing) are implemented.

EmailFacebookLinkedInGoogle+Twitter

How Cloudfier uses Orion – shell features

Following last week’s post on editor features, today I am going to cover how Cloudfier plugs into Orion’s Shell page to contribute shell commands.

The cloudfier command prefix

All Cloudfier commands must be prefixed with ‘cloudfier’.

By just typing ‘cloudfier ‘ and hitting enter, you are given a list of all Cloudfier-specific commands.

cloudfier-commands

This is how the command prefix is contributed:


provider.registerServiceProvider("orion.shell.command", {}, {   
    name: "cloudfier",
    description: "Cloudfier commands"
});

which is a command contribution without a behavior. All the subcommands you see being offered actually include the prefix in their contributions.

Typical Cloudfier command

The typical Cloudfier command takes a workspace location (a file-type parameter), performs a remote operation and returns a message to the user explaining the outcome of the command (return type is String), and looks somewhat like this:

provider.registerServiceProvider("orion.shell.command", { callback: shellAppInfo }, {   
	name: "cloudfier info",
	description: "Shows information about a Cloudfier application and database",
	parameters: [{
	    name: "application",
	    type: "file",
	    description: "Application to get obtain information for"
	}],
	returnType: "string"
});

The behavior of the command is specified by the callback function. In this specific case, the callback performs a couple of HTTP requests against the server, so it it returns a dojo.Deferred which implements the Promise pattern contract used by Orion. Once the last server request is completed, it returns a string to be presented to the user with the outcome of the operation.

info-command

Note that the output of a command needs to use Markdown-style notation to produce links, HTML output is not suppported. Also, newlines are honored.

Commands that contribute content to the workspace

Commands that contribute content to the workspace use a “file” (single file) or “[file]” (multiple files) return type. Cloudfier has a few commands in this style:

An init-project command, which marks the current directory as a project directory:

provider.registerServiceProvider("orion.shell.command", { callback: shellCreateProject }, {   
    name: "cloudfier init-project",
    description: "Initializes the current directory as a Cloudfier project"
    returnType: "file"
});

An add-entity command which adds a new entity definition to the current directory:

provider.registerServiceProvider("orion.shell.command", { callback: shellCreateEntity }, {   
    name: "cloudfier add-entity",
    description: "Adds a new entity with the given name to the current directory",
    parameters: [{
        name: "namespace",
        type: {name: "string"},
        description: "Name of the namespace (package) for the entity (class)"
    },
    {
        name: "entity",
        type: {name: "string"},
        description: "Name of the entity (class) to create"
    }],
    returnType: "file"
});

And finally a db-snapshot command which grabs a snapshot of the current application database state and feeds it into a data.json file in the current application directory.

provider.registerServiceProvider("orion.shell.command", { callback: shellDBSnapshot }, {   
    name: "cloudfier db-snapshot",
    description: "Fetches a snapshot of the current application's database and stores it in as a data.json file in the current directory",
    returnType: "file"
});

That snapshot can be further edited and later pushed into the application database.

Note that for all file-generating commands, if files already exist (mdd.properties, <entity-name>.tuml, and data.json, respectively), they will be silently overwritten (bug 421349).

Readers beware

This ends our tour over how Cloudfier uses Orion extension points. Keep in mind this is not documentation.
See this wiki page for the most up-to-date documentation on the orion.shell.command extension point and this blog post by the Orion team for some interesting shell command examples.

EmailFacebookLinkedInGoogle+Twitter

How Cloudfier uses Orion – editor features

Cloudfier now runs on Orion 4.0 RC2. It took some learning and patience and a few false starts (tried the same in the Orion 2.0 and 3.0 cycles), but I finally managed to port the Cloudfier Orion plug-in away from version 1.0 RC2 (shipped one year ago) to 4.0 RC2. Hopefully when 4.0 final is released (any time now?), it should be a no-brainer to integrate with it. I will only then look into hack/branding it a bit so it doesn’t look identical to a vanilla Orion instance.

But how does Cloudfier extend the Orion base feature set? This post will cover the editor-based features.

Content type

Cloudfier editor-based features are applicable for TextUML files only. This content type definition provides the reference for all features to be configured against.

    provider.registerServiceProvider("orion.core.contenttype", {}, {
        contentTypes: [{  id: "text/uml",
                 name: "TextUML",
                 extension: ["tuml"],
                 extends: "text/plain"
        }]
    });

Outliner

outliner
The outliner relies on the server to parse and generate an outline tree for the contents in the editor.

    var computeOutline = function(editorContext, options) {
        var result = editorContext.getText().then(function(text) {
            return dojo.xhrPost({
	             postData: text,
	             handleAs: 'json',
	             url: "/services/analyzer/?defaultExtension=tuml",
	             load: function(result) {
	                 return result;
	             }
	        });
        });
        return result;
    };


    provider.registerServiceProvider("orion.edit.outliner", { computeOutline: computeOutline }, { contentType: ["text/uml"], id: "com.abstratt.textuml.outliner", name: "TextUML outliner" });

Note the outliner API changed in 4.0 and the editor buffer contents is now available via a deferred instead of directly. Also, note that in order to use this API your plugin needs to load Deferred.js (see this orion-dev thread) as it implicitly turns your service into a long-running operation.

Source validation

validator

Also a server-side functionality, which already returns a JSON tree in the format expected by the orion.edit.validator extension point.

    var checkSyntax = function(title, contents) {
        return dojo.xhrGet({
             handleAs: 'json',
             url: "/services/builder" + title,
             load: function(result) {
                 return result
             }
        });
    };

    provider.registerServiceProvider("orion.edit.validator", { checkSyntax: checkSyntax }, { contentType: ["text/uml", "application/vnd-json-data"] });

Note the validation service uses a GET method, and only uses the file path, not the contents. The reason is that the server reaches into the project contents stored in the server instead the client contents (in order to perform multi-file validation).

Syntax highlighting

highlighter

    /* Registers a highlighter service. */    
    provider.registerServiceProvider("orion.edit.highlighter",
      {
        // "grammar" provider is purely declarative. No service methods.
      }, {
        type : "grammar",
        contentType: ["text/uml"],
        grammar: {
          patterns: [
			  {  
			     end: '"',
			     begin: '"',
			     name: 'string.quoted.double.textuml',
			  },
			  {  begin: "\\(\\*", 
			     end: "\\*\\)",
			     name: "comment.model.textuml"
			  },
			  {  
			     begin: "/\\*", 
			     end: "\\*/",
			     name: "comment.ignored.textuml"
			  },
			  {  
			     name: 'keyword.control.untitled',
			     match: '\\b(abstract|access|aggregation|alias|and|any|apply|association|as|attribute|begin|broadcast|by|class|component|composition|constant|datatype|dependency|derived|destroy|do|else|elseif|end|entry|enumeration|exit|extends|external|function|id|if|implements|interface|in|initial|inout|invariant|is|link|model|navigable|new|nonunique|not|on|operation|or|ordered|out|package|port|postcondition|precondition|private|primitive|profile|property|protected|provided|public|raise|raises|readonly|reception|reference|required|return|role|self|send|signal|specializes|state|statemachine|static|stereotype|subsets|terminate|to|transition|type|unique|unlink|unordered|var|when)\\b'
			  },
              {
                "match": "([a-zA-Z_][a-zA-Z0-9_]*)",
                "name": "variable.other.textuml"
              },                  
              {
	            "match": "<|>|<=|>=|=|==|\\*|/|-|\\+",
	            "name": "keyword.other.textuml"
              },
              {
	            "match": ";",
	            "name": "punctuation.textuml"
              }
            ]
        }
    });

Source formatting

The code formatter in Cloudfier is server-side, so the client-side code is quite simple:

    var autoFormat = function(selectedText, text, selection, resource) {
        return dojo.xhrPost({
             postData: text,
             handleAs: 'text',
             url: "/services/formatter/?fileName=" + resource,
             load: function(result) {
                 return { text: result, selection: null };
             }
        });
    }; 

    provider.registerServiceProvider("orion.edit.command", {
        run : autoFormat
    }, {
        name : "Format (^M)",
        key : [ "m", true ],
        contentType: ["text/uml"]
    });

Content assist

contentAssist
Content assist support is quite limited, basically a few shortcuts for creating new source code elements, useful for users not familiar with TextUML, the notation used in Cloudfier.

    var computeProposals = function(prefix, buffer, selection) {
        return [
            {
                proposal: "package package_name;\n\n/* add classes here */\n\nend.",
                description: 'New package' 
            },
            {
                proposal: "class class_name\n/* add attributes and operations here */\nend;",
                description: 'New class' 
            },
            { 
                proposal: "attribute attribute_name : String;",
                description: 'New attribute' 
            },
            { 
                proposal: "operation operation_name(param1 : String, param2 : Integer) : Boolean;\nbegin\n    /* IMPLEMENT ME */\n    return false;\nend;",
                description: 'New operation' 
            },
            { 
                proposal: "\tattribute status2 : SM1;\n\toperation action1();\n\toperation action2();\n\toperation action3();\n\tstatemachine SM1\n\t\tinitial state State0\n\t\t\ttransition on call(action1) to State1;\n\t\tend;\n\t\tstate State1\n\t\t\ttransition on call(action1) to State1\n\t\t\ttransition on call(action2) to State2;\n\t\tend;\n\t\tstate State2\n\t\t\ttransition  on call(action1) to State1\n\t\t\ttransition on call(action3) to State3;\n\t\tend;\n\t\tterminate state State3;\n\tend;\n\t\tend;\n",
                description: 'New state machine' 
            }
        ];
    };

    provider.registerServiceProvider("orion.edit.contentAssist",
	    {
	        computeProposals: computeProposals
	    },
	    {
	        name: "TextUML content assist",
	        contentType: ["text/uml"]
	    }
	);

Coming next

The next post will cover the Shell-based features in Cloudfier.

EmailFacebookLinkedInGoogle+Twitter

Presenting Cloudfier at VIJUG

I will be presenting Cloudfier at the next VIJUG meeting on May 30th.

I believe you will find the subject at least a bit intriguing (oh, and there will be some beer). It is open to anyone interested. If you intend to come, it will help if you can RSVP. There is an EventBrite page, but a comment here or on the VIJUG blog or Google+ or Facebook pages should do as well.

The goal is to give an overview of Cloudfier and gather feedback from fellow Victoria developers, in preparation for the first release later this year. VIJUG meetings have a varied audience so that should mean some interesting feedback. Also, if you are a fan of my speaking skills (I kid!), it is possibly my last talk in Victoria, given we’ll be packing and moving back to Brazil in just a few months. Honestly though, it would be great to see again some of the many great tech folks I met in Victoria in the last 6 years before heading to the other side of the Equator.

UPDATE: the presentation was a great opportunity for gathering feedback on Cloudfier. Got some very good questions and suggestions from Paul, John, Kelly, Shea and others. Thanks to all who managed to attend. These were the slides presented:

EmailFacebookLinkedInGoogle+Twitter

cloudfier.com and abstratt.com servers moved to new hosting

The cloudfier.com and abstratt.com servers (including this blog) moved to new hosting. Please let me know if you find broken links or any issues. If you do, please try pinging the server you found it at. You should see something like this:

ping abstratt.com
PING abstratt.com (54.244.115.27) 56(84) bytes of data.
64 bytes from ec2-54-244-115-27.us-west-2.compute.amazonaws.com (54.244.115.27): icmp_req=1 ttl=51 time=0.860 ms
64 bytes from ec2-54-244-115-27.us-west-2.compute.amazonaws.com (54.244.115.27): icmp_req=2 ttl=51 time=0.992 ms
64 bytes from ec2-54-244-115-27.us-west-2.compute.amazonaws.com (54.244.115.27): icmp_req=3 ttl=51 time=0.848 ms
^C
--- abstratt.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 0.848/0.900/0.992/0.065 ms

If not, it is probably a temporary DNS propagation issue.

EmailFacebookLinkedInGoogle+Twitter

Authenticating users in Cloudfier applications

Up until very recently, Cloudfier applications had no way to authenticate users – there was a login dialog but all it was meant for was to allow users to assume an arbitrary identity by informing the name of an existing user.

That is no longer the case. The latest release build (#29) addresses that by implementing a full-blown authentication mechanism. Now, when you try to access a Cloudfier app, like this one, you will be greeted by this login dialog:

login

which allows you to sign up, request a password reset and sign in either with proper credentials or as a guest.

For more details about how authentication works in Cloudfier applications, check the new authentication documentation.

BTW, kudos to Stormpath for providing such a great API for managing and authenticating user credentials. Highly recommended.

What’s next?

Glad you asked. Next is authentication’s bigger friend: authorization. Right now any user can do anything to any data, and of course that is not reasonable even for the simplest applications. Stay tuned for more on that.

EmailFacebookLinkedInGoogle+Twitter

New Cloudfier release supports many-to-many associations

One clear gap Cloudfier used to have was lack of support for many-to-many associations. That has now been implemented all the way from back-end to the UI.

For instance, in the ShipIt! sample issue tracking application, a user can watch multiple issues, and an issue can be watched by multiple users:

class Issue
end;

class User
end;

association WatchedIssues
    navigable role watchers : User[*];
    navigable role issuesWatched : Issue[*];
end;

UI

…which in the UI means that is now a way to link issues as watched issues for a user:

(and vice-versa from Issue, as the relationship is navigable both ways). Once the user triggers that action, they can pick multiple target objects (in this case, issues) to pair the source object (in this case, a User) up with, by clicking the “connector” button on the target entity instance’s toolbar (the second from left to right):

which once triggered shows a notice confirming the objects have now been linked together.

I will admit this UI may require some using to. It is just a first cut, and I am interested in suggestions from those of you less UX-challenged than me.

REST API

Accordingly, the application’s REST API allows querying related objects using a URI in the form:

…/services/api/<application>/instances/<entity>/<id>/relationships/<relationship-name>/

for instance:

…/services/api/demo-cloudfier-examples-shipit/instances/shipit.Issue/10/relationships/watchers/

produces a list of all users watching the base issue:

[
  {
    uri: ".../instances/shipit.User/2",
    shorthand: "rperez",
    type: ".../entities/shipit.User",
    typeName: "User",
    values: {
      ...
    },
    links: {
      ...
    },
    actions: {
      ...
    },
    relatedUri: ".../instances/shipit.Issue/10/relationships/watchers/2"
  },
  {
    uri: ".../instances/shipit.User/8",
    shorthand: "gtorres",
    type: ".../entities/shipit.User",
    typeName: "User",
    values: {
      ...
    },
    links: {
      ...
    },
    actions: {
      ...
    },
    relatedUri: ".../instances/shipit.Issue/10/relationships/watchers/8"
  }
]

and to establish links, you can POST a similar representation to the same URI, but you only really need to include the ‘uri’ attribute, everything else is ignored.

New tour video

There is also now a new tour video, this time with audio and much better image quality, if you gave up on watching the original one, please give this one a try!

EmailFacebookLinkedInGoogle+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.

EmailFacebookLinkedInGoogle+Twitter

Yet another Orion-based site: cloudfier.com

Okay, we are live.

I just put the last finishing touches on the developer site at cloudfier.com.

The developer site, develop.cloudfier.com, is powered by Orion. Cloudfier’s instance of Orion has several features to support modeling with TextUML, such as:

  • Syntax highlighting
  • Outline
  • Validation
  • Auto-formatting
  • Templates

and I have a picture to prove it:

but wouldn’t you rather see for yourself? If you are shy because you don’t know how to model in TextUML, just make sure you create a file with a “.tuml” extension and use the content assist templates to get a model going. Or if you are feeling lazy, just clone this Git repository: https://bitbucket.org/abstratt/cloudfier-examples.git

But what and who is Cloudfier for you may ask. I won’t tell you here though. Please go to cloudfier.com, give it a quick read. If you don’t get it, please let me know in the comments – a main goal now is to ensure the main page can get the message across.

EmailFacebookLinkedInGoogle+Twitter