Showing posts with label Spring. Show all posts
Showing posts with label Spring. Show all posts

Friday, April 3, 2015

Caching of web content with Spring's cache manager


I this post, I would like to show basics how to cache and manage the caching of web content with Spring's CacheManager, @Cacheable and JMX annotations. Imagine a web shop which fetches some content, such as header, footer, teasers, main navigation, from a remote WCMS (Web Content Management System). The fetching may e.g. happen via a REST service. Some content is rarely updated, so that it makes sense to cache it in the web application due to performance reasons.

Getting Started

First, we need a cache provider. A good cache provider would be EhCache. You need to add the EhCache as dependency to your project. You also need to configure ehcache.xml which describes, among other things, the cache name(s), where and how long the cached content is stored. Please refer to the documentation to learn how the ehcache.xml looks like. The central class of the EhCache is the net.sf.ehcache.CacheManager. With help of this class you can add or remove any objects to / from the cache and much more programmatically. Objects can be cached in memory, on the disk or somewhere else.

The Spring framework provides a CacheManager backed by the EhCache - org.springframework.cache.CacheManager. It also provides the @Cacheable annotation. From the documentation: "As the name implies, @Cacheable is used to demarcate methods that are cacheable - that is, methods for whom the result is stored into the cache so on subsequent invocations (with the same arguments), the value in the cache is returned without having to actually execute the method. In its simplest form, the annotation declaration requires the name of the cache associated with the annotated method". We will use the JMX annotations as well. These are Spring's annotations @ManagedResource and @ManagedOperation. Why do we need those? We need them to be able to clear cache(s) via an JMX console. Why? Well, e.g. the underlying data have been changed, but the cache is not expired yet. The outdated data will be still read from the cache and not from the native source. The beans annotated with @ManagedResource will be exposed as JMX beans and methods annotated by @ManagedOperation can be executed via an JMX console. I recommend to use JMiniX as a simple JMX entry point. Embedding JMiniX in a webapp is done simply by declaring a servlet. Parametrized methods are supported as well, so that you can even input some real values for method's parameters and trigger the execution with these values.

How to do it...

Now we are ready to develop the first code. We need a service which communicates with a remote backend in order to fetch various contents from the WCMS. Let's show exemplary a basic code with one method fetchMainNavigation(). This method fetches the structure of the main navigation menu and converts the structure to a DTO object NavigationContainerDTO (model class for the menu). The whole business and technical logic is resided in the bean MainNavigationHandler. This logic is not important for this blog post. The method fetchMainNavigation() expects two parameters: locale (e.g. English or German) and variant (e.g. B2C or B2B shop).
@Component
public class WCMSServiceImpl extends BaseService implements WCMSService {
 
    // injection of Spring's CacheManager is needed for @Cacheable
    @Autowired
    private CacheManager cacheManager;
 
    @Autowired
    private MainNavigationHandler mainNavigationHandler;
 
    ...
 
