Saturday, October 27, 2012

Spring AOP in security - controlling creation of UI components via aspects



The following post will show how in one of the projects that I took part in we used Spring's AOP to introduce some security related functionalities. The concept was such that in order for the user to see some UI components he needed to have a certain level of security privillages. If that requirement was not met then the UIComponent was not presented. Let's take a look at the project structure:


Then there were also the aopApplicationContext.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

 <aop:aspectj-autoproxy />
 <context:annotation-config />
 <context:component-scan base-package="pl.grzejszczak.marcin.aop">
  <context:exclude-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
 </context:component-scan>
 <bean class="pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor" factory-method="aspectOf"/> 

</beans>

Now let's take a look at the most interesting lines of the Spring's application context.

First we have all the required schemas - I don't think that this needs to be explained in more depth.
Then we have:
<aop:aspectj-autoproxy/>


which enables the @AspectJ support.

Next there is the

<context:annotation-config />
<context:component-scan base-package="pl.grzejszczak.marcin.aop">
    <context:exclude-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>

first we are turning on Spring configuration via annotations. Then deliberatly we exclude aspects from being initialized as beans by Spring itself. Why? Because...

<bean class="pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor" factory-method="aspectOf"/>

we want to create the aspect by ourselves and provide the factory-method="aspectOf" . By doing so our aspect will be included in the autowiring process of our beans - thus all the fields annotated with the @Autowired annotation will get the beans injected.

Now let's move on to the code:

UserServiceImpl.java

package pl.grzejszczak.marcin.aop.service;

import org.springframework.stereotype.Service;

import pl.grzejszczak.marcin.aop.type.Role;
import pl.grzejszczak.marcin.aop.user.UserHolder;

@Service
public class UserServiceImpl implements UserService {
 private UserHolder userHolder;

 @Override
 public UserHolder getCurrentUser() {
  return userHolder;
 }

 @Override
 public void setCurrentUser(UserHolder userHolder) {
  this.userHolder = userHolder;
 }

 @Override
 public Role getUserRole() {
  if (userHolder == null) {
   return null;
  }
  return userHolder.getUserRole();
 }
}

The class UserServiceImpl is immitating a service that would get the current user information from the db or from the current application context.

UserHolder.java

package pl.grzejszczak.marcin.aop.user;

import pl.grzejszczak.marcin.aop.type.Role;

public class UserHolder {
 private Role userRole;

 public UserHolder(Role userRole) {
  this.userRole = userRole;
 }

 public Role getUserRole() {
  return userRole;
 }

 public void setUserRole(Role userRole) {
  this.userRole = userRole;
 }
}


This is a simple holder class that holds information about current user Role.

Role.java

package pl.grzejszczak.marcin.aop.type;


public enum Role {
 ADMIN("ADM"), WRITER("WRT"), GUEST("GST");

 private String name;

 private Role(String name) {
  this.name = name;
 }

 public static Role getRoleByName(String name) {

  for (Role role : Role.values()) {

   if (role.name.equals(name)) {
    return role;
   }
  }

  throw new IllegalArgumentException("No such role exists [" + name + "]");
 }

 public String getName() {
  return this.name;
 }

 @Override
 public String toString() {
  return name;
 }
}

Role is an enum that defines a role for a person being an Admin, Writer or a Guest.

UIComponent.java

package pl.grzejszczak.marcin.aop.ui;

public abstract class UIComponent {
 protected String componentName;

 protected String getComponentName() {
  return componentName;
 }

}

An abstraction over concrete implementations of some UI components.

SomeComponentForAdminAndGuest.java

package pl.grzejszczak.marcin.aop.ui;

import pl.grzejszczak.marcin.aop.annotation.SecurityAnnotation;
import pl.grzejszczak.marcin.aop.type.Role;

@SecurityAnnotation(allowedRole = { Role.ADMIN, Role.GUEST })
public class SomeComponentForAdminAndGuest extends UIComponent {

 public SomeComponentForAdminAndGuest() {
  this.componentName = "SomeComponentForAdmin";
 }

 public static UIComponent getComponent() {
  return new SomeComponentForAdminAndGuest();
 }
}

This component is an example of a UI component extention that can be seen only by users who have roles of Admin or Guest.

SecurityAnnotation.java

package pl.grzejszczak.marcin.aop.annotation;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import pl.grzejszczak.marcin.aop.type.Role;

