Showing posts with label CDI. Show all posts
Showing posts with label CDI. Show all posts

Wednesday, December 11, 2013

Modular JSF applications

Everybody heard about portals which combine single web applications to a big one. A portal software works like mashups - content from several sources is picked up in a single service, mostly displayed in a single web page. A portal software also allows to change user settings, such as language or theme, across all single web applications (independent modules) embedded into the portal software. Furthermore, a single sign-on (SSO) is expected and it should work as well. That means, a single login permits an user to access all embedded web applications. It would be interesting to know if there is a simple and lightweight solution in the JEE world to develop modular JSF 2 applications, to gather and to present them in one portal-like web application automatically. Sure, there are OSGi and complex Portals Bridges providing support for JSR-168 or JSR-286 compliant Portlet development. But fortunately, JSF 2 already provides a simple possibility for that "under the hood". With less effort we can build a portal-like software. All what we need are JSF 2 and CDI - de-facto standard DI framework in the Java world.

The topic of this post is not new. You can find some discussions and ideas in the web. I would only mention here two links. The first one is the article "How-to: Modular Java EE Applications with CDI and PrettyFaces" in the ocpsoft's blog. The second one "Modular Web Apps with JSF2" was presented in the JBoss' wiki. The idea is to create JAR files containing single web applications and to supply them with a main WAR file. The WAR file bundles JARs during build process, e.g. via Maven dependencies. That means, JARs are located in the WAR under WEB-INF/lib/. XHTML files in JARs are placed below /META-INF/resources/ and will be fetched automatically by JSF 2. They are available to JSF as if they were in the /webapp/resources/ folder. You can e.g. include facelets from JARs with a quite common ui:include. This works like a charm. To be able to pick up generic informations about every JSF module at runtime, we also need empty CDI's beans.xml in JARs files. They are located as usually below the META-INF folder.

Now let's start with the coding. But first, let's define the project's structure. You can find a complete implemented example on the GitHub. This is just a proof of concept for a lightweight JSF 2 portal-like implementation with demo web apps (written with JSF 2.2). There are 5 sub-projects

jsftoolkit-jar Base framework providing interfaces and utilities for modular JSF applications.
modA-jar First web application (module A) which depends on jsftoolkit-jar.
modB-jar Second web application (module B) which depends on jsftoolkit-jar.
portal-jar Java part of the portal-like software. It also depends on jsftoolkit-jar.
portal-war Web part of the portal-like software. It aggregates all artefacts and is a deployable WAR.

The base framework (jsftoolkit-jar) has interfaces which should be implemented by every single module. The most important are
/**
 * Interface for modular JSF applications. This interface should be implemented by every module (JSF app.)
 * to allow a seamless integration into a "portal" software.
 */
public interface ModuleDescription {

    /**
     * Provides a human readable name of the module.
     *
     * @return String name of the module
     */
    String getName();

    /**
     * Provides a description of the module.
     *
     * @return String description
     */
    String getDescription();

    /**
     * Provides a module specific prefix. This is a folder below the context where all web pages and
     * resources are located.
     *
     * @return String prefix
     */
    String getPrefix();

    /**
     * Provides a name for a logo image, e.g. "images/logo.png" (used in h:graphicImage).
     *
     * @return String logo name
     */
    String getLogoName();

    /**
     * Provides a start (home) URL to be navigated for the module.
     *
     * @return String URL
     */
    String getUrl();
}

/**
 * Any JSF app. implementing this interface can participate in an unified message handling
 * when all keys and messages are merged to a map and available via "msgs" EL, e.g. as #{msgs['mykey']}.
 */
public interface MessagesProvider {

    /**
     * Returns all mesages (key, text) to the module this interface is implemented for.
     *
     * @param  locale current Locale or null
     * @return Map with message keys and message text.
     */
    Map<String, String> getMessages(Locale locale);
}
Possible implementations for the module A look like as follows:
/**
 * Module specific implementation of the {@link ModuleDescription}.
 */
@ApplicationScoped
@Named
public class ModADescription implements ModuleDescription, Serializable {

    @Inject
    private MessagesProxy msgs;

