Loosely Coupled Wicket

The next step in the Wicket example I've been looking at, which currently looks as follows...

...is to make the panels "Welcome Page", "About Page", and "Guest Book" loosely coupled from the web application. I.e., each tab in the web application now comes from a different JAR:


There are great benefits to this structure. First of all, the application is now more cleanly organized since each archive (whether it be JAR or WAR) is dedicated to a specific task. You could have a different group of developers working on each JAR, which would be especially useful when the web application becomes very large, where you'll find that each tab (or page) in the application becomes more specialized and distinct from the rest of the application.

Secondly, during development, if there's a compilation problem in the "GuestPanel" JAR, which is more than likely to happen since that panel has a lot of backend code for database access, I can temporarily exclude it from the classpath of the web application (and then file a bug for the backend developer to take a look at the GuestPanel code) and the rest of the application will continue to be deployable... since the classpath is the only connection that the panels have to the web application:


(Having NetBeans projects on the classpath, as opposed to JARs, means that they're going to automatically be cleaned and built together with the web application that contains them. Even the web application's 'Enterprise Beans' node is updated with enterprise beans annotated as such in NetBeans projects on the web application's classpath.) 

So how are the panels loaded into the web application's tabbed panel container? Like this:

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.wicket.extensions.ajax.markup.html.tabs.AjaxTabbedPanel;
import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.Model;
import org.openide.util.Lookup;

public class TabbedPanelPage extends BasePage {

    public TabbedPanelPage() {
        List tabs = new ArrayList();
        Collection panels = Lookup.getDefault().lookupAll(Panel.class);
        for (final Panel panel : panels) {
            final String name = panel.getClass().getSimpleName().replace("Panel", "");
            tabs.add(new AbstractTab(new Model(name)) {
                @Override
                public Panel getPanel(String panelId) {
                    return panel;
                }
            });
        }
        add(new AjaxTabbedPanel("tabs", tabs));
    }
    
}

Take note of the line in bold above. (The naming of the panels could be done more elegantly than extracting from the class name, of course.) Each panel registered in META-INF/services is going to be found by means of Lookup.getDefault, which behaves in the same way as the JDK 6 ServiceLoader class, except that it takes care of the position attribute below, which is critically important in this case, since you want to have control of the order in which the panels are displayed in the tabbed panel container:

import org.apache.wicket.markup.html.panel.Panel;
import org.openide.util.lookup.ServiceProvider;

@ServiceProvider(service = Panel.class, position = 10)
public class WelcomePanel extends Panel {

    public WelcomePanel(String id) {
        super(id);
    }

    public WelcomePanel() {
        super("panel");
    }
    
}

The @ServiceProvider annotation creates a META-INF/services entry, as required by the ServiceLoader mechanism, when the JAR is built. Each panel that should be contributed to the tabbed panel container needs to register itself in the same way as the above.

All of the above also means that contributing a new panel to the application would be as simple as dropping a new JAR into the classpath. Now imagine that your organization is creating multiple web applications and these different web applications have similar needs. For example, several of your web applications need a guest book, right? Well, just create a generic guest book solution (maybe include an embedded database so that the guest book component is completely self-contained) and drop the JAR onto the classpath of multiple different web applications. Though the functionality of the guest book would be the same in each web application, the appearance, i.e., styling, of the guest book would be different in each of the web applications, since the stylesheet is provided by the tabbed panel container, which is a unique component in each web application. That's reuse in the large, i.e., taking Wicket component reusability up a notch—from reuse within applications to across applications.

For the uninitiated, this is not an example of using the NetBeans Platform's module system, but its mechanism for loose coupling, which is built upon the same principle as the JDK 6 ServiceLoader concept. It is all contained within a single JAR file. This is an incredibly simple yet powerful solution that could be used not only within the Wicket framework, of course, i.e., any web framework could make use of this style of loose coupling, though Wicket lends itself to it very easily (and Vaadin probably too, for the same reasons) because it is so strongly Java-centric, i.e., it has no XML files for configuring when or how pages are displayed, which is really liberating, as well as Swing-style component extensibility, which makes for flexible application development.

Comments:

Oh, boy! Using NBP Lookup with Wicket must be the best idea ever! That's a great way to start 2012!

Posted by Eduardo Costa on January 02, 2012 at 08:56 PM PST #

Is there a reason there are two versions of wicket-ioc in the Libraries?
More importantly, does the dependency on org.openide.util.lookup not make it harder to load the project in other IDEs?

Posted by ReinoutS on January 02, 2012 at 09:23 PM PST #

Ah, thanks for picking up the wicket-ioc thing. Didn't need that second one, I forgot that the NetBeans plugin already provided it. And, no, the dependency on org.openide.util.lookup is simply a JAR. You can use that JAR anywhere, just like any other JAR.

Posted by Geertjan on January 02, 2012 at 09:45 PM PST #

Actually, only the 1.4.10 version of wicket-ioc.jar has the 'web' package, it seems to be missing from the 1.5.3 wicket-ioc.jar. That's why I need the 1.4.10 version for this scenario. Anyone can tell me how that would work in 1.5.3?

Posted by Geertjan on January 02, 2012 at 09:54 PM PST #

@ReinoutS: you could use this pattern in any IDE (e.g. if they were structured with Maven). Just beware that the processor for @ServiceProvider must be run, which it will be when you compile somehow with javac (e.g. mvn compile from the command line), but not automatically from Eclipse: https://bugs.eclipse.org/bugs/show_bug.cgi?id=280542

@Geertjan: "naming of the panels could be done more elegantly than extracting from the class name" means that you need to call some method on the Panel, or check its class for some other annotation, etc.; and note that you are eagerly loading all panel implementations even when most are never clicked. Using http://sezpoz.java.net/ rather than ServiceProvider/Lookup makes it easy to define a custom annotation giving the position and label for each panel, while avoiding loading the actual panel class unless and until clicked. (You can do this using NetBeans layer-generating annotations too, it is just a lot more work and larger dependencies.)

Posted by Jesse Glick on January 03, 2012 at 01:22 AM PST #

I wrote a little library that did this a couple years ago. The one thing that didn't quite work with well with Wicket was @ServiceProvider requiring a default constructor.

https://github.com/btilford/joint/tree/master/wicket-joint

Posted by btilford on January 03, 2012 at 02:59 PM PST #

Hi btilford, that requirement is not a problem at all. See the WelcomePanel code above for an example. Just use the default constructor to pass "panel" to the superclass. That's all you need to do.

Posted by Geertjan on January 03, 2012 at 04:25 PM PST #

for the javaee inject libraries, u could use the latest ones. the files names from the repository are: wicketstuff-javaee-inject-1.5.3.jar,wicketstuff-ioc-bundle-1.5.3.jar and cglib-2.2.2.jar

Posted by vinob on January 05, 2012 at 08:36 AM PST #

Yes, but there's no 'web' package in javaee-inject-1.5.3. In other words, there seems to be a big difference, i.e., less classes, in the 1.5.3 version vs the 1.4.10 version.

Posted by Geertjan on January 05, 2012 at 04:03 PM PST #

Hello Geertjan,

I am facing a tough time to know how to control tabs of a JTabbedPane (using Netbeans 7.1). I want to have multiple tabs and make some of them invisible and add items to certain tabs in different times. I would really appreciate it if you provide us with a tutorial or a link that explain such things?

With all the best,
Rosie

Posted by Risie on January 26, 2012 at 10:14 AM PST #

In Java EE environment, I used CDI.

See http://code.google.com/p/jawabot/source/browse/web/src/main/java/org/jboss/jawabot/web/_base/JawaBotTabBarPanel.java

@Inject Instance<ITabBarContrib> tabsInstances;

...

for( ITabBarContrib tabContrib : this.tabsInstances ) {
view.add( new WMC( view.newChildId() )
.add( new BookmarkablePageLink("link", tabContrib.getLinkedPage() )
.add( tabContrib.getLabel("label"))
)
);
}

Posted by Ondra Zizka on October 22, 2013 at 06:28 PM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
12
13
14
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today