2. NetBeans Module System Basics

The NetBeans IDE (version 5.0) has a ton of modules inside. More than one hundred modules! There're modules for ant, debugging, xerces, projects, class file handling, database handling, all sort of editors (java, sql, html, xml... ) and editing capabilities (bookmarks, templates, folding, syntax highlighting, ...), internationalization, configuration options, J2EE (servlets, EJBs, Web Services, etc.), XML (catalogs, editing, XSLT processing, etc.), GUI form editing, tasks lists, and many others.

That's a lot of modules, isn't it?

Now, how is it they all dance together gracefully? What is that misterious mechanism that allows these modules to handle dependencies between them, to change internally withouth the rest noticing, to be downloaded from the network automatically? How is it that these modules are initialized so fast? How is it possible that these modules can be enabled and disabled at runtime at your will?.

All those features are provided by the module management system of NetBeans (which can be used for free outside of NetBeans, by the way, so you can reuse it in any other application of your liking). This is indeed an excellent piece of software. It's so important for the rest of chapters that deserves some attention.

In this section I present some of the main charateristics of modules I've seen, from an architectural point of view. If I were you I wouldn't try to remember everything in this section now. Just read through and get acquaintanced with the terms we'll be using in the next sections. I'll try to explain the details whenever they're needed in the future.

2.1. Public APIs, exported packages, documentation and the importance of APIs

Only a part of a module is visible to the rest of modules. This public part that is visible to other modules is known as the "Module's Public API". It works basically as a Java interface: you can work with the interface (with the Public API) without knowing how the implementation works.

Figure 1. Public APIs

Public APIs

Separating Public APIs from implementation details of modules is a very important design decission, of course.

While developing NetBeans modules it is important to remember that you must explicitly indicate which Java packages of your project are visible to other modules. This is, you have to mark your packages as being public. (What I mean is that you have to indicate which packages conform the socket of your module, and this socket is used by the rest of modules to plug-in to yours). If you don't mark any Java packages as public, then other modules won't be able to load classes from your module, and your module (although working) won't be able to be used by the other modules.

All NetBeans modules (for the editor, syntax highlighting, etc.) have a Public API, too. That's the part of the modules that you can use. For a detailed description of all NetBeans Modules Public APIs you can see the NetBeans Module Javadoc.

NetBeans Modules Public APIs have different degrees of stability. Solid modules have a "frozen" API, that you can use safely. Modules under development may have different degrees of stability. You may use those Public APIs, but be warned that they are subject to change, and that a change in those may require changes in your code.

The NetBeans Team seems to be quite perfectionist about building APIs! Of course that's good for me, as an user of the NetBeans Platform, because I prefer programming against a well designed API. The NetBeans Team has Games on API design, Tutorials on API Development and even Tutorials on API Reviews!.

2.2. Embracing change

Of course Public APIs change (well, we all do change, right?). You can try to build a fancy, perfect API, but time will erode it in ways you haven't thought of, and eventually you'll be forced to change that API. That's life!

So instead of trying to build perfect APIs, which seems to be somewhat futile, what about embracing change?. Of course trying to write good APIs is important, but embracing change is even more important. What about marking each API with a version, so that everybody knows which API to use (and which to change)?

Figure 2. Versions

Versions

This is exactly what the NetBeans Platform does: build good APIs, but use versions to mark each API too. Furthermore, the NetBeans Platform allows you to version the implementation of each module, so you have different types of versions:

Major Release Version

This version is used to mark a Public API. If you change the Public API too much then you probably want to change this "Major Release Version".

Specification Version

This version is used to mark a minor change to a Public API, that remains compatible with a prior Public API. You increase this, for instance, if you have added a method in your API (but the rest of methods remain intact).

Implementation Version

You use this to mark a change in the internals of your module only. This is usually a version for your eyes only, so the rest of people using your module shouldn't be worried about implementation versions.

Don't get too worried about versions while you're beginning to work with the NetBeans Platform. As you get experienced, and start building modules that others use then you should start using versions thoroughly. This makes life easier for you.

2.3. Using... and being used: dependencies

Figure 3. Dependencies (maven)

Dependencies (maven)


One important aspect of a modular system is how the dependencies are handled. Most successful modular systems declare dependencies explicitly in a plain text file. Linux distros, for instance, do usually have a list of all software packages available, with versions and dependencies between those. Maven has also a good depencency tracking mechanism. Having a list of dependencies available helps you to see how important a change is (how many other modules are affected by a change). It also allows you to distribute a single module for update, and make the rest of modules to be updated automatically.

As we will see in the next section, the NetBeans IDE has an easy way to declare dependencies between modules. This makes things extremely easy for you, because you can visually track, declare and handle which modules you need.

2.3.1. I need you, you need me, I need you...

While managing depencencies, you must remember that dependencies must not be cyclic. If you have cyclic dependencies between a module A and a module B (because A needs B and B needs A) then, how are you supposed to initialize those modules? You cannot initialize A, because it needs B that is not initialized; but you cannot initialize B either, because it depends on A that has not been yet initialized. That's a problem.

The NetBeans IDE detects cyclic dependencies between modules, when you are building them, so there's no problem in doing that by accident.