@Retention(RetentionPolicy.RUNTIME)
public @interface SecurityAnnotation {
 Role[] allowedRole();
}


Annotation that defines a roles that can have this component created.

UIFactoryImpl.java

package pl.grzejszczak.marcin.aop.ui;

import org.apache.commons.lang.NullArgumentException;
import org.springframework.stereotype.Component;

@Component
public class UIFactoryImpl implements UIFactory {

 @Override
 public UIComponent createComponent(Class<? extends UIComponent> componentClass) throws Exception {
  if (componentClass == null) {
   throw new NullArgumentException("Provide class for the component");
  }
  return (UIComponent) Class.forName(componentClass.getName()).newInstance();
 }
}


A factory class that given the class of an object that extends UIComponent returns a new instance of the given UIComponent.

SecurityInterceptor.java


package pl.grzejszczak.marcin.aop.interceptor;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import pl.grzejszczak.marcin.aop.annotation.SecurityAnnotation;
import pl.grzejszczak.marcin.aop.service.UserService;
import pl.grzejszczak.marcin.aop.type.Role;
import pl.grzejszczak.marcin.aop.ui.UIComponent;

@Aspect
public class SecurityInterceptor {
 private static final Logger LOGGER = LoggerFactory.getLogger(SecurityInterceptor.class);

 public SecurityInterceptor() {
  LOGGER.debug("Security Interceptor created");
 }

 @Autowired
 private UserService userService;

 @Pointcut("execution(pl.grzejszczak.marcin.aop.ui.UIComponent pl.grzejszczak.marcin.aop.ui.UIFactory.createComponent(..))")
 private void getComponent(ProceedingJoinPoint thisJoinPoint) {
 }

 @Around("getComponent(thisJoinPoint)")
 public UIComponent checkSecurity(ProceedingJoinPoint thisJoinPoint) throws Throwable {
  LOGGER.info("Intercepting creation of a component");

  Object[] arguments = thisJoinPoint.getArgs();
  if (arguments.length == 0) {
   return null;
  }

  Annotation annotation = checkTheAnnotation(arguments);
  boolean securityAnnotationPresent = (annotation != null);

  if (securityAnnotationPresent) {
   boolean userHasRole = verifyRole(annotation);
   if (!userHasRole) {
    LOGGER.info("Current user doesn't have permission to have this component created");
    return null;
   }
  }
  LOGGER.info("Current user has required permissions for creating a component");
  return (UIComponent) thisJoinPoint.proceed();
 }

 /**
  * Basing on the method's argument check if the class is annotataed with
  * {@link SecurityAnnotation}
  * 
  * @param arguments
  * @return
  */
 private Annotation checkTheAnnotation(Object[] arguments) {
  Object concreteClass = arguments[0];
  LOGGER.info("Argument's class - [{}]", new Object[] { arguments });
  AnnotatedElement annotatedElement = (AnnotatedElement) concreteClass;
  Annotation annotation = annotatedElement.getAnnotation(SecurityAnnotation.class);
  LOGGER.info("Annotation present - [{}]", new Object[] { annotation });
  return annotation;
 }

 /**
  * The function verifies if the current user has sufficient privilages to
  * have the component built
  * 
  * @param annotation
  * @return
  */
 private boolean verifyRole(Annotation annotation) {
  LOGGER.info("Security annotation is present so checking if the user can use it");
  SecurityAnnotation annotationRule = (SecurityAnnotation) annotation;
  List<Role> requiredRolesList = Arrays.asList(annotationRule.allowedRole());
  Role userRole = userService.getUserRole();
  return requiredRolesList.contains(userRole);
 }
}


