Mono, .NET and Java

Microsoft recently sponsored a research to compare the development costs of using .NET and J2EE to build enterprise applications. The story is here.

The results are not surprising to me. The study claims that .NET is on aveage 28% cheaper than developing with J2EE-based frameworks. Both J2EE and Swing have been over-engineered and over-designed. It is probably a very solid design, but has abused the model-view-controller architecture. If not kept under adult supervision architects designing MVC platforms make the development of average applications cumbersome.

The results are exactly along the lines of the input we received in March 2003 when we did the Second Mono Survey. Our Survey focused on ASP.NET as a technology for developing web-based applications, and the people we had a chance to interview gave us that same message: ASP.NET saves them on average 20% of their development time.

The research study we did shed some light into the debate. The application serving market can be divided in three groups:

Market Development
Price Range (dollars)
Technologies State
Entry level 0-200,000 Php, Python, Zope, Perl, home-grown, cgi-bin, asp, aps.net, jsp, j2ee Plenty of offerings, no dominant player.
Mid level 200,000 to 5,000,000 J2EE, ASP.NET J2EE had an early start, but ASP.NET is taking over.
These are multi-developer projects of six to twelve months. This market is very sensitive to development times, and a 20% to 28% increase in productivity makes ASP.NET solutions more competitive price-wise and more profitable. The Windows-only nature of ASP.NET its the only thing stopping its mass adoption.
High level 5,000,000+ J2EE and ASP.NET J2EE is considered the standard to use. These projects require hardware mobility and are large organizational projects. The availability of many J2EE providers is considered a big plus.
The lack of third-party providers for ASP.NET slows down its adoption in this market.

Mono is by no means ready to be an ASP.NET provider on the high-level application server market today, but the mid-market is the sweet spot for Mono and will help drive the adoption of ASP.NET. This is very compelling, because developers can use the state-of-the-art Visual Studio to create their applications (cutting costs there), and deploy on Linux (cutting costs on the server side).

Plugins in Mono

Nat asked me to write a small document for people who are interesting in creating applications with plugins. The following is the smallest setup I could think of (this is the same model I used on the Dashboard).

Plugins in Mono: (Article Permalink)

The Common Language Infrastructure has various mechanisms that developers can use to extend their applications with plugins. This document describes the facilities available and some possible implementation strategies.

Plugins are dynamically loaded modules that extend the functionality of an application. Plugins can be used to enable developers to extend an application after it has shipped, and also as a modularization technique. Applications like the GIMP use a plugin architecture to implement transformation and file formats.

CLI Foundations

When a program or a library are compiled into assemblies (the binary file format used by the CLI)the information about the available classes, methods, variables, names, parameters are stored into the resulting .exe or .dll. This information can be later retrieved through the Reflection API.

Technically, the only difference between a .exe and a .dll in the CLI world is that executable files contain an entry point, and libraries do not.

For example, the following program will list all of the types available in an assembly. Both a .exe or a .dll will work:

using System;
using System.Reflection;

class ListTypes {
	static void Main (string [] args)
	{
		foreach (string file in args){
			Assembly a = Assembly.LoadFrom (file);

			Type [] types = a.GetTypes ();

			Console.WriteLine ("Assembly: " + file);
			foreach (Type t in types)
				Console.WriteLine ("   Type: " + t.Name);
		}
	}
}

Compile and run:

$ mcs query.cs
Compilation succeeded
$ mono query.exe demo.exe
Assembly: query.exe
   Type: Class1
$ _

Once an assembly is loaded, it is also possible to extract the available methods or dynamically invoke methods in the recently loaded assembly. For example, the following program will invoke the method "Boot" on any types passed on the command line.

using System;
using System.Reflection;

class ListTypes {
	static void Main (string [] args)
	{
		foreach (string file in args){
			Assembly a = Assembly.LoadFrom (file);

			Type [] types = a.GetTypes ();

			Console.WriteLine ("Assembly: " + file);
			foreach (Type t in types){
				MethodInfo boot = t.GetMethod ("Boot");
				if (boot == null)
					continue;

				if (!boot.IsStatic)
					continue;
					
				boot.Invoke (null, new Type [0]);
			}
		}
	}
}