If you seem to be stuck on a cyclic dependency problem then that's usually due to a bad modular approach to your software. Consider splitting modules in smaller pieces, factoring out common functionality. For instance, if you have a cyclic dependency between modules A and B, then consider extracting common functionality into another module C, and make A depend on both B and C, and B depend on C. You don't have cyclic dependencies then.

2.3.2. I need you, you need him, I don't know him

Another important thing to remember about dependencies is that they're not transitive. So if you declare dependencies with between a module A and a module B, and if module B depends on C, then A won't be able to load classes from C. Because there're no explicit dependencies between A and C.

That may seem too restrictive, but I think it's all the way round. It's easier to manage explicit dependencies than implicit ones. Remember that if you need to load classes from a module X then you need to declare a dependency with that module. It's a rule easy to remember.

2.4. Don't shout: I can't hear you anyway

Another important thing about NetBeans modules is that modules are completely isolated from each other. Each module runs in its own classloader. You can shout as much as you want from module A, that module B may not be hearing you!! That's why you may have ClassNotFoundExceptions in the beginnings (I suffered from those too, until I learned the reasons for this).

Figure 4. Don't shout...

Don't shout...

The fact is that a module A cannot load a class C from module B unless these conditions are met:

  • The class C is in a public API of module B (well, this seems reasonable: after all a class must be in the public API to be visible to other modules, right?)

  • Module A has declared a dependency with module B (well, of course, after all it's trying to load a class from module B, right?)

Although it may seem a little bit weird, the fact that one module cannot load a class from another module is indeed a great feature. This feature allows for better encapsulation and makes module-coupling explicit (because dependencies are explicit, remember?), so you cannot end up messing things up just because you're using the wrong class from the wrong module by accident.

Another great advantage of this feature is that you can bundle different versions of the same library in different modules. So, for instance, you can build a module that contains Apache BCEL 5.2 and another module that contains Apache BCEL 5.0 (for some legacy code that needs it, for instance). And them both can coexist in the same application. (Well I think this is feasible, although I haven't experimented it myself).

If you're having trouble with the module's classloader you may be interested in reading more.

2.5. One for all, and all for one!

Another important problem that we usually face when we start programming with the NetBeans Platform is module initialization.

When you're building a system with several dozen modules you should beware of startup time. Say you have fifty modules, and that each one takes 500 ms. to get initialized. If you initialize all modules at startup time then you'll need at least 25 seconds to get all modules initialized. And the user is going to get nervous about that!

What I mean is that when you're building a modular system, you should keep in mind that there're several other modules out there. And that you cannot grab all initialization time for you yourself. All modules should cooperate to reduce as much as possible the initialization time. It's one for all, and all for one!

Figure 5. An autoload module, sleeping until activated

An autoload module, sleeping until activated

The NetBeans IDE faced this problem several years ago (and Eclipse has faced it in release 3 too, as far as I can remember). And the NetBeans Team solved the problem using several intelligent techniques:

  • Many modules are initialized in a declarative way, so no class loading or class instantiation is performed. A plain text file, or an XML file, is parsed and the module is automatically registered into the system.

  • The fastest module to be initialized is the one that is not initialized at all, until needed. They decided to include lazy initialization mechanisms.

  • They decided to minimize initialization time as much as possible, for each module in the system, so as to work cooperatively towards a minimum startup time of all the system. The less operations your module performs at startup time, the better.

Making the joint startup time as small as possible is one of the reasons why there're different types of modules (attending to the way the module is initialized). While building NetBeans modules you'll see these types of modules:

Regular modules

These are modules that are loaded when the whole application is initialized. The initialization time of the module adds up to the whole system initialization time, so you'd better be fast initializing these.

Autoload modules

These "lazy" modules are not loaded unless someone needs them (probably at runtime). This is usually used for those modules that contain libraries. You don't really want to load a library unless you're going to use it, right?

Eager modules

This another interesting type of module. This module is loaded only if all its dependencies are satisfied. This is another intelligent way to minimize startup time. After all, if your module depends on modules A,B,C and those are not available, there's no point in loading your module, right?

2.6. Summary

So in this section we've seen some of the main characteristics of the modules in the NetBeans Platform. If you're interested in any other detail then this link explaining more details about modules may be of interest. To summarize, it's important to remember that:

Public APIs

Your modules may mark some Java packages as public. The classes in these Java packages will be made available to other modules.

Public APIs for NetBeans modules are well documented. Keep a bookmark to the API Javadoc for further reference.

Versions

Versions help you (and the users using your modules) to embrace change. There're versions for public APIs (major release version, specification version) and versions for tagging implementation details (implementation versions).

Dependencies

You must explicitly indicate which modules you need to use. After you've done that you can start using classes in the Public APIs of those modules. Remember not to build cyclic dependencies between modules.

Module isolation

Modules use their own classloaders. You won't be able to load classes from other modules unless you've declared a dependency with that module (and that module publishes the class through its Public API).

Module initialization

Try to do as little as possible during module initialization. After all you want the startup time of your application to be as short as possible, don't you?

2.7. What next?

Now, after all this theory, let's get our feet wet with some NetBeans Platform programming. Let's start by recognizing Scheme files (those that end in ".scm") in our application.


blog comments powered by Disqus