
Monikers in GNOME, 
Miguel de Icaza (miguel@helixcode.com)

* Introduction

	We recently reimplemented and fully revamped the the Moniker
	support in Bonobo.  This work has opened a wide range of
	possibilities: from unifying the object naming space, to
	provide better integration in the system.
	
	Note: on this document I have ommited the IDL: prefix in the
	CORBA interface names as well as the version number of the
	interface in an attempt to simplify the topic being
	discussed. Exception environments are also omitted for
	brevity.

* Monikers - a user perspective

	Monikers are used to name objects, they effectively implement
	an object naming space.   You can obtain monikers either because
	you created the moniker manually, or from a stringified
	representation of a moniker.

	A stringified moniker can look like this:

		file:quake-scores.gnumeric
			This would be a moniker that represents the
			file quake-scores.gnumeric

		oafid:GNOME:Gnumeric:WorkbookFactory:1.0:7cf6fb4d-c5a1-4ace-aa6a-4ece790138c9
			This moniker represents the Gnumeric Workbook
			factory object.

		oafid:GNOME:Gnumeric:WorkbookFactory:1.0:7cf6fb4d-c5a1-4ace-aa6a-4ece790138c9:new:
			This moniker represents a Gnumeric Workbook instance. 
			Notice that we are using the exact same OAFID
			as the example before, but there is a "new:"
			suffix at the end.

		file:/tmp/a.gz
			This represents the file in /tmp/a.gz

		file:/tmp/a.gz#gunzip
			This represents the decompressed stream of
			data from a.gz

		file:/tmp/a.gz#gunzip:streamcache
			This provides a cache on top of the
			decompressed stream of data for a.gz (the
			streamcache moniker is an in-proc component). 

		http://www.gnome.org
			This one represents the GNOME web site.

		evolution:Mail/Inbox
			This represents the Evolution Mail/Inbox
			folder

		file:quake-scores.gnumeric!January
			This represents the January Sheet in the
			quake-scores.gnumeric workbook.
		
		file:quake-scores.gnumeric!January!Winner
			This represents the cell whose name is
			"Winner" in the January sheet in the
			quake-scores.gnumeric workbook.
	
		file:quake-scores.gnumeric!January!Winner!Style!Font
			This represents the Font interface of the
			Style attached to the Winner cell.

		file:quake-scores.gnumeric!Jannuary!Winner!Style!BackgroundColor
			This represents the background color for the cell.

		http://www.gnome.org/index.html!title
			This represents the title element in the HTML
			web page at www.gnome.org

		file:toyota.xml!cars/car/model/
			The "cars/car/model" is an XPath expression
			that for locating a specific node in the
			toyota.xml file.

		config:*/Session/Calendar
			This represents a PropertyBag for the GNOME
			Calendar using the Local configuration system
			and using the settings stored in the Session
			domain. 

		oafid:Helix:Evolution:Wombat:1.0:7cf6fb4d-c5a1-4ace-1020-4ece790138c0:
			This represents the Evolution model server
			that stores all the per-user information.

		queue:oafid:Helix:Evolution:Wombat:1.0:7cf6fb4d-c5a1-4ace-1020-4ece790138c0:
			This represents an interface that queues CORBA
			requests to the Evoution Wombat: Any calls
			issued will be queued: if the Wombat is busy
			or not accepting connection, all the CORBA
			method invocations will be queued without
			stopping the execution of the client code.

		http://www.gnome.org/index.html.gz#gunzip#html:title
			This will reutrn the title element of the
			compressed HTML file at 
			http://www.gnome.org/index.html.gz

 		ftp://ftp.gnome.org/gnome-core-1.0.tar.gz#utar/gnome-core-1.0/ChangeLog
			A reference to the ChangeLog file contained in
			the compressed gnome-core-1.0.tar.gz tar file
			at ftp://ftp.gnome.org 

		desktop:Backgound
			The background object for the user's desktop.

		trashcan:
			The system trashcan.

		file:logo.png
			This represents the logo.png file.
		
		oafid:OAFIID:eog_viewer_factory:777e0cdf-2b79-4e36-93d8-e9d490c9c4b8:file:logo.png
			This specifies a specific image viewer to be
		   	used to display the file "logo.png", in this
		   	case the "EOG" program.

		file:logo.png!Zoom=2.0
			This represents the logo.png file in EOG at
		   	zoom level 2.0

		file:logo.png!Zoom=2.0,dither=max,notransparency
			The image logo.png is configured to be zoomed
		   	at 2.0 factor, to do maximum dithering and not
		   	use any transparency

	Now, what you saw above are some examples of stringified
	representations of monikers.  This means that they are not
	really monikers, it is the way a Moniker is represented in
	string form. 

	Monikers typically are created either by using a Bonobo API
	call that transforms the stringified representation into an
	object (which exports the IDL:Bonobo/Moniker:1.0 interface),
	like this:

	moniker = bonobo_moniker_client_new_from_name (moniker_string);

	Now, a moniker is only interesting because it can yield other
	objects when resolved.  During the resolution process, you
	specify which interface you are intersted on the moniker to
	return.  This is achieved by invoking the ::resolve method on
	the moniker and passing the repoid of the interface you
	desire, like this:

		Bonobo::Unknown control;

		control = moniker->resolve ("IDL:Bonobo/Control:1.0")

	This would request the moniker to return an object that
	implements the IDL:Bonobo/Control:1.0 interface.  This means
	that the object could be embedded as a regular Bonobo control
	in applications.  

	Maybe you do not want to get a control, but rather to resolve
	the moniker against a different interface, for instance a
	Bonobo::PropertyBag interface:

		properties = moniker->resolve ("IDL:Bonobo/PropertyBag:1.0");

	The resolution process might yield completely different
	objects.  

	The parsing and resolution process is all encapsulated into a
	single API call for your convenience: the bonobo_get_object
	function:

		Bonobo::Unknown bonobo_object_get (char *moniker_string, char *interface);

	
	Now, as I said, the resolution process might yield very
	different objects depending on the interface being requested,
	for example:

		x = bonobo_object_get ("http://www.gnome.org", "IDL:Bonobo/Control:1.0")
		y = bonobo_object_get ("http://www.gnome.org", "IDL:Bonobo/Stream:1.0")
	
	The "x" object might launch Mozilla which would in turn load
	www.gnome.org, and the returned object can be used as a Bonobo
 	Control, and used in your application as a widget.

	The "y" object on the other hand does not need all the power
	of Mozilla, we are only requesting the very simple Stream
	interface, so we might be able to implement this with a
	lightweight HTTP implementation: maybe a wget-based bonobo
	server, or a libghttp server.  

	Note that even if the stringified versions of the monikers
	were the same (ie, "http://www.gnome.org") the resulting
	objects might differ wildely depending on the interface being
	requested. 