This is the aspect defined at the pointcut of executing a function createComponent of the UIFactory interface. Inside the Around advice there is the logic that first checks what kind of an argument has been passed to the method createComponent (for example SomeComponentForAdminAndGuest.class). Next it is checking if this class is annotated with SecurityAnnotation and if that is the case it checks what kind of Roles are required to have the component created. Afterwards it checks if the current user (from UserService to UserHolder's Roles) has the required role to present the component. If that is the case thisJoinPoint.proceed() is called which in effect returns the object of the class that extends UIComponent.

Now let's test it - here comes the SpringJUnit4ClassRunner

AopTest.java

package pl.grzejszczak.marcin.aop;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import pl.grzejszczak.marcin.aop.service.UserService;
import pl.grzejszczak.marcin.aop.type.Role;
import pl.grzejszczak.marcin.aop.ui.SomeComponentForAdmin;
import pl.grzejszczak.marcin.aop.ui.SomeComponentForAdminAndGuest;
import pl.grzejszczak.marcin.aop.ui.SomeComponentForGuest;
import pl.grzejszczak.marcin.aop.ui.SomeComponentForWriter;
import pl.grzejszczak.marcin.aop.ui.UIFactory;
import pl.grzejszczak.marcin.aop.user.UserHolder;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:aopApplicationContext.xml" })
public class AopTest {

 @Autowired
 private UIFactory uiFactory;

 @Autowired
 private UserService userService;

 @Test
 public void adminTest() throws Exception {
  userService.setCurrentUser(new UserHolder(Role.ADMIN));
  Assert.assertNotNull(uiFactory.createComponent(SomeComponentForAdmin.class));
  Assert.assertNotNull(uiFactory.createComponent(SomeComponentForAdminAndGuest.class));
  Assert.assertNull(uiFactory.createComponent(SomeComponentForGuest.class));
  Assert.assertNull(uiFactory.createComponent(SomeComponentForWriter.class));
 }
}


And the logs:

pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:26 Security Interceptor created
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:38 Intercepting creation of a component
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:48 Argument's class - [[class pl.grzejszczak.marcin.aop.ui.SomeComponentForAdmin]]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:54 Annotation present - [@pl.grzejszczak.marcin.aop.annotation.SecurityAnnotation(allowedRole=[ADM])]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:57 Security annotation is present so checking if the user can use it
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:70 Current user has required permissions for creating a component
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:38 Intercepting creation of a component
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:48 Argument's class - [[class pl.grzejszczak.marcin.aop.ui.SomeComponentForAdminAndGuest]]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:54 Annotation present - [@pl.grzejszczak.marcin.aop.annotation.SecurityAnnotation(allowedRole=[ADM, GST])]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:57 Security annotation is present so checking if the user can use it
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:70 Current user has required permissions for creating a component
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:38 Intercepting creation of a component
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:48 Argument's class - [[class pl.grzejszczak.marcin.aop.ui.SomeComponentForGuest]]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:54 Annotation present - [@pl.grzejszczak.marcin.aop.annotation.SecurityAnnotation(allowedRole=[GST])]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:57 Security annotation is present so checking if the user can use it
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:66 Current user doesn't have permission to have this component created
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:38 Intercepting creation of a component
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:48 Argument's class - [[class pl.grzejszczak.marcin.aop.ui.SomeComponentForWriter]]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:54 Annotation present - [@pl.grzejszczak.marcin.aop.annotation.SecurityAnnotation(allowedRole=[WRT])]
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:57 Security annotation is present so checking if the user can use it
pl.grzejszczak.marcin.aop.interceptor.SecurityInterceptor:66 Current user doesn't have permission to have this component created

The unit test shows that for given Admin role only first two components get created whereas for the two others nulls are returned (due to the fact that user doesn't have proper rights).

That is how in our project we used Spring's AOP to create a simple framework that would check if the user can have the given component created or not. Thanks to this after having programmed the aspects one doesn't have to remember about writing any security related code since it will be done for him.

If you have any suggestions related to this post please feel free to comment it :)

Wednesday, October 24, 2012

Mockito InvocationOnMock - checking an argument



Hi!

In one of my projects we had a very interesting situation in terms of testing. We couldn't mock an invocation of a static method due to the fact that PowerMock was not allowed to be used so there were plenty of objects and dependencies being initialized. What is more we were using a custom made dependency injection system that had a possibility of injecting a mock.

The problem was such that during a test we wanted to assert whether one of the objects was in a very precise state. This object was created using the new operator so we couldn't mock it (again no PowerMock allowed). Fortunately this object got passed to a method of an object that we could mock...

As presented below timeConsumingExternalService is an object that we could mock via the custom dependency injection system whereas the SomePojo class is an object whose state we would like to verify.

timeConsumingExternalService.processSomeObject(new SomePojo("name", "surname", 1, 1.0));

So what we did was that in our mock to which the object got passed we created a new Answer (the same Answer that I spoke of here). Due to which we could access the InvocationOnMock and the arguments passed to the method as such.

Mockito.doAnswer(new Answer<Object>() {
   public Object answer(InvocationOnMock invocation) throws Throwable {
    Object[] object = invocation.getArguments();

      if (object.length > 0) {
          SomePojo somePojo = (SomePojo) object[0];

          Assert.assertEquals("name", somePojo.getName());
          LOGGER.debug("Names are equal");
          Assert.assertEquals("surname", somePojo.getSurname());
          LOGGER.debug("Surnames are equal");
          Assert.assertTrue(1 == somePojo.getIntValue());
          LOGGER.debug("Ints are equal");
          Assert.assertTrue(1.0 == somePojo.getDoubleValue());
          LOGGER.debug("Doubles are equal");

          LOGGER.debug("Object being an argument of the function [" + String.valueOf(somePojo) + "]");
  }
    return null;
   }
  }).when(timeConsumingExternalServiceMock).processSomeObject(Mockito.any(SomePojo.class));

Of course the logs regarding the equalities are unnecessary since if they wouldn't be equal we would have an assertion exception - I left them for the purpose of this post.

And in the logs we can find:

pl.grzejszczak.marcin.ServiceIntegrationTest:48 Names are equal
pl.grzejszczak.marcin.ServiceIntegrationTest:50 Surnames are equal
pl.grzejszczak.marcin.ServiceIntegrationTest:52 Ints are equal
pl.grzejszczak.marcin.ServiceIntegrationTest:54 Doubles are equal
pl.grzejszczak.marcin.ServiceIntegrationTest:56 Object being an argument of the function [SomePojo [name=name, surname=surname, intValue=1, doubleValue=1.0]]

So in this way something that seems impossible to be verified can get verified :)

