Saturday, September 28, 2013

Spring 4 Conditional

Spring 4 is introducing a new feature called Conditional - an annotation targeted towards Spring components which generate beans and vetos the generation of these beans, in essence it provides a way to conditionally generate beans.

Consider a simple example:

I have a service called "CustomerService", with two implementations of this service, say "CustomerService1" and "CustomerService2". Based on the presence of a System property, say "servicedefault", I want to create the default "CustomerService1" implementation and if it is absent I want to create an instance of "CustomerService2".

Using Java configuration based Spring bean definition I would do it this way:

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public static class ContextConfig {
 @Bean
 public CustomerService customerService() {
  if (System.getProperty("servicedefault")!=null) {
   return new CustomerServiceImpl1();
  }
 
  return new CustomerServiceImpl2();
 }
}

An alternate approach is to use Spring Bean Profiles introduced with Spring 3.1:

1
2
3
4
5
6
7
8
9
10
11
@Bean
@Profile("default")
public CustomerService service1() {
 return new CustomerServiceImpl1();
}
 
@Bean
@Profile("prod")
public CustomerService service2() {
 return new CustomerServiceImpl2();
}


However, Profiles in this specific instance is a bit unwieldy as it will be difficult to set a profile for managing the implementation strategy of one bean, it is much more appropriate for cases where the behavior for a set of beans needs to be controlled.


Spring 4 introduces Conditional annotation where this behavior can be achieved in a little more reusable way.

Conditional depends on a set of Condition classes to specify the predicate, this way:

1
2
3
4
5
6
7
8
9
10
11
12
13
class HardCodedSystemPropertyPresentCondition implements Condition {
 @Override
 public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  return (System.getProperty("servicedefault") != null);
 }
}
 
class HardCodedSystemPropertyAbsentCondition implements Condition {
 @Override
 public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  return (System.getProperty("servicedefault") == null);
 }
}

I need two predicates, one to specify the positive condition and one to specify the negative condition, these can now be applied on the bean definitions:

1
2
3
4
5
6
7
8
9
10
11
@Bean
@Conditional(HardCodedSystemPropertyPresentCondition.class)
public CustomerService service1() {
 return new CustomerServiceImpl1();
}
 
@Bean
@Conditional(HardCodedSystemPropertyAbsentCondition.class)
public CustomerService service2() {
 return new CustomerServiceImpl2();
}

However, note that there is a hardcoded system property name "servicedefault" in the code of the Condition, this can be cleaned up further by using meta annotations. A new meta annotation can be defined this way:

1
2
3
4
5
6
7
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnSystemPropertyCondition.class)
public @interface ConditionalOnSystemProperty {
 public String value();
 public boolean exists() default true;
}

This meta annotation ConditionalOnSystemProperty takes in two user specified attributes - "value" for the system property name and "exists" to check whether the property exists or to check that the property does not exist. The meta annotation is tagged with @Conditional annotation which points to the Condition class to trigger for beans annotated with this new meta annotation, the Condition class is the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class OnSystemPropertyCondition implements Condition {
 
 @Override
 public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  Map<String, Object> attributes
   = metadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName());
  Boolean systemPropertyExistsCheck = (Boolean)attributes.get("exists");
  String systemProperty = (String)attributes.get("value");
   
  if ((systemPropertyExistsCheck && (System.getProperty(systemProperty) != null)) ||
    (!systemPropertyExistsCheck && (System.getProperty(systemProperty) == null))) {
   return true;
  }
  return false;
 }
}

The logic here is to get hold of the attributes defined on the @Bean instances using the meta-annotation, and to trigger the check for the presence or the absence of the system property based on the additional "exists" attribute. This reusable meta-annotation can now be defined on the @Bean instances to conditionally create the beans, this way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
public static class ContextConfig {
  
 @Bean
 @ConditionalOnSystemProperty("servicedefault")
 public CustomerService service1() {
  return new CustomerServiceImpl1();
 }
  
 @Bean
 @ConditionalOnSystemProperty(value="servicedefault", exists=false)
 public CustomerService service2() {
  return new CustomerServiceImpl2();
 }
}