* The Moniker parsing system

	During parsing the Moniker stringified, Bonobo will use the
	colon-terminated prefix as the toplevel moniker to be invoked
	for the resolution process. 

	For the prefix "file:" the file moniker will be used;  For the
	prefix "oafid", the oafid moniker will be used;  For the
	"queue:" prefix, the queue moniker will be used.

	Once the moniker that handles a specific prefix has been
	activated, the moniker will be requested to parse the
	remaining of the string specification and return a valid
	moniker.

	Each moniker typically will consume a number of bytes up to
	the point where its domain stops, will figure out what is the
	next moniker afterwards.  Then it will activate the next
	moniker and pass the remaining of the moniker stringified
	version until the parsing is finished.

	Each moniker is free to define its own mechanism for parsing,
	its special characters that are used to indicate the end of a
	moniker space, and the beginning of a new one (like the "#"
	and the "!" characters in some of the examples above).  This
	flexibility is possible because each moniker gets to define
	its rules (and this is important, as we want to integrate with
	standards like http and file).

* Monikers as an object naming scheme

	As you can see, monikers are used to implement a naming system
	that can be used to reference and manipulate objects.  As you
	might have noticed, the ::resolve method on the Moniker
	interface returns a Bonobo::Unknown interface.  And by
	definition, the bonobo_get_object also returns a
	Bonobo::Unknown.

	This means that the resulting object from the moniker
	resolution will always support ref, unref and
	query_interface methods.

	The Moniker object naming scheme is:

		1. Extensible.  new entry point into the object naming
		   space can be created and installed into the
		   system.  

		   This is achieved by installing a a new component 

		2. Hierarchical.  Monikers 