Update!

Thanks to Holger's suggestion I took a look at the ArgumentCaptor object and that is true that it is an elegant solution to retrieve information about the arguments executed on a method. Where InvocationOnMock can give you much more information and possibilities (for instance regarding the method being executed or just execute the real method) for this particular case a much more elegant, easier and faster way of dealing with the issue would be:


  //service that executes the external service
  executorService.execute(someTask);

  final ArgumentCaptor<SomePojo> argumentCaptor = ArgumentCaptor.forClass(SomePojo.class);
  Mockito.verify(timeConsumingExternalServiceMock).processSomeObject(argumentCaptor.capture());
  SomePojo somePojo = argumentCaptor.getValue();
  Assert.assertEquals("name", somePojo.getName());
  LOGGER.debug("Names are equal");
  Assert.assertEquals("surname", somePojo.getSurname());
  LOGGER.debug("Surnames are equal");
  Assert.assertTrue(1 == somePojo.getIntValue());
  LOGGER.debug("Ints are equal");
  Assert.assertTrue(1.0 == somePojo.getDoubleValue());
  LOGGER.debug("Doubles are equal");

The logs:
pl.grzejszczak.marcin.junit.SomeTask:26 Before processing an object
pl.grzejszczak.marcin.junit.SomeTask:28 After processing an object
pl.grzejszczak.marcin.ServiceIntegrationTest:75 Names are equal
pl.grzejszczak.marcin.ServiceIntegrationTest:77 Surnames are equal
pl.grzejszczak.marcin.ServiceIntegrationTest:79 Ints are equal
pl.grzejszczak.marcin.ServiceIntegrationTest:81 Doubles are equal

Thanks again Holger!

More interesting articles

Next portion of interesting articles from dZone

Understanding JVM internals
How to analyze Java thread dumps
Practicing Code, Java, and Databases
10 Things I Never Want to See a Java Developer Do Again
jQuery Kwicks - Sexy Sliding Panels And Navigational Interaction
Why Do Bad Things Happen to Good Code?

and sth about Apache Camel

Tuesday, October 23, 2012

Hibernate inheritance table per subclass with different foreign key names



In one of the projects we were supposed to implement an inheritance in the db. The project based on Spring and Hibernate so we could pick one of the three inheritance methods. We decided to pick the inheritance of table for subclass. The problem as been discussed in many ways but it is not common that the names of the foreign keys are not equal to the name of the primary key.


This is a diagram of a database that will be used in our example. As far as logic goes - we have an abstraction called ABSENCE that can be either a recursive absence (REC_ABSENCE) or a month recursive absence (MONTH_REC_ABSENCE).

The recursive absence and the month recursive absence are differing from the absence in terms of data being held more than in terms of the behavior.

Of course from the logical point of view, this example can have little sense but it is just an example so don't focus on the business side.

Absence.java

