NetBeans Runtime Container
From APIDesign
The module system engine behind NetBeans based applications.
Contents |
Manifesto
Nobody is surprised anymore that operating systems, and distributions build on top of them are coded in modular way. The final product is assembled from independently developed components. This allows to coordinate work of many people around the world and assemble very complex system in a reasonably reliable way.
Similar change is happening to individual applications. They get more and more complicated, they get assembled from pieces developed independently, they need to be reliable. The way to achieve and handle all that complexity is to code applications in modular way.
Because the present applications grow in size and functionality, it is just necessary to separate them into individual pieces/components/modules/plugins. Each such separated part then becomes one element of the modular architecture. It is supposed to be independent, provide well defined interfaces that it exports (e.g. other parts can use them) and imports (e.g. those that it needs for itself to run correctly).
Splitting application into modules can greatly improve the design. It is not hard to guess that a monolithic piece of code where every line in any source code can access any other source file is much more interconnected and unreadable than a code which introduces many modules and allows such uncontrolled calls to happen just inside the module.
Modular Application vs. Object Oriented Applications are in similar position as spaghetti code vs. structured programming in the sixties. The spaghetti code used to be name of one big fortran or basic program where every line of code could use GOTO to another place in the program and this was done in quite chaotic way that at most only the writer of the program could understand. Structured programming tried to reduce the complete disorder by introducing blocks of code: for loops, while loops, if statements, procedures and calls to procedures. Indeed this improved the situation and the readability and maintainability of the application increased. If nothing else, one could be sure that a call to a method has to return once (of course except in boundary conditions).
The plain object oriented style of programming in some way resembles the situation before structured programming arived. The application is full of classes and nearly any method in class can call any other. Indeed there are public, private, protected access modifiers, but most of the granularity is done on single class level and that is too small entity to serve as a basic building brick to an application design and definitely too small to create a unit of evolution.
Modular applications in Java are usually composed of modules, where one module is a collection of classes. Some of these classes are public and thus serve as an exported API to other modules, some of them are private and cannot be accessed from outside. Moreover a module has a set of dependencies on other modules, clearly stating on a very high level what functional environment it requires to execute well. Indeed inside a module once can still apply the worsest coding practices, but still the architecture of an application can be well observed by checking the dependencies among all its modules. If one module does not have dependency on another, then it is clear its classes cannot directly access the other module's ones.
This cleans up the architecture as it prevents accidental GOTOs to completely unrelated parts of the code base. Sometimes people say that their application is too small for use of modular architecture. Well, it may be, but if it is not just student's project, then it is going to evolve and as it evolves, it is likely to grow. And rewriting a messy interconnected object oriented application to have nicer modular design is often too hard task that many people are not willing to try and rather prefer to live with old, monolithic code that is harder to maintain, but known to work. Just look at the JDK: Classes from Java.lang implement interfaces from Java.io, sometimes from Java.util and everything is moreover wired with sun.* packages that it is nearly impossible to imagine someone to really split this into modular parts. This is just a natural result of coding in the spaghetti object oriented style.
There are just dozens of problems related to poor architecuture coding style and modular programming is one way to solve them. Modularity would give systems clearer design, control of dependencies between modules and give developers more flexibility in maintanence. Think about it when starting any new project - nevertheless big it is, do it in modular way . It is going to be big plus for the architecture of the whole application as it grows from its child days.
NetBeans Offering
NetBeans is a term with many overloaded meanings. It can be used to talk about the website [www.netbeans.org], about the open source community around it. NetBeans is also well known for its IDE. Recently a significant amount of attention has been finally given to use of NetBeans as a platform for development of rich client applications. However it shall not be forgotten that behind all of this there is the NetBeans Module System.
NetBeans Module System
NetBeans Module System is the first runtime application container for modular Java applications. Being in production use since year 1999 it forms a well tested framework that can handle life-cycle, cooperation, communication between each module in any application written in modular way.
NetBeans IDE 5.0 contains an excellent support for writing NetBeans Plug-In Projects and greatly simplifies the process of creation of pieces of modular application. Following paragraphs are going to demonstrate how to get, configure and write sample Hello World! application based on modular architecture provided by NetBeans Module System and using NetBeans IDE 5.0 as the development environment.
Getting The NetBeans IDE
This should be pretty simple. Just download and install the bits that can be found at [www.netbeans.org]. Any version newer than 6.0 shall be suitable for this demo.
Getting The NetBeans Module System
If you have build of NetBeans IDE 6.0 or newer, then you already have the right version of NetBeans Module System. Otherwise either download the another build of NetBeans Platform or IDE or use another project that is built on top of NetBeans - for example binary ZIP file of DVB Central. Then just register it in the NetBeans Platform Manager as another platform you want to develop against.
Next step is to create a skeleton for your modular application. Inside your NetBeans IDE select New Project and create new NetBeans Module Suite Project. Choose its properties and just either use the default NetBeans platform (if you have build of NetBeans 6.0) or the platform that you have downloaded and registered in previous step.
To get just the miminal configuration needed for modular development go to Libraries panel in the customizer and carefully select the right modules. In fact deselect nearly all and keep just the few that compose the NetBeans Runtime Container:
- Utilities API - set of general utility methods and base communication primitives
- Module System API - defines interface of the module system and methods to interact with it
- FileSystems API - virtual file system wrappers, most needed for access to resources defined by modules
- Bootstrap - implementation of the module system logic
- Startup - glues all these pieces together
Select them as shown on the picture and your own personal NetBeans Runtime Container is ready to accept your modules. Just to try it, run the suite with a run command from the popup menu. The application starts, shows welcome screen and exists as there is nothing else it could do.
The main Module Class
To begin the actual coding you need to create a module to hold your java classes. Create new NetBeans Module Project and make it part of the suite.
The simplest thing which any proposal starts with is to write hello world. In NetBeans world this means to create a ModuleInstaller class and override its restored method. Just use the new file dialog and create new object from NetBeans Module Development/Module Installer template. Then just add the hello code:
public void restored() { System.err.println("Say Hello!"); }
Now start the application again and check the result. As in the previous case, the splash screen is shown, the application is started and also exits. However in contrast to the previous execution, among other things also the output contains Say Hello! printed by your first module. Is it not impressive?
Registering and Discovery of Services
Indeed it has to be admitted that using a module container like NetBeans to write such a trivial application like the hello world example described in previous section is more or less ridiculous. Simple main method would suffice. However the benefits will show as soon as the application gets more complex and starts to use the modularity in its own architecture.
This section is going to show how to do registration and discovery of services and establish communication channels between individual modules in the system. First of all let's change the Hello world module to be more visual and also to offer an API to for others to contribute hello messages.
First task is to create an API. By default no other module can access classes of another module. This is enforced during runtime by classloader machinery provided by the NetBeans Runtime Container. Think about this as additional access modifier extending the standard java choices of private, package private, protected and public. In NetBeans we moreover add a notion of public vs. private packages. As an API is supposed to be public, it needs to be put into new package and this package has to be marked as public in the projects configuration dialog. So create:
package org.test.sayhello.api; public interface HelloTip { public String giveMeATip(); }
and make sure that the package org.test.sayhello.api is marked as public in the API Versioning category of the project's customizer.
Now we are going to write a Swing window that will discover all registations of the interface using standard NetBeans way for doing it - using Lookup. So let's write a JFrame that will have a text field and fills it with some message when its Say Hello! button is pressed. Put the code in the module that provides the HelloTip interface, but into some some non-API package like org.test.sayhello:
Lookup.Template t = new Lookup.Template(HelloTip.class); Lookup.Result res = Lookup.getDefault().lookup(t); Collection all = res.allInstances(); if (all.isEmpty()) { text.setText("I do not know what to say"); return; } List l = new ArrayList(all); Collections.shuffle(l); HelloTip tip = (HelloTip)l.get(0); text.setText(tip.giveMeATip());
To compile this code you need to also add a dependency on Utilities API as that is the module providing Lookup, which is essential for the example. Also you may need to include "Swing Layout Extensions Integration" if you let your frame to use its default layout and you run on JDK 1.5. So do not forget to either change the layout of your frame to BorderLayout (or any other), or make sure the "Swing Layout Extensions Integration" is included in your suite libraries or use JDK 1.6 which includes the layout in its standard Swing libraries.
This code just gets all the registered implementations of HelloTip, randomly selects one and assigns its tip to the text box. Beyond this, do not forget to show the frame in your ModuleInstall otherwise nothing visible will happen. And of course there is no registered HelloTip provider yet. That is why, let's write one!
Create new module, put it also into the suite and adjust its dependencies to include the module that provides HelloTip interface. Then create a class that implements that interface:
package org.test.ciao; public final class Ciao implements HelloTip { public String giveMeATip() { return "Ciao, Mondo!"; } }
Indeed this is not all. The NetBeans lookup mechanism based around a JDK standard which has been in use for long time, but it got its own API in Java6 and which requires creation of additional file in META-INF/services directory. That is why create META-INF/services package in your module sources and inside it create file named org.test.sayhello.api.HelloTip. Put following line into it:
org.test.ciao.Ciao
This instructs the NetBeans Lookup to create instance of class Ciao when someone asks for implementations of interface HelloTip using Lookup.getDefault().lookup(HelloTip.class). To see it with your own eyes, start your app and make the system say hello. The Ciao version is going to be shown. This is a bit more interesting hello world version, isn't it?
Using Virtual Files For Communication
Of course the example would be more funny if it contained more providers. That is why let's try to write another one. And in order to show something new, we'll demonstrate the concept of virtual files and layers.
Nearly every module in NetBeans has a special file called layer inside itself. The content of it is an XML filesystem - e.g. an XML file that describes folders, files and their content. We are now going to create new module with another implementation of HelloTip that is going to read its message from a file in folder HelloWorld. To access the virtual files we will use FileObject defined by the FileSystems API. The important part of the code is:
public String giveMeATip() { FileObject root = FileUtil.getConfigRoot(); FileObject hello = FileUtil.createFolder(root, "HelloWorld"); FileObject[] arr = hello.getChildren(); Collections.shuffle(Arrays.asList(arr)); return read(arr[0].getInputStream()); }
It finds or creates the HelloWorld folder, selects one of its files and returns its content as a tip. Now it is just necessary to add at least one of such file to the folder. This can be done by expanding Important Files node of the module, expanding the layer node and modifying its content by creating a folder and a file in it. As a result the text of the layer shall look like:
<filesystem> <folder name="HelloWorld"> <file name="name-of-the-file" url="url-to-the-file"/> </folder> </filesystem>
Now there is the time to start and test the application. Both of the providers shall be registered and repetitive asking for Say Hello! shall randomly switch the messages. UserDir and its Content
Because every module can have its own layer, it is now possible to create yet another module, now completely without coding that will just register another file into the HelloWorld directory with a new hellotip to show. So create new module, expand its layer file and choose this layer in context. You should see the HelloWorld folder with a file registered by the other module. Create yet another file next to it. Start the application and you can see that the content of this file has been read by the ReadTip module and that you have in fact extended its behaviour without writing a line of code in your module.
And this is not enough. A similar feature is available to any user without even creating a module! Please note that when your application starts it prints an information about a lots of directories including location of UserDir - usually it is thesuitedirectory/build/testuserdir. Find this location in your file manager and just put a new file into $userdir/config/HelloWorld/thefile, write something into it and go back to your application. After few requrests for Say Hello! your newly created tip shall be shown.
Summary
There are two things to remember after reading this article. The first one is that modular applications are the necessity and one shall always develop in a modular way. The second one is that developing with NetBeans Runtime Container is simple and easy. By selecting the right modules one can strip it down and make it usable even for non-visual applications. Moreover creating a new module using NetBeans IDE is a matter of few clicks and using the lookup and layers is straightforward and powerful. Building with and on top of NetBeans is simply the best choice one can make!