Current operating systems are based on the concept of locally installed applications. Even thought networks are used pervasively, software is run locally and normally needs to be downloaded manually first. Of course there has been great progress in this area. E.g. the Debian Linux distribution provides the apt system that downloads and installs arbitrary applications using only a single command. While this system makes system administration really easy, you still have to interrupt your work when you try to use a shell command that is not installed. Java has a nice system called Java WebStart that allows you to click a link on a website and then automatically downloads and starts the application the link referred to.
What this article is about is to remove the step of explicitly telling the computer to download the missing application, using a mechanism more general than Java WebStart. The mechanism presented here is more general in that it also allows you to use an arbitrary identifier to specify the application you want to download and start. The identifier could be a URL, like in Java WebStart or a name like when you want to execute a shell command.
Depending on how complex the applications are, they can either be cached or be installed locally when they have been downloaded. These are the most common ways to avoid repeated downloading of previously used applications.
Because Jini provides a very powerful service lookup mechanism, it is chosen as the basic services platform. Jini services will be used in this system in the same way normal operating systems use local applications.
Since Jini services are written in Java, those operating systems could benefit most from an integration, that deeply integrate Java. The Jnode project develops a Java-based OS that is itself programmed in Java and is therefore an ideal candidate for running Jini services.
Having a look at the proposed system does not only include the computer running the Jnode operating system. Since Jini is used to leverage the applications provided by other computers, these other computers need to be depicted as well.
Having a look at the picture shows three computers. The one running the Jnode OS with its numerous internals and on the right two other hosts, both running a lookup service. (A lookup service is a special-purpose Jini service, similar to a registry, that is used by other services to announce their availability. It is also used by services and clients to find [other] services.) Of course it is possible to use only a single lookup service, but in order to add some redunancy it makes sense to use more. On a large corporate network services may be provided by a set of centrally administered lookup services running on dedicated machines. On a small office, or at home you may share services with your colleagues or friends in a peer-to-peer manner. Hence it is perfectly possible that the two computers running a lookup service also run the Jnode operating system.
Turning to the internals of the machine that runs Jnode, we can see that the lookup services at the remote hosts are not used directly. Instead a LookupCache is used to store previously used services for later reuse. This is a performance optimization to both reduce network traffic as well as perceived latency when looking up services. LookupCaches are commonly used in Jini-based systems. If made persistent, they can have the nice side-effect of providing previously downloaded services when the network is down or the computer is offline for another reason.
The LookupCache itself is used by the Shell and the DeviceManager. Both will serve as use cases later. The shell loads commands using Jini that aren't installed locally. The device manager does so with device drivers. Even thought any command or driver might be available from the network, they may sometimes be needed before network access has been set up. I.e. at least the installer of the OS must contain basic drivers for graphics and network cards etc. - hence the local device drivers and commands.
Jini was developed and introduced to the market by Sun Microsystems in 1999. Since then a community has been formed that guides the future of the technology. Jini is a service-oriented architecture for network services that addresses some specific problems that are typically experienced in distributed systems.
Contrary to an often cited myth Jini is not specifically for devices. Jini services are simply Java objects that are passed across the network. This allows for a wide variety of service implementations. Services may consist of a single object that performs all work inside the Java Virtual Machine (JVM) that downloaded it. Alternatively a Jini service may consist of a proxy object that is downloaded to the JVM and communicates to a backend server using Java RMI, raw sockets, XML or whatever protocol. Finally there is even the option to have an embedded device, that is not Java enabled, provide a Java object that acts as a proxy to the embedded device.
Together with the members of the Jini community, Sun recently decided to release the upcoming 2.1 version of Jini under the Apache Software License 2.0 and thereby make it open source. While the 2.1beta release is already available and is provided under an ASL 2.0 license, many community projects are still in the process of migrating to the new license.
While Jini also provides concepts like distributed transactions and methods for freeing unused remote resources, the parts used most for integrating Jini into the Jnode operating system are the lookup and discovery specifications which will be introduced in the next two sections.
Jini uses lookup services to register and find services. Each Jini service is registered with a lookup service with an accompanying set of attributes. Thereby you can lookup services by specifying a set of Java types the service should implement. Hence you may also find services that implement an interface that is dervied from the interface you are looking for (in opposite to String-based matching used by other systems, that supports only exact match).
Additionally you may look for services that specify a certain set of attributes. These attributes, also called entries, are normal Java objects that are instances of classes that implement the net.jini.core.entry.Entry marker interface. When entries are used to find a matching service, the type implemented by the entries and all their public, non-final, non-static fields are compared. The fields of an entry may not have primitive types (e.g. int or boolean need to be replaced by Integer or Boolean instances).
When querying a lookup service for a particular service, a ServiceTemplate is used that specifies the types matching services need to implement and a set of entries that must have been added to the lookup service together with the service proxy. The entries contained in the ServiceTemplate are used as templates while the lookup query is evaluated to match candidate services. The fields of the template entries may also be set to null, turning them into wildcards (i.e. a value of null in a field in the template entry will match all values of this field in all other entries of the same type).
The above image illustrates service lookup. On the left a number of services contained in the lookup service are depicted. The right side shows the template supplied by the client to find one or more matching services. The lookup process starts by examining the Driver service. Since it is not an instance of the Command type required by the template, it doesn't match and will not be part of the set of services returned to the client that issued the lookup query. The second service has the same type as the template and also provides an Alias entry. Nonetheless it doesn't match the template, since the value of the alias field of the entry is "Ping" and not "Date". Fortunately the third service implements the sought type and provides a matching Alias entry. (Note that if the alias field of the template entry would have been null, the second service would also match and be returned to the client.)
The proxies of services registered at a lookup service are stored inside the lookup service in their marshalled (serialized) form. This frees the lookup service from unmarshalling the registered service objects / proxies (they needed to be marshalled to be transported over the network). This is also the reason why proxies and entries are not compared using their equals method, but based on their marshalled representation.
Discovery is used by clients and services to find a lookup service. They may search for lookup services on the local network using multicast or may contact a number of lookup services on the internet using unicast. When lookup services have been found using either uni- or multicast, their service proxy (a marshalled Java object) is downloaded and can then be used by the client.
Discovery also supports arbitrarily named groups that can be chosen to partition collections of services into federations.
Since version 2.0, Jini also supports a comprehensive security model. Based on the Java Security Architecture, services may specify which clients may invoke which methods on them and if and how to secure the network connection between proxy and backend. It is also possible that clients dynamically grant permissions to individual services instances (i.e. not to all services of the same type, but to single instances).
Jini also supports a mechanism to gain trust in a proxy from an insecure source. This is neccessary because the proxy may be corrupted on its way through the network. The proxy trust mechanism is described in an interview about Jini security Bill Venners did with Bob Scheifler in 2002.
Jnode (the Java New Operating System Design Effort) went public in may 2003. The project is lead by Ewout Prangsma who was working on a number of similar systems before. Jnode is a Java Virtual Machine that does not need another operating system to run inside, but instead runs as OS itself. Development is currently undertaken by a handful of core developers and interested people who commit irregularly. Jnode is released under the LGPL license, so everybody is welcome to contribute.
Most notably is the fact, that besides a small microkernel, everything (even device drivers) is written in Java. This leads to a system that should not suffer from security threads C programs often face (e.g. buffer overflows).
The project recently released the 0.2 release. The project homepage is located at http://www.jnode.org/.
Jnode uses the Grub bootloader to load and boot a kernel image. After the bootstrapper code is executed, the JVM is initalized, which is the first time Java code is run. Jnode does not contain any C code. Only the microkernel and small amounts of the VM are written in assembler. The VM supports the standard Java features (GC, multithreading, the Java Security architecture). To implement the Java API, the classpath project is used.
The device drivers in Jnode are also programmed in Java. Right now drivers for graphics- and network cards as well as keyboards, mice, disks and CD-ROM drives are available. Device drivers are currently deployed as plugins (see below).
Jnode is currently a single-user OS. To enhance performance, all Java code executed on Jnode is compiled to native code on-the-fly before it is executed.
Jnode uses a custom plugin framework that uses Jar files accompanied by XML descriptors to organize the various parts into a modular system. The jar files contain the classes and resources needed to run the plugin. The descriptor specifies which packages are contained in the plugin and which other plugins are used by it.
The plugin framework also uses the concept of extension points. Plugins can define extension points that other plugins implement. The plugin that provides the extension point can then obtain a list of all plugins that implement the extension point. Among others, the shell currently uses this mechanism to make a list of available commands. Therefore the shell defines an extension point and all commands implement that extension point. When the user enters the name of a command, the shell can then search through the list of available implementations of the command extension point to find a command with the name equal to the one entered by the user.
Jnode currently provides a shell that is started when the system has finished the boot process. The shell already allows commands to be run that are implemented as plugins. It is also possible to start Java applications from the shell by entering the name of their main class. To run custom Java applications users may set a custom classpath by using a special shell command.
The graphical user interface is currently in the works. No desktop is available so far.
This section discusses a number of uses cases to show which parts of the Jnode operating system may benefit from using Jini technology. Of course, those parts can benefit most, that are already implemented as kinds of modules or plugins.
Albeit it may be possible to advance the extension points model using Jini, I thought it may be better to stick to less abstract examples (that I understand ;-) ). Therefore I will focus on making shell commmands and device drivers available as Jini services.
Since there are currently no GUI applications available for Jnode, shell commands are the most application-like entities that can currently be used with Jnode. To make use of all commands that have ever been implemented, without needing to explicitly update the installation when new ones become available, Jini will be used to load commands that aren't available locally.
Jnode commands are Java classes that implement the Command interface. This interface (see below) provides a single method that is used to invoke the command. The arguments entered by the user as well as input-, output- and error streams are provided via the methods parameters.
public interface Command { execute(CommandLine commandLine, java.io.InputStream in, java.io.PrintStream out, java.io.PrintStream err) }
When Jini services are used as commands, they will also implement the Command interface. This allows the services to be directly used as commands, and enables clients to lookup command services by querying the lookup service for services that implement the command interface.
Jnode internally uses CommandInvoker
s to invoke commands. To load Jini services
as commands, the DefaultCommandInvoker
class is extended to create the
JiniCommandInvoker
class which uses a LookupCache
to lookup services.
The invoke
method of the CommandInvoker
is called when the user
entered a command and hit return. When no command with the name given by the user is available locally,
the invoke
method of the JiniCommandInvoker
will use the
LookupCache
to contact lookup services and query them for a command services with
the name entered by the user. Already downloaded command services will be cached locally inside the
LookupCache
to have them at hand for later invocations and avoid repeated lookups
of the same command service.
Of course the JiniCommandInvoker
needs to lookup individual commands.
Therefore it is not enough to query the lookup services for a list of all services implementing the
Command
interface. The name (a.k.a. alias) of the command to be found also needs to
be added to the lookup query so that only command services with a matching alias are returned.
In order to find a command service for a given alias, the alias entered by the user will be used to
create an Alias template entry (according to the right side of the above image) which is in turn added to the
template that already specifies that the lookup should return services implementing the
Command
interface. The first returned command service which is trusted by the user
using the proxy trust mechanism will then have its execute method invoked with the provided
arguments. If no matching command service was found, an error will be printed to the shell indicating
that no command with the given name could be found neither locally, nor on the network.
Like all other operating systems, Jnode uses device drivers to access all kinds of devices. While Jnode already provides the ability to write platform-independent drivers it would also be nice if the OS would automatically discover drivers for new hardware without requiring a CD to be inserted. Since most computers, once installed, are connected to the internet and broadband connections are speading at a fast pace, it makes perfect sense to load the device drivers from the network. Of course there are exceptions. E.g. no drivers can be loaded from the network during installation when no network card has been installed so far. Hence there will also be a need to have a set of locally installed drivers available.
Similar to the way shell commands are loaded, Jnode already provides a modular device driver architecture that makes it easy to load drivers using Jini if none of the locally available drivers fits for a certain device.
Jnode loads drivers using a DeviceManager
. This DeviceManager
uses a number of DeviceToDriverMapper
s to load drivers for devices. To load a driver,
the DeviceManager
successively consults all available
DeviceToDriverMapper
s to find a suitable driver and will use the first driver returned
by a DeviceToDriverMapper
. To load Jini drivers, a special
JiniDeviceToDriverMapper
is added to the end of the list of mappers so it is consulted
when no driver could be found locally. This special mapper uses a LookupCache to lookup matching
driver services from the Jini federation.
In order to find a driver matching a given device, again Jini entries play a key role. While all driver
services will extend the abstract org.jnode.driver.Driver
class, it doesn't make much
sense to use this class for lookup because it is too broad. To find a specific driver, additional entries will
be used, that probably differ depending on the bus (e.g. PCI, PS2, USB) used to connect the device to
the host. In the image above the USBInterface
entry is shown, that contains diverse
identifiers used by USB to identify interfaces implemented by a device. To match a driver to a device,
the interfaceClass, interfaceSubClass and interfaceProtocol fields may be used for a lookup query.
Similar to the entries used for matching the shell commands, the entries are derived from
classes already used inside Jnode. The existing classes could also be used, but using separate classes
is more flexible since both types of classes are made for different purposes. E.g. the
USBInterface
entry uses Integer
instances to represent the various
identifiers to comply with the Jini Entry Specification whereas the class it was derived from uses the
primitive type int
.
Jini also provides a Surrogate Architecture that could potentially be used to load drivers. Unfortunately this architecture needs a custom specification and implementation for every transport (e.g. ethernet or usb). There are already a spec and implementation for a Surrogate Architecture using IP that are used to load Jini services that access embedded devices over the IP protocol. Also efforts to create a USB interconnect specification for using the Surrogate Architecture to load Jini services as kind of drivers for usb devices were restarted recently.
Because device drivers are obviously security sensitive, they provide a good case for the Jini security mechanisms, most notably proxy trust verification, which allows to verify the driver service originates from a trusted provider and hasn't been modified on its way to the client.
Desktop services are surely another interesting area for connecting Jini and Jnode. Since Jnode doesn't have a desktop so far and Jini is currently also deployed most often for machine-to-machine communication, both technologies are still in their early days with respect to this field. This provides great room for experimentation.
While it may at first sound strange to turn the desktop into a distributed system, this is just an adaption on how desktop computers are used today. Not only is much software downloaded from the internet. Many people also use more than one computer or work in groups that would benefit from being able to easily share Jini services.
This scenario is especially interesting when it come to switching to a new computer. That's a real pain when everything is installed locally. But when you only use services from the network you simply turn off your old machine, turn on the new one and grab the services you were using on the old machine again. There may even be a Jini service that keeps track of your desktop session, and automatically reopens all services you used before.
This document is updated from time to time to fix errors and update external links. Below you find a list of these corrections.
Version | Date | Correction |
---|---|---|
1.2 | 06/20/2005 | Fixed errors in image 2 (thanks to Andreas Semt) |
1.1 | 06/01/2005 | Changed link to the Jnode homepage to http://www.jnode.org |
1.0 | 05/20/2005 | Initial publication |