package pl.grzejszczak.marcin.entity;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;

/**
 * Absence generated by hbm2java
 */
@Entity
@Table(name = "ABSENCE")
@Inheritance(strategy = InheritanceType.JOINED)
public class Absence implements java.io.Serializable {
 /**
  * 
  */
 private static final long serialVersionUID = -4572952141587410338L;
 private int id;
 private int version;
 private Date startDate;
 private Date endDate;

 public Absence() {
 }

 @Id
 @Column(name = "ID", unique = true, nullable = false)
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 public int getId() {
  return this.id;
 }

 public void setId(int id) {
  this.id = id;
 }

 @Version
 @Column(name = "VERSION", nullable = false)
 public int getVersion() {
  return this.version;
 }

 public void setVersion(int version) {
  this.version = version;
 }

 @Temporal(TemporalType.TIMESTAMP)
 @Column(name = "START_DATE", nullable = false, length = 23)
 public Date getStartDate() {
  return this.startDate;
 }

 public void setStartDate(Date startDate) {
  this.startDate = startDate;
 }

 @Temporal(TemporalType.TIMESTAMP)
 @Column(name = "END_DATE", length = 23)
 public Date getEndDate() {
  return this.endDate;
 }

 public void setEndDate(Date endDate) {
  this.endDate = endDate;
 }
}

RecursiveAbsence.java

Note that you do not have to explicitly create an id field. You define it by means of an annotation @PrimaryKeyJoinColumn with providing the name of the id column for the given entity (REC_ABSENCE_ID) and the name of the column for the id it is referencing (ID in the ABSENCE table).

package pl.grzejszczak.marcin.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

/**
 * 
 * Hibernate mapping strategy: Inheritance mapping: joined subclasses (table per
 * subclass)
 * 
 */
@Entity
@Table(name = "REC_ABSENCE")
@PrimaryKeyJoinColumn(name = "REC_ABSENCE_ID", referencedColumnName = "ID")
public class RecursiveAbsence extends Absence implements java.io.Serializable {

 /**
  * 
  */
 private static final long serialVersionUID = 1L;
 private boolean isMonthly;
 private boolean isWeekly;

 public RecursiveAbsence() {
 }

 @Column(name = "isMONTHLY", nullable = false, precision = 1, scale = 0)
 public boolean getIsMonthly() {
  return this.isMonthly;
 }

 public void setIsMonthly(boolean isMonthly) {
  this.isMonthly = isMonthly;
 }

 @Column(name = "isWEEKLY", nullable = false, precision = 1, scale = 0)
 public boolean getIsWeekly() {
  return this.isWeekly;
 }

 public void setIsWeekly(boolean isWeekly) {
  this.isWeekly = isWeekly;
 }
}

MonthRecursiveAbsence.java

Issue with the referencing of the column name as in the previous example.

package pl.grzejszczak.marcin.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Transient;

@Entity
@Table(name = "MONTH_REC_ABSENCE")
@PrimaryKeyJoinColumn(name = "MONTH_ABSENCE_ID", referencedColumnName = "ID")
public class MonthRecursiveAbsence extends Absence implements java.io.Serializable {

 /**
  * 
  */
 private static final long serialVersionUID = 7844275381822579686L;

 private Integer monthId;

 public MonthRecursiveAbsence() {
 }

 @Transient
 public MonthDefinitionEnum getMonth() {
  return MonthDefinitionEnum.getByKey(monthId);
 }

 @Column(name = "MONTH_ID")
 public Integer getMonthId() {
  return monthId;
 }

 public void setMonthId(Integer monthId) {
  this.monthId = monthId;
 }
}


Monday, October 22, 2012

Spring BeanPostProcessor for a specified type



I was recently having a discussion how can one use a BeanPostProcessor to execute some logic for a specified class.

Looking at the Javadoc for the BeanPostProcessor one can read that:
Factory hook that allows for custom modification of new bean instances, e.g. checking for marker interfaces or wrapping them with proxies.
So how can one create in an easy way a BeanPostProcessor for a precise type without creating a cascade of ifs or instance ofs? This is my concept of solving this problem - perhaps you know an easier one? :)

SomeService.java

package pl.grzejszczak.marcin.postprocessor;

public interface SomeService {
 void methodA();

 void methodB();
}

SomeServiceImpl.java