* Creating Monikers

	Monikers are created typically by API calls into the Bonobo
	runtime or by your own classes that implement monikers.

	FIXME: more.

** Moniker internals.


Monikers implement the Bonobo::Moniker interface, and when you
hold a reference to a Moniker, you can request that Moniker to be
resolved against a given interface:

	Bonobo::Unknown object;

	object = moniker->resolve ("Bonobo/Embeddable");

The 

* Internals

   Two steps: parsing, and binding.

* Object Name Space

  Monikers define an object naming space.  

* Instance initialization

	Explain how this works.

* Resolution of a moniker against an interface

   A moniker can be resolved against different interfaces.  The
resulting object might be different depending on the interface that is
being resolved.  To illustrate this, here is an example, lets say we
have the "http://www.helixcode.com" string representation of a moniker.

   The string representation of the moniker can be resolved against
the "Bonobo/Control" interface:

	bonobo_get_object ("http://www.helixcode.com", "Bonobo/Control");

   This could return an embeddable Mozilla component that is suitable
to be embedded into your application as a widget (because we are
requesting the moniker to return a Bonobo/Control interface).  If the
interface is resolved against the "Bonobo/Stream" interface, maybe
Mozilla is not required, and the process could use a smaller process
that just provides Bonobo/Streams, say a corbaified wget.

   The logic for this lives on the http: moniker handler.

* Pluggable mechanism for defining arbitrary initialization strings.

* Core monikers

	File Moniker
	Item Moniker
	OAFID Moniker
	New Moniker

** The "new:" moniker

The new moniker requests from its parent the "Bonobo/GenericFactory"
interface and invokes the method create_instance in the interface.
This moniker can be used in lieu of the OAF/Gnorba hacks to create
instances.

Typically this moniker would be invoked like this:

	bonobo_get_object ("oafid:.....:new:");

Although instance creation is somethin
A note about Oaf/Gnorba: 

* Ideal monikers:
	configuration moniker.
	VFS Moniker.

* The Configuration Moniker

The configuration moniker is invoked by using the "config:" prefix.
The string afterwards is the configuration locator.  The moniker
should support being querried against the "IDL:Bonobo/Property:1.0" or
"IDL:Bonobo/PropertyBag:1.0" depending on whether we are requesting a
set of attributes, or a single attribute.

For example, retrieving the configuration information for a specific
configuration property in Gnumeric would work like this:

	Bonobo_Property auto_save;
	CORBA_Any value;
	
	auto_save = bonobo_get_object (
		"config:gnumeric/auto-save", "IDL:/Bonobo/Property:1.0");
	value = bonobo_property_get_value (auto_save, &ev);
	if (value->tc == TC_bool)
	    printf ("Value: %s\n", (CORBA_bool) value->_value ? "true" : "false");
	else
	    printf ("Property is not boolean\n");

In the above example, we first use the bonobo_get_object routine to
locate the configuration object through its moniker.  The return value
from the bonobo_get_object is of type Bonobo_Property which is the
standard Bonobo way of manipulating properties. 

This has two main advantages: 

	1. By accessing the configuration engine through the moniker
	   interface we have eliminated the need to define a
	   C-specific API for the configuration management.  The
	   configuration could have been reached through any other
	   programming language that supports CORBA.

	   The GNOME project has always tried to define APIs that
	   could be easily wrapped and accessed from various languages
	   (particularly, we have done this with the toolkit and
	   recently with the CORBA bindings).  

	   But even if we have taken special care of doing this, and
	   there are continous efforts to wrap the latest and greatest
	   APIs, widgets, and tools, the bindings typically lag a few
	   weeks to monthsw behind the actual C API.

	   By moving towards CORBA, we only need to support CORBA in
	   the various programming languages and we get access to any
	   new APIs defined by it. 

	2. Any tools on the system that can manipulate a
	   Bonobo::Property or ::PropertyBag (a GUI in a visual
	   designer, or a configuration engine that persists/hidrates
	   objects, or a browsing tool) can talk directly to the
	   configuration engine all of a sudden, as we are using the
	   same interface method across every language on the system.