    @Override
    public String getName() {
        return msgs.get("a.modName");
    }

    @Override
    public String getDescription() {
        return msgs.get("a.modDesc");
    }

    @Override
    public String getPrefix() {
        return "moda";
    }

    @Override
    public String getLogoName() {
        return "images/logo.png";
    }

    @Override
    public String getUrl() {
        return "/moda/views/hello.jsf";
    }
}

/**
 * Module specific implementation of the {@link MessagesProvider}.
 */
@ApplicationScoped
@Named
public class ModAMessages implements MessagesProvider, Serializable {

    @Override
    public Map<String, String> getMessages(Locale locale) {
        return MessageUtils.getMessages(locale, "modA");
    }
}
The prefix of this module is moda. That means, web pages and resources are located under the folder META-INF/resources/moda/. That allows to avoid path collisions (identical paths) across all single web applications. The utility class MessageUtils (from the jsftoolkit-jar) is not exposed here. I will only show the class MessagesProxy. The class MessagesProxy is an application scoped bean giving an access to all available messages in a modular JSF web application. It can be used in Java as well as in XHTML because it implements the Map interface. All found available implementations of the MessagesProvider interface are injected by CDI automatically at runtime. We make use of Instance<MessagesProvider>.
@ApplicationScoped
@Named(value = "msgs")
public class MessagesProxy implements Map<String, String>, Serializable {

    @Inject
    private UserSettingsData userSettingsData;

    @Any
    @Inject
    private Instance<MessagesProvider> messagesProviders;

    /** all cached locale specific messages */
    private Map<Locale, Map<String, String>> msgs = new ConcurrentHashMap<Locale, Map<String, String>>();

    @Override
    public String get(Object key) {
        if (key == null) {
            return null;
        }

        Locale locale = userSettingsData.getLocale();
        Map<String, String> messages = msgs.get(locale);

        if (messages == null) {
            // no messages to current locale are available yet
            messages = new HashMap<String, String>();
            msgs.put(locale, messages);

            // load messages from JSF impl. first
            messages.putAll(MessageUtils.getMessages(locale, MessageUtils.FACES_MESSAGES));

            // load messages from providers in JARs
            for (MessagesProvider messagesProvider : messagesProviders) {
                messages.putAll(messagesProvider.getMessages(locale));
            }
        }

        return messages.get(key);
    }
    
    public String getText(String key) {
        return this.get(key);
    }
    
    public String getText(String key, Object... params) {
        String text = this.get(key);

        if ((text != null) && (params != null)) {
            text = MessageFormat.format(text, params);
        }

        return text;
    }

    public FacesMessage getMessage(FacesMessage.Severity severity, String key, Object... params) {
        String summary = this.get(key);
        String detail = this.get(key + "_detail");

        if ((summary != null) && (params != null)) {
            summary = MessageFormat.format(summary, params);
        }

        if ((detail != null) && (params != null)) {
            detail = MessageFormat.format(detail, params);
        }

        if (summary != null) {
            return new FacesMessage(severity, summary, ((detail != null) ? detail : StringUtils.EMPTY));
        }

        return new FacesMessage(severity, "???" + key + "???", ((detail != null) ? detail : StringUtils.EMPTY));
    }
    
    /////////////////////////////////////////////////////////
    // java.util.Map interface
    /////////////////////////////////////////////////////////

    public int size() {
        throw new UnsupportedOperationException();
    }

    // other methods ...
}
Well. But where the instances of the ModuleDescription are picked up? The logic is in the portal-jar. I use the same mechanism with CDI Instance. CDI will find out all available implementations of the ModuleDescription for us.
/**
 * Collects all available JSF modules.
 */
@ApplicationScoped
@Named
public class PortalModulesFinder implements ModulesFinder {

    @Any
    @Inject
    private Instance<ModuleDescription> moduleDescriptions;

    @Inject
    private MessagesProxy msgs;

    private List<FluidGridItem> modules;