package pl.grzejszczak.marcin.postprocessor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SomeServiceImpl implements SomeService {
 private static final Logger LOGGER = LoggerFactory.getLogger(SomeServiceImpl.class);

 public SomeServiceImpl() {
  LOGGER.debug("SomeServiceImpl - I'm created!");
 }

 private void afterInit() {
  LOGGER.debug("SomeServiceImpl - After init!");
 }

 private void destroyMethod() {
  LOGGER.debug("SomeServiceImpl - Destroy Method!");
 }

 @Override
 public void methodA() {
  LOGGER.debug("SomeServiceImpl - Method A executed");
 }

 @Override
 public void methodB() {
  LOGGER.debug("SomeServiceImpl - Method B executed");
 }

}

SomeOtherService.java

package pl.grzejszczak.marcin.postprocessor;

public interface SomeOtherService {
 void methodC();

 void methodD();
}

SomeOtherServiceImpl.java

package pl.grzejszczak.marcin.postprocessor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SomeOtherServiceImpl implements SomeOtherService {
 private static final Logger LOGGER = LoggerFactory.getLogger(SomeOtherServiceImpl.class);

 public SomeOtherServiceImpl() {
  LOGGER.debug("SomeOtherServiceImpl - I'm created!");
 }

 private void afterInit() {
  LOGGER.debug("SomeOtherServiceImpl - After init!");
 }

 private void destroyMethod() {
  LOGGER.debug("SomeOtherServiceImpl - Destroy Method!");
 }

 @Override
 public void methodC() {
  LOGGER.debug("SomeOtherServiceImpl - Method C executed");
 }

 @Override
 public void methodD() {
  LOGGER.debug("SomeOtherServiceImpl - Method D executed");
 }

}

AbstractBeanPostProcessor.java

package pl.grzejszczak.marcin.postprocessor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public abstract class AbstractBeanPostProcessor<T> implements BeanPostProcessor {

 private Class<T> clazz;

 public AbstractBeanPostProcessor(Class<T> clazz) {
  this.clazz = clazz;
 }

 @Override
 public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
  checkConditions();

  if (clazz.isAssignableFrom(bean.getClass())) {
   doAfter();
  }
  return bean;
 }

 @Override
 public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
  checkConditions();

  if (clazz.isAssignableFrom(bean.getClass())) {
   doBefore();
  }
  return bean;
 }

 private void checkConditions() {
  if (clazz == null) {
   throw new NullArgumentException("Provide the interface for the post processor");
  }
 }

 public abstract void doBefore();

 public abstract void doAfter();

}

SomeServicePostProcessor.java

package pl.grzejszczak.marcin.postprocessor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class SomeServicePostProcessor extends AbstractBeanPostProcessor<SomeService> {

 private static final Logger LOGGER = LoggerFactory.getLogger(SomeServicePostProcessor.class);

 public SomeServicePostProcessor() {
  super(SomeService.class);
 }

 @Override
 public void doBefore() {
  LOGGER.info("BEFORE it's init method has been executed but AFTER SomeServiceImpl has been instantiated I would like to do sth...");
 }

 @Override
 public void doAfter() {
  LOGGER.info("AFTER SomeServiceImpl has executed its init method I would like to do sth more...");
 }
}

SpringMain.java


package pl.grzejszczak.marcin.postprocessor;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringMain {

 public static void main(String[] args) {
  ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  SomeService someService = context.getBean(SomeService.class);
  someService.methodA();
  someService.methodB();
  SomeOtherService someOtherService = context.getBean(SomeOtherService.class);
  someOtherService.methodC();
  someOtherService.methodD();
  context.close();
 }
}


ApplicationContext.xml



 <bean class="pl.grzejszczak.marcin.postprocessor.SomeServiceImpl" destroy-method="destroyMethod" init-method="afterInit"/>
 <bean class="pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl" destroy-method="destroyMethod" init-method="afterInit"/>
 <bean class="pl.grzejszczak.marcin.postprocessor.SomeServicePostProcessor"/>


Logs