    @Override
    @Cacheable(value = "wcms-mainnavigation",
                        key = "T(somepackage.wcms.WCMSBaseHandler).cacheKey(#root.methodName, #root.args[0], #root.args[1])")
    public NavigationContainerDTO fetchMainNavigation(Locale lang, String variant) {
        Object[] params = new Object[0];
        if (lang != null) {
            params = ArrayUtils.add(params, lang);
        }
        if (variant != null) {
            params = ArrayUtils.add(params, variant);
        }
 
        return mainNavigationHandler.get("fetchMainNavigation", params);
    }
}
The method is annotated with the Spring's annotation @Cacheable. That means, the returned object NavigationContainerDTO will be cached if it was not yet available in the cache. The next fetching will return the object from the cache until the cache gets expired. The caching occurs according to the settings in the ehcache.xml. Spring's CacheManager finds the EhCache provider automatically in the classpath. The value attribute in @Cacheable points to the cache name. The key attribute points to the key in the cache the object can be accessed by. Since caches are essentially key-value stores, each invocation of a cached method needs to be translated into a suitable key for the cache access. In a simple case, the key can be any static string. In the example, we need a dynamic key because the method has two parameters: locale and variant. Fortunately, Spring supports dynamic keys with SpEL expression (Spring EL expression). See the table "Cache SpEL available metadata" for more details. You can invoke any static method generating the key. Our expression T(somepackage.wcms.WCMSBaseHandler).cacheKey(#root.methodName, #root.args[0], #root.args[1]) means we call the static method cacheKey in the class WCMSBaseHandler with three parameters: the method name, first and second arguments (locale and variant respectively). This is our key generator.
public static String cacheKey(String method, Object... params) {
    StringBuilder sb = new StringBuilder();
    sb.append(method);

    if (params != null && params.length > 0) {
        for (Object param : params) {
            if (param != null) {
                sb.append("-");
                sb.append(param.toString());
            }
        }
    }

    return sb.toString();
}
Let's show how the handler class MainNavigationHandler looks like. This is just a simplified example from a real project.
@Component
@ManagedResource(objectName = "bean:name=WCMS-MainNavigation",
                                description = "Manages WCMS-Cache for the Main-Navigation")
public class MainNavigationHandler extends WCMSBaseHandler<NavigationContainerDTO, Navigation> {

    @Override
    NavigationContainerDTO retrieve(Objects... params) {
        // the logic for content retrieving and DTOs mapping is placed here
        ...
    }
 
    @ManagedOperation(description = "Delete WCMS-Cache")
    public void clearCache() {
        Cache cache = cacheManager.getCache("wcms-mainnavigation");
        if (cache != null) {
            cache.clear();
        }
    } 
}
The CacheManager is also available here thanks to the following injection in the WCMSBaseHandler.
@Autowired
private CacheManager cacheManager;
@ManagedResource is the Spring's JMX annotation, so that the beans are exported as JMX MBean and become visible in the JMX console. The method to be exported should be annotated with @ManagedOperation. This is the methode clearCache() which removes all content for the main navigation from the cache. "All content" means an object of type NavigationContainerDTO. The developed WCMS service can be now injected into a bean on the front-end side. I already blogged about how to build a multi-level menu with plain HTML and shown the code. This is exactly the main navigation from this service.

There is more...

The scanning of JMX annotations should be configured in a Spring's XML configuration file.
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
    <property name="server" ref="mbeanServer"/>
    <property name="assembler" ref="assembler"/>
    <property name="namingStrategy" ref="namingStrategy"/>
    <property name="autodetect" value="true"/>
</bean>
The JMX console of the JMiniX is reachable under the http(s)://:/mct/webshop/admin/jmx/ A click on the execute button of the clearCache() method triggers the cache clearing.

Monday, July 16, 2012

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

Web applications written in JSF consist of beans which interact among each other. Communication between beans is one of the main design patterns when developing a web application. Sometimes, one bean needs to send events to other beans to inform them about some changes or whatever else. We can normally inject a managed or Spring bean into the property of another bean, so that another bean can notify injected bean directly. Injection is good, but it was not introduced for the purpose of communication. It is far away from a dynamic loosely coupled system where each bean doesn't know about other beans. In a loosely coupled system we need a good event-based communication mechanism. This post will cover two design patterns: Observer / Event Listener and Mediator pattern. These patters are widely used in many web applications nowadays, but they have disadvantages. The system is not really loosely-coupled with them. There are much better and modern approaches. Therefore, I wrote "Old-school approach" in the post name. New-school approaches will be disclosed in the next post.

Observer / Event Listener
We will start with the Observer (also called as Event Listener) pattern. An object, called the subject or observable object, maintains a list of its dependents, called observers, and notifies them automatically of any state changes. In Java there are classes java.util.Observer and java.util.Observable which help to implement this pattern. Other related constructs for event-based communication by means of this pattern are the class java.util.EventObject and the interface java.util.EventListener. Let's start coding. Assume we have a I18N web application and user can choose a language (Locale) somewhere in user settings. Assume we have a bean called UserSettingsForm which is responsible for user settings. Some session scoped beans can keep I18N text / messages, so that when user changes the current languages, a reset of previous text / messages in the last selected language is needed. Firstly, we need a LocaleChangeEvent.
public class LocaleChangeEvent extends EventObject {
    
    Locale locale;

    public LocaleChangeEvent(Object source, Locale locale) {
        super(source);
        this.locale = locale;
    }

    public Locale getLocale() {
        return locale;
    }
}
Secondly, we need an interface LocaleChangeListener.
public interface LocaleChangeListener extends EventListener {
    
    void processLocaleChange(LocaleChangeEvent event);
}
Our UserSettingsForm can manage now instances of type LocaleChangeListener by registring and notifying them.
@ManagedBean
@SessionScoped
public class UserSettingsForm implements Serializable {

    private Locale selectedLocale;
    private List<SelectItem> locales;
    private List<LocaleChangeListener> localeChangeListeners = new ArrayList<LocaleChangeListener>();

    public void addLocaleChangeListener(LocaleChangeListener listener) {
        localeChangeListeners.add(listener);
    }

    public void localChangeListener(ValueChangeEvent e) {
        ...
        // notify listeners
        LocaleChangeEvent lce = new LocaleChangeEvent(this, this.selectedLocale);
        for (LocaleChangeListener lcl : localeChangeListeners) {
            lcl.processLocaleChange(lce);
        }
    }
    ...
}
The method localChangeListener() is an JSF ValueChangeListener and can be applied e.g. in h:selectOneMenu. Every bean which implements LocaleChangeListener should be registered by UserSettingsForm in order to be notified by locale changes.
@ManagedBean
@SessionScoped
public MyBean implements LocaleChangeListener, Serializable {

    // UserSettingsForm can be injected e.g. via @ManagedProperty annotation or via Spring facility
    private UserSettingsForm userSettingsForm;

    @PostConstruct
    public void initialize() {
        userSettingsForm.addLocaleChangeListener(this);
    }

    public void processLocaleChange(LocaleChangeEvent event) {
        // reset something related to I18N data
        ...
    }
}
In terms of Observer pattern the UserSettingsForm is Observable and instances of LocaleChangeListener (like MyBean) are Observers. The discussed pattern comes with some important issues that you need to be aware of. Beans are tightly coupled. There are a lot of manually work to regsiter beans. Beans have to implement defined interfaces. If you have a bean informed by 100 semantic different changes, it has to implement 100 interfaces. It is not possible to notify a subset of registered listeners - always all listeners get notified even if they don't need to be notified. Last but not least - memory management issue. Martin Fowler wrote "Assume we have some screens observing some domain objects. Once we close a screen we want it to be deleted, but the domain objects actually carry a reference to the screen though the observer relationship. In a memory-managed environment long lived domain objects can hold onto a lot of zombie screens, resulting in a significant memory leak."

Mediator
The Mediator pattern improves the event-based communication in comparison to the Observer / Event Listener pattern. With the mediator pattern, communication between objects is encapsulated with a mediator object. Objects no longer communicate directly with each other, but instead communicate through the mediator. This reduces dependencies between communicating objects. We will see how it works for JSF-Spring beans (in examples above were standard managed beans). We will implement a Mediator class to manage the communication between scoped beans. It is important to understand that a bean only can notify another beans having broader scope(s). A view scoped bean can notify view, session and application scoped beans, but not request scoped beans with the narrower scope. Follow this rule to avoid troubles. This is a nature of scoped bean - you might remember that you can always inject a bean of wider scope into a bean of narrower scope, but not vice versa. To start working with Mediator we will introduce two interfaces MediatorEvent, MediatorListener and the centric class Mediator.
public interface MediatorEvent {
    ...
}

public interface MediatorListener {

    public void listenToEvent(MediatorEvent event);
}

public class Mediator implements Serializable {

    private Collection<MediatorListener> collaborators = new HashSet<MediatorListener>();

    public static Mediator getCurrentInstance() {
        // access Mediator bean by JSF-Spring facility
        return ContextLoader.getCurrentWebApplicationContext().getBean("mediator");
    }

    public void fireEvent(MediatorEvent event) {
        for (MediatorListener mediatorListener : collaborators) {
            mediatorListener.listenToEvent(event);
        }
    }

    public void addCollaborator(MediatorListener collaborator) {
        collaborators.add(collaborator);
    }

    public void removeCollaborator(MediatorListener collaborator) {
        collaborators.remove(collaborator);
    }
}
Mediator is a scoped bean which can register and notify collaborators. Collaborators register themself by Mediator. In Spring, a bean can implement the interface InitializingBean, so that the method afterPropertiesSet() will be called automatically after the bean's instantiation. This is similar to @PostConstruct. afterPropertiesSet() is a right place for such bean to register itself by Mediator. The bean should also implement MediatorListener in order to be notified (see listenToEvent()).
public MyBean implements MediatorListener, InitializingBean, Serializable {

    public void afterPropertiesSet() throws Exception {
        ...
        Mediator.getCurrentInstance().addCollaborator(this);
    }

    @Override
    public void listenToEvent(MediatorEvent event) {
        if (event instanceof LocaleChangeEvent) {
            // do something
        }
    }
}
We will use the same scenario with UserSettingsForm and locale changing. Beans registered by Mediator will be notified by fireEvent().
public class LocaleChangeEvent implements MediatorEvent {
    ...
}

public class UserSettingsForm implements Serializable {

    private Locale selectedLocale;
    private List<SelectItem> locales;

    public void localChangeListener(ValueChangeEvent e) {
        ...
        // notify listeners
        Mediator.getCurrentInstance().fireEvent(new LocaleChangeEvent(this, this.selectedLocale));
    }
    ...
}
Mediator pattern offers better coupling between beans, but they are still coupled with mediator. Further disadvantages: It is still necessary to register beans manually - see extra code Mediator.getCurrentInstance().addCollaborator(this). Every bean should still implement at least one MediatorListener and that brings another constraint - listenToEvent(). Every bean should implement this interface method! The probably biggest shortcoming of Mediator pattern in JSF is that it is a scoped bean. A view scoped Mediator would only work smoothly with view scoped beans. Registered view scoped beans are being removed automatically when the view scoped Mediator gets destroyed. Other scenarios can cause memory leaks or several issues. For instance, request scoped beans, registered by a view scoped Mediator, should be removed manually by calling removeCollaborator() (easy to forget). Session scoped beans should be registered by a session scoped Mediator, otherwise they will not be notified after destroying the view scoped Mediator. Etc, etc.

In the fact, the Mediator pattern is only one step better than a regular Observer / Event Listener concept. There are more flexible approaches where *any method* can catch thrown event and not only fix specified, like listenToEvent(). In the next post, we will see easy and unobtrusive ways how to catch multiply events by only one method and other advices.

Tuesday, October 19, 2010

JSF Ajax redirect after session timeout

JSF 2 has a facility to be able to do Ajax redirects. The specification says - an element <redirect url="redirect url"/> found in the response causes a redirect to the URL "redirect url". JSF component libraries often write other "redirect" elements into response, but the end result is the same - a redirect happens. It works great in case of JSF Ajax redirects.
A web application normally has a security filter - an own solution or from Spring framework or something else. Such security filter does a redirect if user is not authenticated to use requested web resources. This is not an JSF redirect. The filter doesn't pass current request to the FacesServlet at all. Here is an example:
public class SecurityFilter implements Filter {

    private Authenticator authenticator;
    private String loginPage;
    
    public void init(FilterConfig config) throws ServletException {
        ...
        // create an Authenticator  
        authenticator = AuthenticatorFactory.createAuthenticator(config);
        loginPage = getLoginPage(config);
        ...
    }

    public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {
        HttpServletRequest hReq = (HttpServletRequest) request;
        HttpServletResponse hRes = (HttpServletResponse) response;
        ...
        // get user roles
        Collection roles = ... 
        // get principal
        Principal principal = hReq.getUserPrincipal();
        
        if (!roles.isEmpty() && principal == null) {
            // user needs to be authenticated
            boolean bRet = authenticator.showLogin(hReq, hRes);
            if (!bRet) {
         // redirect the a login page or error sending
                ...
            }
        }
        ...
    }
}
Problem: indeed an Ajax request is redirected to a specified page after session timeout, but the response can not be proper treated on the client-side. We have a quite regular redirect in this case and the specified page in the response. Client-side Ajax code doesn't expect the entire page coming back.
A possible solution would be to pass "not authenticated" request through FacesServlet. We need to write an Authenticator class which stores the page URL we want to be redirected to in the request scope.
public class FormAuthenticator {
    ...

    public boolean showLogin(HttpServletRequest request, 
        HttpServletResponse response, String loginPage) throws IOException {
        ...
        request.setAttribute("web.secfilter.authenticator.showLogin", loginPage);
 
        return true;
    }
}
The method showLogin returns true in order to avoid a redirect by filter. Let us execute the redirect by JSF! The call gets now a third parameter "loginPage".
authenticator.showLogin(hReq, hRes, loginPage);
If you use Spring security you should have an entry point in your XML configuration file like this one
<beans:bean id="authenticationProcessingFilterEntryPoint"
       class="com.xyz.webapp.security.AuthenticationProcessingFilterEntryPoint">
    <beans:property name="loginFormUrl" value="/login.html"/>
    <beans:property name="serverSideRedirect" value="true"/>
</beans:bean>
The class AuthenticationProcessingFilterEntryPoint is derived from Spring's one
/**
 * Extended Spring AuthenticationProcessingFilterEntryPoint for playing together with JSF Ajax redirects.
 */
public class AuthenticationProcessingFilterEntryPoint extends
    org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint
{
    public static final String ATTRIBUTE_LOGIN_PAGE = "web.secfilter.authenticator.showLogin";

    public void commence(ServletRequest request, ServletResponse response, AuthenticationException authException)
        throws IOException, ServletException {

        request.setAttribute(ATTRIBUTE_LOGIN_PAGE, getLoginFormUrl());
        super.commence(request, response, authException);
    }
}
On the JSF side we need a special phase listener to do the redirect by ExternalContext. We check at first the buffered page URL in the beforePhase. If it was set - an JSF redirect is needed and will be done.
/**
 * Phase listener for the Restore View Phase which manages display of login page.
 */
public class SecurityPhaseListener implements PhaseListener
{
    private static final Log LOG = LogFactory.getLog(SecurityPhaseListener.class);

    public void afterPhase(PhaseEvent event) { ; }

    public void beforePhase(PhaseEvent event) {
        FacesContext fc = event.getFacesContext();

        String loginPage = (String) fc.getExternalContext().getRequestMap().
            get("web.secfilter.authenticator.showLogin");
        if (StringUtils.isNotBlank(loginPage)) {
            doRedirect(fc, loginPage);
        }
    }

    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }

    /**
     * Does a regular or ajax redirect.
     */
    public void doRedirect(FacesContext fc, String redirectPage) 
        throws FacesException {
        ExternalContext ec = fc.getExternalContext();

        try {
            if (ec.isResponseCommitted()) {
                // redirect is not possible
                return;
            }

            // fix for renderer kit (Mojarra's and PrimeFaces's ajax redirect)
            if ((RequestContext.getCurrentInstance().isAjaxRequest()
                || fc.getPartialViewContext().isPartialRequest())
                && fc.getResponseWriter() == null
                && fc.getRenderKit() == null) {
                    ServletResponse response = (ServletResponse) ec.getResponse();
                    ServletRequest request = (ServletRequest) ec.getRequest();
                    response.setCharacterEncoding(request.getCharacterEncoding());

                    RenderKitFactory factory = (RenderKitFactory)  
                     FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);

                    RenderKit renderKit = factory.getRenderKit(fc,
                     fc.getApplication().getViewHandler().calculateRenderKitId(fc));

                    ResponseWriter responseWriter =
                        renderKit.createResponseWriter(
                        response.getWriter(), null, request.getCharacterEncoding());
                        fc.setResponseWriter(responseWriter);
            }

            ec.redirect(ec.getRequestContextPath() + 
                (redirectPage != null ? redirectPage : ""));
        } catch (IOException e) {
            LOG.error("Redirect to the specified page '" + 
                redirectPage + "' failed");
            throw new FacesException(e);
        }
    }
}
It works in both cases - for Ajax and regular request.