Decorator Pattern
In our previous post, we covered the motivation for using design patterns to limit custom code changes in uPortal 5 and discussed two of the most useful patterns, Decorator and Strategy. In this post, we provide a real example of applying Decorator to uPortal5. In our next post, we will provide a real example of Strategy.
This post assumes you are comfortable with Spring Framework and XML contexts for configuration. The key point is that Spring instantiates beans based on the XML contexts and wires them up together when one bean references another. For example:
```
<bean id="portalPreAuthenticationFilter"
class="org.apereo.portal.spring.security.preauth.PortalPreAuthenticatedProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
```
This stanza denotes a bean, an object of class PortalPreAuthenticatedProcessingFilter, that will be created by Spring, and that a setter or property will be given another bean named authenticationManager. There are several ways to wire up beans, including annotations, that provide a rich (and sometimes confusing) environment to inject beans and values into other beans.
On to our patterns ...
Decorator
By nature, Decorators are often quite simple. Too much additional code is usually a sign that more than a decorator class is needed. So, our example is the need for a straightforward feature -- the ability to turn on/off CAS authenticationFilter for uPortal.
The goal for this change is to allow setting a simple configuration value in a property file to enable the use of the Central Authentication Service (CAS) authentication filter, which is only needed when ‘Guest Redirect to CAS’ is desired. For a majority of portal deployments, this is not used. If we did not have the requirement that this feature needs to be driven by a property value, we might have simply documented how to add the bean and update other beans. However, the decorator approach provides a simple solution to address this requirement.
Let’s start with the class. It was easy to come up with a name, OptionalFilterDecorator, that describes this feature. If you would like to look at the live code, feel free to search for it, but we will take a look at the significant parts here.
The class implements javax.servlet.Filter interface.
```
public class OptionalFilterDecorator implements Filter {
```
It only has two properties: the flag and the filter it wraps, with setters.
```
private boolean enable = false;
private Filter wrappedFilter;
public void setEnable(boolean enable) {
this.enable = enable;
}
public void setWrappedFilter(Filter wrappedFilter) {
this.wrappedFilter = wrappedFilter;
}
```
The remainder of the class implements the Filter interface and checks the enable property to determine if the wrapped filter should be called.
```
@Override
public void init(FilterConfig filterConfig) throws ServletException {
if (wrappedFilter == null) {
throw new ServletException("No wrappedFilter configured");
}
if (this.enable) {
this.wrappedFilter.init(filterConfig);
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (wrappedFilter == null) {
throw new ServletException("No wrappedFilter configured");
}
if (this.enable) {
this.wrappedFilter.doFilter(request, response, chain);
} else {
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
if (this.enable && this.wrappedFilter != null) {
this.wrappedFilter.destroy();
}
}
}
```
This is so generic, it could decorate any derived Filter object that needs this functionality. Nothing in this class cares that it is currently meant for a CAS filter. This generality is not always possible, but it is nice when we can attain it.
On to the Spring context XML. The live version is in the securityContext.xml file in uPortal. These are the last two bean definitions in that file. Here, we get to leverage what Spring offers: injection of control! Typically, the CAS authentication filter is named casAuthenticationFilter. No surprises there. In our case, we are going to name the actual filter casInnerAuthenticationFilter and name the decorator casAuthenticationFilter.
The last bit of the puzzle is how to set enable to TRUE or FALSE from a properties file. Here, uPortal shines. It already supports loading a predefined set of properties files and importing them into the Spring context. Setting a default is also supported. In our real-world example, the property value is casAuthenticationFilter, which is set in uPortal.properties.
Pulled all together, here is what the Spring bean definition looks like:
```
<!--
| Spring-managed instance of CAS AuthenticationFilter. As above, this allows us to configure
| CAS authentication in the preferred approach. This is only needed for 'Guest Redirect to CAS'.
+-->
<bean name="casAuthenticationFilter" class="org.apereo.portal.spring.web.OptionalFilterDecorator">
<property name="wrappedFilter" ref="casInnerAuthenticationFilter" />
<property name="enable" value="${cas.enable.redirect.guest.to.login:false}" />
</bean>
<bean name="casInnerAuthenticationFilter" class="org.jasig.cas.client.authentication.AuthenticationFilter">
<property name="casServerLoginUrl" value="${cas.authenticationFilter.cas.login.url}" />
<property name="service" value="${cas.authenticationFilter.service}" />
<property name="gateway" value="false" />
<property name="encodeServiceUrl" value="true" />
</bean>
</beans>
```
In the decorator bean definition, we define the class to the decorator. We also tell Spring to use the bean named casInnerAuthenticationFilter as the wrapped filter value. We also pull the properties value as the value of enable, including a default of FALSE.
So, that is a real example of using the Decorator pattern in uPortal. While this is not used a whole lot, when applicable, it sure is nice to implement, test, and review.
In our next post, we will look at using the Strategy pattern to customize a class by modifying its behavior versus what the Community usually needs.
Stay tuned, and contact us with any questions!
Useful Reading:
- Customizing uPortal (Part 1): Using Design Patterns
- Customizing uPortal (Part 2): Using the Decorator Design Pattern
- Customizing uPortal (Part 3): Using the Strategy Design Pattern
- Customizing uPortal (Part 4): Adding Custom Code to uPortal-start