Wrap Up
The example here is trivial and probably not very realistic and is used purely to demonstrate the Conditional feature. A far better example in Spring 4 is the way Conditional is used for modifying the behavior of Spring 3.1 based Profiles itself that I had mentioned previously, Profiles is internally now based on meta-annotation based Conditional:

1
2
3
4
@Conditional(ProfileCondition.class)
public @interface Profile {
 String[] value();
}

Friday, September 6, 2013

Spring data "Page" based bootstrap pager

Spring data "Page" provides a good abstraction for a Page of records, and has fields to access the current page that is being displayed, the count of all the records and the page size.

A good pager control can be built in jsp, given a Page of records, something like the following:



I have created a small gist with just such a jsp tag library, and it internally displays this pager using twitter bootstrap based styling. Using this tag library is simple, it just requires a "Page" as an attribute:

1
<util:pagination thispage="${aPage}"></util:pagination>


Here is a copy of the gist:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<%@ attribute name="thispage" type="org.springframework.data.domain.Page" required="true" rtexprvalue="true" description="The current Page"%>
<c:set var="pageNumber" value="${thispage.number}" />
<c:set var="maxPages" value="${thispage.totalPages}" />
<c:set var="size" value="${thispage.size}" />
<div class="span12">
<ul class="pager text-right">
<c:choose>
<c:when test="${pageNumber gt 0}">
<spring:url value="" var="first">
<spring:param name="page" value="0" />
<spring:param name="size" value="${size}" />
</spring:url>
<spring:url value="" var="previous">
<spring:param name="page" value="${pageNumber - 1}" />
<spring:param name="size" value="${size}" />
</spring:url>
<spring:message code="list_first" var="first_label" text="First" htmlEscape="false" />
<spring:message code="list_previous" var="previous_label" text="Previous" htmlEscape="false"/>
<li><a href="${first}" title="${fn:escapeXml(first_label)}">First</a></li>
<li><a href="${previous}" title="${fn:escapeXml(previous_label)}">Previous</a></li>
</c:when>
<c:otherwise>
<li class="disabled"><a href="#">First</a></li>
<li class="disabled"><a href="#" title="${fn:escapeXml(previous_label)}">Previous</a></li>
</c:otherwise>
</c:choose>
<c:out value=" " />
<spring:message code="list_page" text="Page {0} of {1}"
arguments="${pageNumber + 1},${maxPages}" argumentSeparator="," />
<c:out value=" " />
<c:choose>
<c:when test="${pageNumber lt (maxPages-1)}">
<spring:url value="" var="next">
<spring:param name="page" value="${pageNumber + 1}" />
<spring:param name="size" value="${size}" />
</spring:url>
<spring:url value="" var="last">
<spring:param name="page" value="${maxPages-1}" />
<spring:param name="size" value="${size}" />
</spring:url>
<spring:message code="list_last" var="last_label" text="Last"
htmlEscape="false" />
<spring:message code="list_next" var="next_label" text="Next"
htmlEscape="false" />
<li><a href="${next}" title="${fn:escapeXml(next_label)}">Next</a></li>
<li><a href="${last}" title="${fn:escapeXml(last_label)}">Last</a></li>
</c:when>
<c:otherwise>
<li class="disabled"><a href="#">Next</a></li>
<li class="disabled"><a href="#"
title="${fn:escapeXml(last_label)}">Last</a></li>
</c:otherwise>
</c:choose>
</ul>
<spring:message code="list_size" var="list_size" text="Records per page" htmlEscape="false" />
<c:out value="${list_size} " />
<c:forEach var="i" begin="5" end="25" step="5">
<c:choose>
<c:when test="${size == i}">
<c:out value="${i}" />
</c:when>
<c:otherwise>
<spring:url value="" var="sizeUrl">
<spring:param name="page" value="0" />
<spring:param name="size" value="${i}" />
</spring:url>
<a href="${sizeUrl}">${i}</a>
</c:otherwise>
</c:choose>
<c:out value=" " />
</c:forEach>
</div>
view raw pagination.tag hosted with ❤ by GitHub