    @Override
    public List<FluidGridItem> getModules() {
        if (modules != null) {
            return modules;
        }

        modules = new ArrayList<FluidGridItem>();

        for (ModuleDescription moduleDescription : moduleDescriptions) {
            modules.add(new FluidGridItem(moduleDescription));
        }

        // sort modules by names alphabetically
        Collections.sort(modules, ModuleDescriptionComparator.getInstance());

        return modules;
    }
}
We are equipped now for creating dynamic tiles in UI which represent entry points to the corresponding web modules.
<pe:fluidGrid id="fluidGrid" value="#{portalModulesFinder.modules}" var="modDesc"
              fitWidth="true" hasImages="true">
    <pe:fluidGridItem styleClass="ui-widget-header">
        <h:panelGrid columns="2" styleClass="modGridEntry" columnClasses="modLogo,modTxt">
            <p:commandLink process="@this" action="#{navigationContext.goToPortlet(modDesc)}">
                <h:graphicImage library="#{modDesc.prefix}" name="#{modDesc.logoName}"/>
            </p:commandLink>

            <h:panelGroup>
                <p:commandLink process="@this" action="#{navigationContext.goToPortlet(modDesc)}">
                    <h:outputText value="#{modDesc.name}" styleClass="linkToPortlet"/>
                </p:commandLink>

                <p/>
                <h:outputText value="#{modDesc.description}"/>
            </h:panelGroup>
        </h:panelGrid>
    </pe:fluidGridItem>
</pe:fluidGrid>
Tiles were created by the component pe:fluidGrid from PrimeFaces Extensions. They are responsive, means they get rearranged when resizing the browser window. The next picture demonstrates how the portal web app looks like after the starting up. It shows two modular demo apps found in the classpath. Every modular web app is displayed as a tile containing a logo, name and short description. Logos and names are clickable. A click redirects to the corresponsing single web app.

As you can see, on the portal's homepage you can switch the current language and the theme. The second picture shows what happens if the user clicks on the module A. The web app for the module A is shown. You can see the Back to Portal button, so a back navigation to the portal's homepage is possible.


Two notes at the end:
  1. It is possible to run every module as a standalone web application. We can check at runtime (again by means of CDI) if the module is within the "portal" or not and use different master templates. A hint is here.
  2. I didn't implement a login screen, but there is no issue with single sign-on because we only have one big application (one WAR). Everything is delivered there.

Monday, February 18, 2013

Eager CDI beans

Everybody knows eager managed beans in JSF 2. @ManagedBean has an eager attribute. If eager="true" and the scope is application, then this bean must be created when the application starts and not during the first reference to the bean. This is a nice feature when you want to load application scoped data (e.g. some select items for menus) during application startup in order to increase the performance at runtime.
@ManagedBean(eager=true)
@ApplicationScoped
public class GlobalBean {
    ...
}
@ManagedBean annotation will be deprecated with JSF 2.2. It is highly recommended to use CDI (context dependency injection) beans in JEE environment. But what is the equivalent to the eager managed beans in CDI? Well, CDI is flexible, you can write portable CDI extensions. I asked Thomas Andraschko how to do this. Thomas is a CDI expert, co-owner of PrimeFaces Extensions and the committer in OpenWebBeans (OWB) project. His tip was to implement such extension as follows
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE})
public @interface Eager
{
}
package mydomain.mypackage;

import java.util.ArrayList;
import java.util.List;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterDeploymentValidation;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ProcessBean;

public class EagerExtension implements Extension {
    private List<Bean<?>> eagerBeansList = new ArrayList<Bean<?>>();

    public <T> void collect(@Observes ProcessBean<T> event) {
        if (event.getAnnotated().isAnnotationPresent(Eager.class)
            && event.getAnnotated().isAnnotationPresent(ApplicationScoped.class)) {
            eagerBeansList.add(event.getBean());
        }
    }

    public void load(@Observes AfterDeploymentValidation event, BeanManager beanManager) {
        for (Bean<?> bean : eagerBeansList) {
            // note: toString() is important to instantiate the bean
            beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean)).toString();
        }
    }
}
The extensions should be registered in a file META-INF/services/javax.enterprise.inject.spi.Extension. The file has only one line with a fully qualified path to the EagerExtension class, e.g. mydomain.mypackage.EagerExtension.

