broschb
broschb

Reputation: 5006

modular/pluggable java web application

I have been trying to build a modular web application.

My requirements are to generate UI dynamically, but have the components of the ui be pluggable. For instance I may have a core set of UI widgets that come out of the box, but if a client wanted to create there own there would be a defined interface for them to implement their own component.

I am using vaadin for my ui framework. And would just like to have the end users provide a jar or war file that contains there ui. I don't want to have to bundle the jar inside my war file however, whatever the end user provides should be deployable as is.

I have looked at using osgi, and have been able to get a framework that allows for dynamic ui from bundles using vaadin, however I am going through dependency hell with other req. Are there other alternatives I haven't considered?

Upvotes: 4

Views: 4771

Answers (3)

broschb
broschb

Reputation: 5006

I got this working the way I wanted using osgi and vaadin. I used this tutorial as a reference. That got me half way to what I needed.

Upvotes: 4

Fabrizio Giudici
Fabrizio Giudici

Reputation: 532

It seems I'm doing a very similar thing. While in the end component frameworks such as OSGi and the NetBeans Platform (that can be uses also server-side) are a workable solution that I've used and I'm using for other projects, they pay their complexity when you use more features that they offer, beyond searching for registered components (e.g. enforcing dependencies checks, version checks, module isolation, etc...).

But for scanning bundled classes there's a simpler solution, based on annotation scanning. In my project with Vaadin I'm creating a user interface referring to "abstract" component names, that have to be matched by actual Java classes that might be provided by a user.

Java classes implementing a component are marked with a custom-made annotation: e.g.

@ViewMetadata(typeUri="component/HtmlTextWithTitle", controlledBy=DefaultHtmlTextWithTitleViewController.class)
public class VaadinHtmlTextWithTitleView extends Label implements HtmlTextWithTitleView

Then I search for annotated classes in the classpath with a ClassScanner:

    final ClassScanner classScanner = new ClassScanner();
    classScanner.addIncludeFilter(new AnnotationTypeFilter(ViewMetadata.class));

    for (final Class<?> viewClass : classScanner.findClasses())
      {
        final ViewMetadata viewMetadata = viewClass.getAnnotation(ViewMetadata.class);
        final String typeUri = viewMetadata.typeUri();
        // etc...
      }

This is my complete implementation for the ClassScanner, implemented on top of Spring:

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;

public class ClassScanner 
  {
    private final String basePackage = "it"; // FIXME

    private final ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);

    @Nonnull
    public final Collection<Class<?>> findClasses() 
      {
        final List<Class<?>> classes = new ArrayList<Class<?>>();

        for (final BeanDefinition candidate : scanner.findCandidateComponents(basePackage)) 
          {
            classes.add(ClassUtils.resolveClassName(candidate.getBeanClassName(), ClassUtils.getDefaultClassLoader()));
          }

        return classes;
      }

    public void addIncludeFilter (final @Nonnull TypeFilter filter)
      {
        scanner.addIncludeFilter(filter);
      }
  }

It's very simple, but effective. Note that, because of how Java ClassLoaders work, you have to specify at least one package to search into. In my example I hardwired the top package "it" (my stuff is "it.tidalwave.*"), it's easy to put this information in a property that can be configured, eventually specifying more than one package.


Another solution could be used by just using two libraries from the NetBeans Platform. I stress the concept that this wouldn't import the whole platform into your project, including the classloader facilities etc., but just using two jar files. Thus it's not invasive. The libraries are org-openide-util.jar and org-openide-util-lookup.jar (I stress again, you can use the plain .jar files instead of the .nbm files that are specific of the NetBeans Platform).

Basically, you'd use the @ServiceProvider annotation. It gets triggered during compilation (with Java 6) and generates a META-INF/services/ description file that will be placed in the classpath. This file is a standard feature of Java (since 1.3, I believe) and can be queried with the standard class ServiceLoader. In this case, you'd use the NetBeans Platform libraries only during compilation, because they are only used for generating META-INF/services. Eventually, the libraries could be used also for better ways to query the registered services, by means of the Lookup class.

There's a design difference between the two solutions. With my custom annotation, I'm discovering classes: then I use them with reflection to instantiate objects. With @ServiceProvider the system automatically instantiates a 'singleton' object from the class. Thus in the former case I register the classes for the objects I want to create, in the second cases I register a factory for creating them. In this case, it seems that the former solution requires one less passage, and that's why I'm using it (normally, I use @ServiceProvider a lot).


Summing up, three solutions have been enumerated:

  1. Use my provided ClassScanner with Spring. Requires Spring in the runtime.
  2. Use @ServiceProvider in code and scan with ServiceLoader. Requires two NetBeans Platform libraries at compile-time, and just the Java Runtime at runtime.
  3. Use @ServiceProvider in code and scan with Lookup. Requires two NetBeans Platform libraries at runtime.

You might also look at answers to this question.

Upvotes: 3

Jon7
Jon7

Reputation: 7215

Well, I've used OSGi for a large modular UI before. We used opensocial gadgets running inside of shindig. OSGi is nice because you can just drop additional gadgets into the framework as bundles and have a listener pick them up and add them to the user's gadget choices. This model extends well to other things like themes. What issues are you having with OSGi and other dependencies?

Upvotes: 1

Related Questions