2012-10-23 00:20:38,863 INFO  [main] org.springframework.context.support.ClassPathXmlApplicationContext:495 Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@28d11816: startup date [Tue Oct 23 00:20:38 CEST 2012]; root of context hierarchy
2012-10-23 00:20:38,956 INFO  [main] org.springframework.beans.factory.xml.XmlBeanDefinitionReader:315 Loading XML bean definitions from class path resource [applicationContext.xml]
2012-10-23 00:20:39,213 INFO  [main] org.springframework.beans.factory.support.DefaultListableBeanFactory:557 Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6bb4469: defining beans [pl.grzejszczak.marcin.postprocessor.SomeServiceImpl#0,pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl#0,pl.grzejszczak.marcin.postprocessor.SomeServicePostProcessor#0]; root of factory hierarchy
2012-10-23 00:20:39,214 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeServiceImpl:10 SomeServiceImpl - I'm created!
2012-10-23 00:20:39,215 INFO  [main] pl.grzejszczak.marcin.postprocessor.SomeServicePostProcessor:18 BEFORE its init method has been executed but AFTER SomeServiceImpl has been instantiated I would like to do sth...
2012-10-23 00:20:39,216 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeServiceImpl:14 SomeServiceImpl - After init!
2012-10-23 00:20:39,216 INFO  [main] pl.grzejszczak.marcin.postprocessor.SomeServicePostProcessor:23 AFTER SomeServiceImpl has executed its init method I would like to do sth more...
2012-10-23 00:20:39,220 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl:10 SomeOtherServiceImpl - I'm created!
2012-10-23 00:20:39,221 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl:14 SomeOtherServiceImpl - After init!
2012-10-23 00:20:39,225 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeServiceImpl:23 SomeServiceImpl - Method A executed
2012-10-23 00:20:39,241 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeServiceImpl:28 SomeServiceImpl - Method B executed
2012-10-23 00:20:39,242 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl:23 SomeOtherServiceImpl - Method C executed
2012-10-23 00:20:39,242 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl:28 SomeOtherServiceImpl - Method D executed
2012-10-23 00:20:39,242 INFO  [main] org.springframework.context.support.ClassPathXmlApplicationContext:1020 Closing org.springframework.context.support.ClassPathXmlApplicationContext@28d11816: startup date [Tue Oct 23 00:20:38 CEST 2012]; root of context hierarchy
2012-10-23 00:20:39,243 INFO  [main] org.springframework.beans.factory.support.DefaultListableBeanFactory:433 Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6bb4469: defining beans [pl.grzejszczak.marcin.postprocessor.SomeServiceImpl#0,pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl#0,pl.grzejszczak.marcin.postprocessor.SomeServicePostProcessor#0]; root of factory hierarchy
2012-10-23 00:20:39,244 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeOtherServiceImpl:18 SomeOtherServiceImpl - Destroy Method!
2012-10-23 00:20:39,245 DEBUG [main] pl.grzejszczak.marcin.postprocessor.SomeServiceImpl:18 SomeServiceImpl - Destroy Method!
As you can see it is quite easy, using generics and the BeanPostProcessor, to specify certain behavors for a given type (generics and constructor of SomeServiceImpl) or a group of types of classes (generics and constructor of SomeService).

Wednesday, October 17, 2012

Interesting articles


Since one of my aims of starting a blog was to treat it as a notepad or a storage place for some interesting concepts regarding programming I recommend reading these articles:

How to tune Java Garbage Collector
NoSQL week review
Javascript and jQuery

Tuesday, October 16, 2012

Calculating Coherence cluster size on a remote server using JMX



In one of the projects I had a problem regarding calculating the size of a Oracle Coherence cluster. It doesn't seem to difficult since there is already a nice piece of code that can do it for us:

Calculating Coherence cluster size

The problem is that this code is working properly only if the Coherence cluster is not remote. So how can we access it?

We have to modify the following function

public static MBeanServer getMBeanServer() {
        MBeanServer server = null;
        for (Object o : MBeanServerFactory.findMBeanServer(null)) {
            server = (MBeanServer) o;
            if (DOMAIN_DEFAULT.length() == 0 ||
                server.getDefaultDomain().equals(DOMAIN_DEFAULT)) {
                break;
            }
            server = null;
        }
        if (server == null) {
            server = MBeanServerFactory.createMBeanServer(DOMAIN_DEFAULT);
        }

        return server;
    }


To this one
public static MBeanServerConnection getMBeanServer() throws IOException {
 JMXServiceURL url = new JMXServiceURL("service://...");
 JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
 return jmxc.getMBeanServerConnection();
}

And modify the line

MBeanServer server = getMBeanServer();


To use the new interface
MBeanServerConnection server = getMBeanServer();

More information on how to define the Coherence JMX service URL and JMX for Coherence as such can be found here

Managing Coherence using JMX