Using is simple. Assume, we have an application scoped LayoutController CDI bean which is responsible for the entire layout configration. We can annotate it with @Eager and speed up the layout creation.
@ApplicationScoped
@Eager
@Named
public class LayoutController implements Serializable {
    private LayoutOptions layoutOptions;

    @PostConstruct
    protected void initialize() {
        layoutOptions = new LayoutOptions();

        LayoutOptions panes = new LayoutOptions();
        panes.addOption("slidable", false);
        panes.addOption("spacing", 6);
        layoutOptions.setPanesOptions(panes);

        ...
    }

    public LayoutOptions getLayoutOptions() {
        return layoutOptions;
    }
}
Have fun with CDI!

Sunday, September 16, 2012

Configure timeout for CDI conversations

CDI conversation scope is a nice feature when developing JSF applications. Imagine you have large data tables which take a long time to be loaded. You normally don't want to place the loaded data in session scoped beans by reason of high memory consumption. And you can't place the loaded data in view scoped beans because you wouldn't like always to reload data again if user leave and enter the same view. It would be nice only to keep data if user enters the same page within a certain time interval and reload them again if the bean was not accessed within this time interval. This can be achieved by conversation scoped bean with timeout. We will deal with MyFaces CODI (CDI Extensions) and see how to set a custom timeout for beans annotated with @ConversationScoped. The default timeout is 30 min. what is too long for our example. We will configure it for 1 min. The first step is to extend CODI's ConversationConfig and overwrite the method getConversationTimeoutInMinutes(). Let's write a class AlternativeConversationConfig.
package controller.cdi;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.Specializes;
import org.apache.myfaces.extensions.cdi.core.api.config.ConfigEntry;
import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.config.ConversationConfig;

@ApplicationScoped
@Alternative
@Specializes
public class AlternativeConversationConfig extends ConversationConfig {

    @ConfigEntry
    public int getConversationTimeoutInMinutes() {
        return 1;
    }
}
The important thing is the annotation @Specializes which allows to inject AlternativeConversationConfig instead of ConversationConfig at every existing places. The second step is a proper entry in beans.xml to use (activate) our class on all injection points for ConversationConfig.
<alternatives>
    <class>controller.cdi.AlternativeConversationConfig</class>
</alternatives>
Server's log output during startup contains these lines now
config implementation:
controller.cdi.AlternativeConversationConfig$Proxy$_$$_WeldClientProxy
config implementation: controller.cdi.AlternativeConversationConfig
   method: getConversationTimeoutInMinutes
   value: 1
To check if everything is ok, we can write a conversation scoped bean and use it in facelets.
import java.io.Serializable;
import javax.faces.event.ActionEvent;
import javax.inject.Named;
import org.apache.myfaces.extensions.cdi.core.api.scope.conversation.ConversationScoped;

@Named
@ConversationScoped
public class CdiBeanConversationScoped implements Serializable {
 
    private int counter;
 
    public int getCounter() {
        return counter;
    }
 
    public void increment(ActionEvent e) {
        counter++;
    }
}
<h:outputText id="counter" value="Conversation scoped counter: #{cdiBeanConversationScoped.counter}"/>
   
<p:commandButton value="Increment counter" process="@this" update="counter"
                                         actionListener="#{cdiBeanConversationScoped.increment}"/>
The counter will expire after 1 min. if no access to the bean happens within this time interval. Simple push the button to increment the counter, wait longer than 1 min. and increment it again. You will see that counter was reseted.

Saturday, July 21, 2012

Event-based communication in JSF. New-school approach.

In the last post, we learnt event-based communication on basis of Observer / Event Listener and Mediator patterns. Due to their shortcomings I would like to show more efficient ways for event-based communication. We will start with Google Guava EventBus and end up with CDI (Contexts and Dependency Injection for the Java EE platform).

Guava EventBus
Google Guava library has an useful package eventbus. The class EventBus allows publish-subscribe-style communication between components without requiring the components to explicitly register with one another. Because we develop web applications, we should encapsulate an instance of this class in a scoped bean. Let's write EventBusProvider bean.
public class EventBusProvider implements Serializable {

