Geertjan is a DZone Zone Leader and has posted 458 posts at DZone. You can read more from them at their website. View Full User Profile

Modularity, Groovy, and Shared Filesystems

02.15.2009
| 10508 views |
  • submit to reddit

Let's use the "ModuleInstall.restored" method, shown in the earlier part (where I introduced a modular approach to Groovy application development) to create our Groovy UI:

@Override
public void restored() {
SwingBuilder builder = new SwingBuilder()
Frame gui = builder.frame(size: [290, 100],
title: 'Swinging with Groovy!') {
panel(layout: new FlowLayout()) {
button(text: 'Groovy Button', actionPerformed: {
builder.optionPane(message: 'Indubitably Groovy!').
createDialog(null, 'Zen Message').show()
})
button(text: 'Groovy Quit',
actionPerformed: {System.exit(0)})
}
}
gui.show()
}

The above is a small piece of one of the samples in Paul King's excellent Groovy Tutorial (PDF). The "ModuleInstall.restored" is called when the module is loaded by the application of which it is a part. At that point, the Groovy UI is created. (This is a completely non-standard way of working with the NetBeans Platform, however, it makes sense if you want to stick with the Groovy approach to UI design, rather than adopting the NetBeans Platform's components.)

So now you can run the application and you will see your Groovy UI on top of the NetBeans Runtime Container. But what's the point? Why add the Runtime Container (5 NetBeans modules, which are all that's required for applications to run on the NetBeans Platform) at all?

Remember that a runtime container of this kind is there to handle common infrastructural concerns for you. (Watch this screencast for details.) In this article, we're primarily benefiting from the modularity of the NetBeans Runtime Container, together with the filesystem that all modules within an application can share with each other.

To start with, let's assume that we want to generate our buttons, rather than hardcode them. As a first step, we'll say that we want to have two buttons, which will be created via an iterator:

@Override
public void restored() {
SwingBuilder builder = new SwingBuilder()
Frame gui = builder.frame(size: [290, 100],
title: 'Swinging with Groovy!') {
panel(layout: new FlowLayout()) {
2.times {
button(text: 'Groovy Button', actionPerformed: {
builder.optionPane(message: 'Indubitably Groovy!').
createDialog(null, 'Zen Message').show()
})
}
// button(text: 'Groovy Quit',
// actionPerformed: {System.exit(0)})
}
}
gui.show()
}

So, take note of the buttons above. Instead of hardcoding two, we're iterating through a loop twice and then ending up with our buttons which, for the sake of simplicity, will perform the same action. Now, in the context of a modular application, we might want to provide different distributions of our application to different users. One set of users might need two buttons, while another set of users (e.g., the administrators) might need three buttons.

That's where the shared filesystem comes into play. Each module within the application can contribute to the shared filesystem. So, let's say we're creating a distribution for our administrators. We'd have the same modules as before, plus an additional one that only contains a layer.xml file, which is an XML file registered in the manifest for providing the module's filesystem contributions. In this case, the layer.xml file has these entries:

<folder name="buttons"> 
<file name="One">
<attr name="position" intvalue="100"/>
</file>
<file name="Two">
<attr name="position" intvalue="200"/>
</file>
<file name="Three">
<attr name="position" intvalue="300"/>
</file>
</folder>

For a different set of users, i.e., those who are not administrators, we wouldn't include the module that provides the layer file with the content above. Instead, there'd be a separate module specifically for those users, with content like this:

<folder name="buttons"> 
<file name="One">
<attr name="position" intvalue="100"/>
</file>
<file name="Two">
<attr name="position" intvalue="200"/>
</file>
</folder>

And then, to load the entries from the layer file, and set values in the buttons based on those entries, we'd rewrite the "ModuleInstall.restored" method to the following:

@Override
public void restored() {

FileObject buttons = Repository.getDefault().getDefaultFileSystem().findResource("buttons");

SwingBuilder builder = new SwingBuilder()
Frame gui = builder.frame(size: [290, 100],
title: 'Swinging with Groovy!') {
FileObject[] kids = buttons.getChildren();
panel(layout: new FlowLayout()) {
for (FileObject kid : FileUtil.getOrder(Arrays.asList(kids), true)) {
button(
text: kid.getName(), actionPerformed: {
builder.optionPane(message: 'Indubitably Groovy!').
createDialog(null, 'Zen Message').show() )
}
}
}
gui.show()

}

Here we're simply creating as many buttons as are registered in the shared filesystem and then setting the text on those buttons to the name of the virtual files registered in the shared filesystem. Run the application and, depending on which of the two additional modules is available, you will have two or three buttons:

The order in which the buttons are displayed is set in the layer.xml file, in combination with the call to "FileUtil.getOrder" in the code. All this is made possible by the NetBeans Filesystem API, which is a Java API for parsing the shared filesystem and treating its content as Java objects. Above the example is quite trivial, but one should be able to imagine a very large distributed application, with a variety of customizations provided in different modules, enabling you to assemble distributions for different customers, depending on their needs... without changing a single line of Groovy code. Unnecessary overhead for small Groovy applications, but very handy for very large, distributed, modular Groovy applications.

 

AttachmentSize
fig-1.png6.39 KB
Published at DZone with permission of its author, Geertjan Wielenga.

Comments

Harris Goldstone replied on Sun, 2009/02/15 - 2:01pm

You're implying that Groovy applications can be of an unlimited size? What about Groovy performance? I think it is good for unit tests and maybe demo apps only.

Paul King replied on Sun, 2009/02/15 - 5:23pm in response to: Harris Goldstone

I am not saying performance with Groovy is a non-issue but I would have hundreds of thousands of lines of Groovy code across hundreds of applications in use (many in prod environments) and for only a handful of those has performance been something worth investing time in to even look at. It might be worth your while to upgrade to Java 6 and Groovy 1.6.

Harris Goldstone replied on Sun, 2009/02/15 - 5:36pm in response to: Paul King

So Groovy apps could become really large and benefit from a modular approach?

Carla Brian replied on Tue, 2012/05/15 - 5:58pm

I will definitely master this groovy.  I still need more resources to learn this one. - Instant Tax Solutions Ratings

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.