The Bonobo::Property interface is pretty comprehensive, and should
address most needs, the methods are:

	string   get_name ();
	TypeCode get_type ();
	any      get_value ();
	void     set_value ();
	any      get_default ();
	string   get_doc_string ();
	long     get_flags ();

Now, this interface as you can see does not specify an implementation
for the actual backend.  There are various possibilities. 

** The Missing pieces.

   Validation of changes.  We want to have some sort of "veto" if a
set of clustered changed are invalid.  (beginTrannsaction,
endTransactions and let the code validate the changes for correctness).


** What about GConf?

   GConf is a configuration management infrastructure that provides
the following features:

	1. A schema system for specifying the various configuration
	   options, as well as their documentation and initial values
	   (default values).

	2. A way for the system administrator to override values in a
	   system-wide fashion (this encompasses a network-wise setup
	   if desired).

	3. A change notification system: applications might be
	   notified of changes to various values they might want to
	   keep track of. 

    There are two drawbacks to GConf currently:

        1. Although gconf provides pretty much everything that is
	   required, but it is a C-based API that needs to be wrapped
	   for every language that wants to support GConf.

	2. GConf is limited in the kind of information that can be
	   stored on its database.  A BonoboProperty stores a
	   CORBA_Any which can contain any of the simple CORBA types
	   (strings, integers, floating points, booleans), structures,
	   arrays and unions. 
	   
    The actual engine and backend for GConf could become the
configuration moniker handler, only the API would be replaced as well
as the actual storage system to support the more complete CORBA_Any,
and the ad-hoc CORBA interface can be replaced with a more powerful
system.

** Open Issues

*** Specifying the location for configuration.

   The syntax for accessing the configuration has not been defined,
but we can cook this up pretty easily.

   Forcing the configuration data to be loaded from a specific
location.  Although the arguments to the moniker could be used to
encode a specific location, for example:

	config:~/file.config!auto-save

   It seems more natural to use the file moniker to provide this
information, for example:

	file:~/file.config!config:auto-save

   The config moniker can test for the presence of a parent, and if
the parent exists, then it would request one of the Persist interfaces
from it to load the actual configuration file, and provide access to
it. 

*** Transactional setting of values. 

    It might make sense to "batch" a number of changes done under a
prefix to avoid listeners to a range of keys to reset themselves
multiple times.  Consider the case in which a command line tool makes
various changes to the background properties, say the changes are done
in this order:

	background = bonobo_get_object ("config:desktop/background", iface);
	bonobo_property_set (background, "gradient", "true");
	bonobo_property_set (background, "color1", "red");
	bonobo_property_set (background, "color2", "blue");

    If the real configuration program for handling the background is
running at that point, it will have registered to be notified of
changes to all those values.  The changes might be very expensive.
For example the code migh react to every change and recompute the
whole background image on each change. 

    An optimization would be to tag the beginning of the transaction
and the end of it in the client code to allow listeners to get batched
notification of changes:

	background = bonobo_get_object ("config:desktop/background", iface);
	bonobo_property_bag_batch_push (background);
	bonobo_property_set (background, "gradient", "true");
	bonobo_property_set (background, "color1", "red");
	bonobo_property_set (background, "color2", "blue");
	bonobo_property_bag_batch_pop (background);

    This would allow the listener code to batch all the expensive
requests into a single pass. 

*** Configuration handlers

    Consider the example above, we would like to be able to change
properties on the system and have those properties to take effect
independently of whether a listener is registered or not.

    A property handler might register with the configuration moniker
to be launched when a property changes.  This could be done in a file
installed in a special location.

* The GNOME VFS becomes deprecated.

   The GNOME VFS would become unnecessary.


* Conclusion 

   Monikers are very powerful mechanisms that can unify the name space
of objects in the system and can be used to provide a uniform API
across components. 