    private EventBus eventBus = new EventBus("scopedEventBus");

    public static EventBus getEventBus() {
        // access EventBusProvider bean
        ELContext elContext = FacesContext.getCurrentInstance().getELContext();
        EventBusProvider eventBusProvider =
            (EventBusProvider) elContext.getELResolver().getValue(elContext, null, "eventBusProvider");

        return eventBusProvider.eventBus;
    }
}
I would like to demonstrate all main features of Guava EventBus in only one example. Let's write the following event hierarchy:
public class SettingsChangeEvent {

}

public class LocaleChangeEvent extends SettingsChangeEvent {

    public LocaleChangeEvent(Object newLocale) {
        ...
    }
}

public class TimeZoneChangeEvent extends SettingsChangeEvent {

    public TimeZoneChangeEvent(Object newTimeZone) {
        ...
    }
}
The next steps are straightforward. To receive events, an object (bean) should expose a public method, annotated with @Subscribe annotation, which accepts a single argument with desired event type. The object needs to pass itself to the register() method of EventBus instance. Let's create two beans:
public MyBean1 implements Serializable {

    @PostConstruct
    public void initialize() throws Exception {
        EventBusProvider.getEventBus().register(this);
    }

    @Subscribe
    public void handleLocaleChange(LocaleChangeEvent event) {
        // do something
    }

    @Subscribe
    public void handleTimeZoneChange(TimeZoneChangeEvent event) {
        // do something
    }
}

public MyBean2 implements Serializable {

    @PostConstruct
    public void initialize() throws Exception {
        EventBusProvider.getEventBus().register(this);
    }

    @Subscribe
    public void handleSettingsChange(SettingsChangeEvent event) {
        // do something
    }
}
To post an event, simple provide the event object to the post() method of EventBus instance. The EventBus instance will determine the type of event and route it to all registered listeners.
public class UserSettingsForm implements Serializable {

    private boolean changed;

    public void localeChangeListener(ValueChangeEvent e) {
        changed = true;        
 
        // notify subscribers
        EventBusProvider.getEventBus().post(new LocaleChangeEvent(e.getNewValue()));
    }

    public void timeZoneChangeListener(ValueChangeEvent e) {
        changed = true;        
 
        // notify subscribers
        EventBusProvider.getEventBus().post(new TimeZoneChangeEvent(e.getNewValue()));
    }

    public String saveUserSettings() {
        ...

        if (changed) {
            // notify subscribers
            EventBusProvider.getEventBus().post(new SettingsChangeEvent());

            return "home";
        }
    }
}
Guava EventBus allows to create any listener that is reacting for many different events - just annotate many methods with @Subscribe and that's all. Listeners can leverage existing events hierarchy. So if Listener A is waiting for events A, and event A has a subclass named B, this listener will receive both type of events: A and B. In our example, we posted three events: SettingsChangeEvent, LocaleChangeEvent and TimeZoneChangeEvent. The handleLocaleChange() method in the MyBean1 will only receive LocaleChangeEvent. The method handleTimeZoneChange() will only receive TimeZoneChangeEvent. But look at the method handleSettingsChange() in the MyBean2. It will receive all three events!

As you may see, a manually registration is still needed (EventBusProvider.getEventBus().register(this)) and the problem with scoped beans, I mentioned in the previous post, is still existing. We should be aware of scoping of EventBusProvider and scoping of publish / subscriber beans. But as you may also see, we have some improvements in comparison to the Mediator pattern: no special interfaces are needed, the subscriber's method names are not fix defined, multi-listeners are possible too, no effort to manage registered instances, etc. Last but not least - asynchronous AsyncEventBus and subscription to DeadEvent (for listening for any events which were dispatched without listeners - handy for debugging). Follow this guide please to convert an existing EventListener-based system to EventBus-based one.