We are using the Type's GetMethod to fetch the public method named "Boot". If we find such a method, we invoke it. In this particular example, we want to invoke static methods, so we test for this and we also pass `null' as the first argument to Invoke.

The following sample program can be used to test our new loader:

using System;
	
public class Demo {
	public static void Boot ()
	{
		Console.WriteLine ("Demo.Boot invoked");
	}
}

We compile and run:

$ mcs loader.cs
Compilation succeeded
$ mcs -target:library demo.cs
Compilation succeeded
$ mono loader.exe demo.dll
Assembly: demo.dll
Demo.Boot invoked
$ _

Access to the Main Application

It is now possible to load code dynamically, but a plugin-system often will need to access the internal data structures and methods to carry out a more interesting job. Our plugins can invoke easily any methods that is available to thme at compilation time.

The following sample program shows a tiny spreadsheet application which provides support for invoking plugins.

using System;
using System.Reflection;

public class Spreadsheet {
	string [,] cells = new string [10, 10];

	public string this [int col, int row] {
		get {
			return cells [col, row];
		}

		set {
			cells [col,row] = value;
		}
	}
}

public class Driver {
	static public Spreadsheet sheet = new Spreadsheet ();
	
	static void Main ()
	{
		string cmd, r;

		do {
			Console.Write ("cmd> ");
			cmd = Console.ReadLine ();

			string [] args = cmd.Split (new char [] {' '});
			
			switch (args [0]){
			case "set": 
				sheet [Byte.Parse (args [1]),
				       Byte.Parse (args [2])] = args [3];
				break;

			case "get":
			        r = sheet [Byte.Parse (args [1]),
				           Byte.Parse (args [2])];
				Console.WriteLine ("value: {0}", r);
				break;

			case "plugin":
				InvokePlugin (args [1]);
				break;
			}
		} while (cmd != "quit");
	}

	static void InvokePlugin (string plugin)
	{
		Assembly a = Assembly.LoadFrom (plugin);

		foreach (Type t in a.GetTypes ()){
			MethodInfo method = t.GetMethod ("SheetPlugin");

			if (method != null && method.IsStatic){
				method.Invoke (null, new Type [0]);
				break;
			}
		}
	}
}

Compile the above like this:

$ mcs sheet.cs
Compilation succeeded
$ _

Here is the `export.cs' program that we will compile as a plugin:

using System;
	
public class DumpSheet {
	public static void SheetPlugin ()
	{
		for (int r = 0; r < 10; r++){
			for (int c = 0; c < 10; c++)
				Console.Write ("{0}, ", Driver.sheet [r, c]);
			Console.WriteLine ();
		}
	}
}

Notice that this plugin needs to access the sheet object in the Driver class from the main program. We will compile this as a library which references the main application. This effectively treats the main application as a library consumed by the plugin.

$ mcs export.cs -r:sheet.exe -target:library
Compilation succeeded
$ _

A sample session looks like this:

$ mono sheet.exe
cmd> set 0 0 hello
cmd> set 0 1 world
cmd> set 8 8 Eight
cmd> plugin export.dll
hello, world, , , , , , , , , 
, , , , , , , , , , 
, , , , , , , , , , 
, , , , , , , , , , 
 , , , , , , , , , 
, , , , , , , , , , 
, , , , , , , , , , 
, , , , , , , , , , 
, , , , , , , , Eight, , 
, , , , , , , , , , 
cmd> _

Details

There are plenty of other scenarios and ways of structuring your program to have a plugin system. Currently part of the contract used in this document was to use the public classes, methods and fields exposed by the host application. A common pattern used in plugin systems is to provide a class method to register objects and handlers with the host application.

Gnome 2.4 is out

The anticipated release of Gnome 2.4 is out now. It includes a list of Why use Gnome?

Ars Technica has a pretty good review.

Posted on 12 Sep 2003 by Miguel de Icaza
This is a personal web page. Things said here do not represent the position of my employer.