CDI (Contexts and Dependency Injection)
Every JEE 6 compliant application server supports CDI (the JSR-299 specification). It defines a set of complementary services that help improve the structure of application code. The best-known implementations of CDI are OpenWebBeans and JBoss Weld. Events in CDI allow beans to interact with no dependency at all. Event producers raise events that are delivered to event observers by the container. This basic schema might sound like the familiar Observer / Observable pattern, but there are a couple of benefits.
  • Event producers and event observers are decoupled from each other.
  • Observers can specify a combination of "selectors" to narrow the set of event notifications they will receive.
  • Observers can be notified immediately or with delaying until the end of the current transaction.
  • No headache with scoping by conditional observer methods (remember problem of scoped beans and Mediator / EventBus?).
Conditional observer methods allow to obtain the bean instance that already exists, only if the scope of the bean that declares the observer method is currently active, without creating a new bean instance. If the observer method is not conditional, the corresponding bean will be always created. You are flexible!

CDI event mechanism is the best approach for the event-based communication in my opinion. The subject is complex. Let's only show the basic features. An observer method is a method of a bean with a parameter annotated @Observes.
public MyBean implements Serializable {

    public void onLocaleChangeEvent(@Observes Locale locale) {
        ...
    }
}
The event parameter may also specify qualifiers if the observer method is only interested in qualified events - these are events which have those qualifiers.
public void onLocaleChangeEvent(@Observes @Updated Locale locale) {
    ...
}
An event qualifier is just a normal qualifier, defined using @Qualifier. Here is an example:
@Qualifier
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
public @interface Updated {}
Event producers fire events using an instance of the parametrized Event interface. An instance of this interface is obtained by injection. A producer raises events by calling the fire() method of the Event interface, passing the event object.
public class UserSettingsForm implements Serializable {

    @Inject @Any Event<Locale> localeEvent;

    public void localeChangeListener(ValueChangeEvent e) {
        // notify all observers
        localeEvent.fire((Locale)e.getNewValue());
    }
}
The container calls all observer methods, passing the event object as the value of the event parameter. If any observer method throws an exception, the container stops calling observer methods, and the exception is re-thrown by the fire() method. @Any annotation above acts as an alias for any and all qualifiers. You see, no manually registration of observers is necessary. Easy? Specifying other qualifiers at the injection point is simple as well:
// this will raise events to observers having parameter @Observes @Updated Locale
@Inject @Updated Event<Locale> localeEvent;
You can also have multiple event qualifiers. The event is delivered to every observer method that has an event parameter to which the event object is assignable, and does not have any event qualifier except the event qualifiers matching those specified at the Event injection point. The observer method may have additional parameters, which are injection points. Example:
public void onLocaleChangeEvent(@Observes @Updated Locale locale, User user) {
    ...
}
What is about a specifying the qualifier dynamically? CDI allows to obtain a proper qualifier instance by means of AnnotationLiteral. This way, we can pass the qualifier to the select() method of Event. Example:
public class DocumentController implements Serializable {

    Document document;

    @Inject @Updated @Deleted Event<Document> documentEvent;

    public void updateDocument() {
        ...
        // notify observers with @Updated annotation
        documentEvent.select(new AnnotationLiteral<Updated>(){}).fire(document);
    }

    public void deleteDocument() {
        ...
        // notify observers with @Deleted annotation
        documentEvent.select(new AnnotationLiteral<Deleted>(){}).fire(document);
    }
}
Let's talk about "conditional observer methods". By default, if there is no instance of an observer in the current context, the container will instantiate the observer in order to deliver an event to it. This behaviour isn't always desirable. We may want to deliver events only to instances of the observer that already exist in the current context. A conditional observer is specified by adding receive = IF_EXISTS to the @Observes annotation.
public void onLocaleChangeEvent(@Observes(receive = IF_EXISTS) @Updated Locale locale) {
    ...
}
Read more about Scopes and Contexts here. In this short post we can not talk more about further features like "event qualifiers with members" and "transactional observers". I would like to encourage everybody to start learn CDI. Have much fun!

Edit: Found today a great CDI specific possibility how to obtain a list of listeners for an event. Solution: Inject BeanManager (allows to interact directly with the container) and call
<T> java.util.Set<ObserverMethod<? super T>> resolveObserverMethods(T event, java.lang.annotation.Annotation... qualifiers)