Monday, December 31, 2012

Lightweight Web Application Framework : PrimeFaces (JSF) + Guice + MyBatis (PART1)

Recently, my friend asks me how to build a lightweight java web application. Many Java web developer would choose Spring and hibernate to build a traditional web application. However, it may not be lightweight enough. I suggested him try to use Guice and MyBatis to build the application framework. Although Spring is more feature-riched than Guice, I admitted that Guice is more lightweight and easier to use. MyBatis is also a lightweight SQL map framework. It can integrate with Guice framework very well.

Here, I will try to set up a simple web application with PrimeFaces, Guice and MyBatis. I hope that my friend could learn how to do it. :)

Using MyFaces and PrimeFaces in presentation layer.
Integrate MyFaces and PrimeFaces is simple. Just simply get the JARS file from MyFaces website and PrimeFaces website.

For MyFaces, just add the following sample configuration into your web.xml.
<display-name>TestGuice</display-name>
<context-param>
    <param-name>facelets.DEVELOPMENT</param-name>
    <param-value>false</param-value>
  </context-param>
  <context-param>
    <param-name>facelets.REFRESH_PERIOD</param-name>
    <param-value>2</param-value>
  </context-param>
  <context-param>
    <param-name>javax.faces.CONFIG_FILES</param-name>
    <param-value>/WEB-INF/faces-config.xml</param-value>
  </context-param>
  <context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
  </context-param>
  <context-param>
    <param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
    <param-value>resources.application</param-value>
  </context-param>
  <context-param>
    <param-name>org.apache.myfaces.ALLOW_JAVASCRIPT</param-name>
    <param-value>true</param-value>
  </context-param>
  <context-param>
    <param-name>org.apache.myfaces.AUTO_SCROLL</param-name>
    <param-value>false</param-value>
  </context-param>
  <context-param>
    <param-name>org.apache.myfaces.DETECT_JAVASCRIPT</param-name>
    <param-value>false</param-value>
  </context-param>
  <context-param>
    <param-name>org.apache.myfaces.ERROR_HANDLING</param-name>
    <param-value>false</param-value>
  </context-param>
  <context-param>
    <param-name>org.apache.myfaces.EXPRESSION_FACTORY</param-name>
    <param-value>org.jboss.el.ExpressionFactoryImpl</param-value>
  </context-param>
  <context-param>
    <param-name>org.apache.myfaces.PRETTY_HTML</param-name>
    <param-value>false</param-value>
  </context-param>
  <welcome-file-list>
    <welcome-file>index.do</welcome-file>
  </welcome-file-list>
  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>org.apache.myfaces.webapp.MyFacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

For PrimeFaces, there should be no configuration. If you want to use the PrimeFaces theme, you could add the following context parameter in the web.xml.
<context-param>
    <param-name>primefaces.THEME</param-name>
    <param-value>glass-x</param-value>
  </context-param>


Integrate Google Guice with JSF
Google Guice will be used as IOC container. Dependency Injection for service class and SQL mapper class is done though Guice. To integrate with JSF, I suggest to simply add a
ServletContextListener to do it.

Add the following configration in your web.xml:
<listener>
    <listener-class>org.borislam.GuiceContextListener</listener-class>
  </listener>

Inside the ServletContextListener, just create a Guice Injector and put it in ServletContext:
public class GuiceContextListener implements ServletContextListener {

   public void contextDestroyed(ServletContextEvent servletContextEvent) {
     ServletContext servletContext = servletContextEvent.getServletContext();
     servletContext.removeAttribute(Injector.class.getName());
   }

   public void contextInitialized(ServletContextEvent servletContextEvent) {
     Injector injector = Guice.createInjector(
       new MyBatisModule() {
        @Override
        protected void initialize() {         
         //add singleton service class
         bind(SimpleService.class).to(SimpleServiceImpl.class).in(Singleton.class); 
        }
       );
     
     ServletContext servletContext = servletContextEvent.getServletContext();
     servletContext.setAttribute(Injector.class.getName(), injector);
  }
}  

Create a JSF base backing bean.
In the base backing bean, get the Guice injector in the PostConstruct method. Then, every backing beans in your web application need to extends this base backing bean.

package org.borislam.view;

import java.io.Serializable;

import javax.annotation.PostConstruct;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;

import com.google.inject.Injector;

public abstract class BasePageBean implements Serializable{

   private Injector injector;
   
   public BasePageBean() {}
   
   public Injector getInjector() {
     if(injector == null) {
       ServletContext servletContext = (ServletContext)FacesContext.getCurrentInstance().
                                        getExternalContext().getContext();
       injector = (Injector)servletContext.getAttribute(Injector.class.getName());  
     }
     return injector;
   }
   public void setInjector(Injector injector) {
     this.injector = injector;
   }
   
   @PostConstruct
   public void init() {
     getInjector().injectMembers(this);
   }
}      

In the second part, I will continue to demonstrate how to integrate MyBatis and Guice.

Wednesday, November 28, 2012

Session Timeout Handling on JSF AJAX request

When we develop JSF application with AJAX behaviour, we may experience the problem in handling timeout scenario of Ajax request. For example, if you are using J2EE Form-based authentication, a normal request should be redirected to the login page after session timeout. However, if your request is AJAX, the response could not be treated properly on the client-side. User will remain on the same page and does not aware that the session is expired.

Many people proposed solution for this issue. The followings are two of possible solutions involve the use of Spring security framework:
1. Oleg Varaksin's post
2. Spring Security 3 and ICEfaces 3 Tutorial

Yet, some applications may just using simple mechanism to stored their authentication and authorization information in session. For those application that is not using Spring Security framework, how can they handle such problem? I just modified the solution proposed by Oleg Varaksin a bit as my reference.


First, create a simple session scoped JSF managed bean called "MyJsfAjaxTimeoutSetting".
The main purpose of this POJO is just to allow you to configure the redirect url after session timeout in faces-config.xml. You may not need this class if you do not want the timeout URL to be configurable.

public class MyJsfAjaxTimeoutSetting {
 

 public MyJsfAjaxTimeoutSetting() {
 }

 private String timeoutUrl;
 
 
 public String getTimeoutUrl() {
  return timeoutUrl;
 }

 public void setTimeoutUrl(String timeoutUrl) {
  this.timeoutUrl = timeoutUrl;
 }

 
}

Second, create a PhaseListener to handle the redirect of Ajax request.
This PhaseListener is the most important part of the solution. It re-creates the response so that the Ajax request could be redirect after timeout.

import org.borislam.util.FacesUtil;
import org.borislam.util.SecurityUtil;
import java.io.IOException;
import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.primefaces.context.RequestContext;

public class MyJsfAjaxTimeoutPhaseListener implements PhaseListener
{

 public void afterPhase(PhaseEvent event)
 {
 }
 
 public void beforePhase(PhaseEvent event)
 {   
  MyJsfAjaxTimeoutSetting timeoutSetting = (MyJsfAjaxTimeoutSetting)FacesUtil.getManagedBean("MyJsfAjaxTimeoutSetting");
  FacesContext fc = FacesContext.getCurrentInstance();
  RequestContext rc = RequestContext.getCurrentInstance();
  ExternalContext ec = fc.getExternalContext();
  HttpServletResponse response = (HttpServletResponse) ec.getResponse();
  HttpServletRequest request = (HttpServletRequest) ec.getRequest();
        
  if (timeoutSetting ==null) {
   System.out.println("JSF Ajax Timeout Setting is not configured. Do Nothing!");
   return ;
  }

  
  UserCredential user = SecurityUtil.getUserCredential(); 
  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //
  // You can replace the above line of code with the security control of your application.
  // For example , you may get the authenticated user object from session or threadlocal storage. It depends on your design.
  //
  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
  if (user==null) {
   // user credential not found. 
   // considered to be a Timeout case
     

   if (ec.isResponseCommitted()) {
    // redirect is not possible
    return;
   }
      
   try{
       
       if ( 
     ( (rc!=null &&  RequestContext.getCurrentInstance().isAjaxRequest())
     || (fc!=null && fc.getPartialViewContext().isPartialRequest()))
      && fc.getResponseWriter() == null
       && fc.getRenderKit() == null) {

        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() + 
          (timeoutSetting.getTimeoutUrl() != null ? timeoutSetting.getTimeoutUrl() : ""));     
       }
        
   } catch (IOException e) {
    System.out.println("Redirect to the specified page '" + 
      timeoutSetting.getTimeoutUrl() + "' failed");
    throw new FacesException(e);
   }
  } else {
   return ; //This is not a timeout case . Do nothing !
  }
 }

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

}


The details of the FacesUtil.getManagedBean("MyJsfAjaxTimeoutSetting") is shown below:
public static Object getManagedBean(String beanName) {
     
     FacesContext fc = FacesContext.getCurrentInstance();
     ELContext elc = fc.getELContext();
     ExpressionFactory ef = fc.getApplication().getExpressionFactory();
     ValueExpression ve = ef.createValueExpression(elc, getJsfEl(beanName), Object.class);
     return ve.getValue(elc);
}


Configuration
As said before, the purpose of the session scoped managed bean, MyJsfAjaxTimeoutSetting, is just to allow you to make the timeoutUrl configurable in your faces-config.xml.
 
  MyJsfAjaxTimeoutSetting
  org.borislam.security.MyJsfAjaxTimeoutSetting
  session
  
   timeoutUrl
   /login.do
  
 

Most importantly, add the PhaseListener in your faces-config.xml.
 <lifecycle>
 <phase-listener id="JSFAjaxTimeoutPhaseListener">hk.edu.hkeaa.infrastructure.security.JSFAjaxTimeoutPhaseListener</phase-listener>
 </lifecycle>

If you are using spring framework, you could managed the MyJsfAjaxTimeoutSetting in Spring with the help of SpringBeanFacesELResolver. Then, you can use the following configuration.
    
    


Saturday, September 1, 2012

Accessing Weblogic Embedded LDAP programmatically by Spring LDAP module

Oracle Weblogic Application Server includes an embedded LDAP server which acts as the default security provider data store. There are few methods to access the embedded LDAP server. Oracle Weblogic provides an administration console for user to access it. We can create user, create group or edit detail through this administration console.

We could also access the embedded LDAP programmatically. Using WLST (Weblogic Scripting tool) is a standard way to do it but you need have knowledge of Jython programming. Alternatively, if you want to access by Java language, you can access the Weblogic MBeans directly. However, this is not an easy task. You can see this link for more detail.

In this article, I will show you an alternative way to access the Weblogic Embedded LDAP programmatically with Spring-LDAP. If you are acquainted with Spring, you may find this method quite easy and useful. In the following example, we do simple operation like search user, create user and add user to group through spring "ldaptemplate".

Step 1: Create WlsLdapUser class which represent a LDAP user.


import java.util.HashSet;
import java.util.Set;
import javax.naming.Name;

public class WlsLdapUser {
 private String dnFull;
 private String id;
 private String password;
 private Set<String> group;
 private Name dn;
 
 public String getId() {
   return this.id;
 }
 
 public void setId(String id) {
   this.id = id;
 }
 
 public String getPassword() {
   return this.password;
 }
 
 public void setPassword(String password) {
   this.password = password;
 }

 
 public Set<String> getGroup() {
   return this.group;
 }
 
 public void setGroup(Set<String> group) {
   this.group = group;
 }
 
 public void addToGroup(String group) {
   if (getGroup() == null)
     setGroup(new HashSet());
   getGroup().add(group);
 }
 
 public void setDnFull(String dn) {
   this.dnFull = dn;
 }
 
 public String getDnFull() {
   return this.dnFull;
 }
 
 public void setDn(Name dn) {
   this.dn = dn;
 }
 
 public Name getDn() {
   return this.dn;
 }

 @Override
 public String toString() {
  return "WlsLdapUser [dnFull=" + dnFull + ", id=" + id + ", password="+ password + ", group=" + group + ", dn=" + dn + "]";
 }

}




Step 2: Create a base class "BaseLdapRepository" for accessing the Weblogic Embedded LDAP
This class make use Spring "ldapTemplate" for accessing the Weblogic Embedded LDAP. It encapsulates basic operation for accessing LDAP (e.g. delete LDAP attribute, replacing LDAP attribute, add LDAP attribute). The "ldapTemplate" is injected to the repository class by define the beans in XML (step 4).


import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.ModificationItem;

import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.LdapTemplate;

public class BaseLdapRepository {

  private LdapTemplate ldapTemplate ;

  public void setLdapTemplate(LdapTemplate ldapTemplate)
  {
      this.ldapTemplate = ldapTemplate;
  }
  
  
  public LdapTemplate getLdapTemplate() {
   return ldapTemplate;
  }

  protected void deleteAttr(Name dn, String attrName) {
      List attributes = new ArrayList();
      Attribute attr = new BasicAttribute(attrName);
      ModificationItem item = new ModificationItem(3, attr);

      attributes.add(item);
      modify(dn, attributes);
  }

  protected void replaceAttr(Name dn, String attrName, String attrVal) {
      List attributes = new ArrayList();
      Attribute attr = new BasicAttribute(attrName);
      attr.add(attrVal);
      ModificationItem item = new ModificationItem(2, attr);

      attributes.add(item);
      modify(dn, attributes);
  }

  protected void addAttr(Name dn, String attrName, String attrVal) {
      List attributes = new ArrayList();
      Attribute attr = new BasicAttribute(attrName);
      attr.add(attrVal);
      ModificationItem item = new ModificationItem(1, attr);

      attributes.add(item);
      modify(dn, attributes);
  }

  private void modify(Name dn, List<ModificationItem> attributes) {
   getLdapTemplate().modifyAttributes(dn, (ModificationItem[])attributes.toArray(new ModificationItem[0]));

      attributes.clear();
  }

  public Map<String, String[]> getAttrsStartsWith(Name dn, String opAttr) {
      return (Map)getLdapTemplate().lookup(dn, new String[] { opAttr }, new ContextMapper()
  {
        public Object mapFromContext(Object ctx)
        {
          DirContextAdapter context = (DirContextAdapter)ctx;
          Attributes attrs = context.getAttributes();
          NamingEnumeration ids = attrs.getIDs();
          Map m = new HashMap();
          try {
            while (ids.hasMore()) {
              String id = (String)ids.next();
              System.out.println("id: " + id);

              m.put(id, context.getStringAttributes(id));
            }
          } catch (NamingException e) {
            e.printStackTrace();
          }
          return m;
        }
      });
  }
}


Step 3: Create a repository class to access the Weblogic Embedded LDAP
In order to implement the create(), find(), addUserGroups() and getUserGroups() methods in this LDAP repository class, we must know some Weblogic specific attributes of the Embedded LDAP.

Each user in Embedded LDAP belongs the "top", "person", "origanizationalPerson", "inetOrgPerson", "wlsUser" objectclass. Therefore, when we implement the create() method, we must make sure the user entry belongs to these objectClasses. Also, each user in Embedded LDAP is under the "People" organization unit (OU). Thus, we need to search under "ou=people" when we find a specific user.

When we add user to groups, we make use of the "uniquemember" attribute in the group entry. On the contrary, when want to know the groups in which a specific user belongs to, we make use if the "wlsMemberOf" attribute in user entry.


import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.naming.Name;

import org.apache.commons.lang.StringUtils;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.support.AbstractContextMapper;
import org.springframework.ldap.filter.EqualsFilter;


public class WlsLdapUserRepository extends BaseLdapRepository {
 
 protected DistinguishedName buildDn(WlsLdapUser user) {
     return buildDn(user.getId());
 }
    
 protected DistinguishedName buildDn(String uid) {
     DistinguishedName dn = new DistinguishedName();
     dn.add("ou", "people");
     dn.add("cn", uid);
     return dn;
 }
 
 protected DistinguishedName buildDnGroup(String cn)
 {
     DistinguishedName dn = new DistinguishedName();
    dn.add("ou", "groups");
     dn.add("cn", cn);
     return dn;
 }
 
 public void addToGroup(String dn, String gid) {
      DistinguishedName gdn = buildDnGroup(gid);
 
      super.addAttr(gdn, "uniquemember", dn);
 }
 
 public WlsLdapUser create(WlsLdapUser user)
 {
     Name dn = buildDn(user);
     DirContextAdapter context = new DirContextAdapter(dn);
     context.setAttributeValues("objectclass", new String[] { "top", "person", "inetOrgPerson", "organizationalPerson", "wlsuser" });
     context.setAttributeValue("cn", user.getId());
     context.setAttributeValue("sn", user.getId());
     context.setAttributeValue("uid", user.getId());
     context.setAttributeValue("userPassword", user.getPassword());
     getLdapTemplate().bind(dn, context, null);
     user = find(user.getId());
     return user;
 }
 
 public WlsLdapUser find(String uid) {
     WlsLdapUser user;
     try {      
      user = (WlsLdapUser)getLdapTemplate().searchForObject("ou=people", new EqualsFilter("uid", uid).toString(), new WlsUserContextMapper());
     }
     catch (Exception e)
     {
       e.printStackTrace();
       return null;
     }
     return user;
 }
   
 public List<String> getUserGroups(WlsLdapUser user) {
  Map<String, String[]> groupMmap = null;
  String[] groupMapArray = null ;
  ArrayList<String> results = new ArrayList<String>();
  groupMmap = this.getAttrsStartsWith(user.getDn(), "wlsMemberOf");
  groupMapArray = groupMmap.get("wlsMemberOf");
  if (groupMapArray.length >0) {
    String[] allGroups= StringUtils.split(groupMapArray[0], ",");
    for (String s:allGroups){
     if (StringUtils.contains(s, "cn=")) {
      String aGroup = StringUtils.remove(s,"cn=");
      results.add(aGroup);  
     }
    }
  }
  return results;
 }
 
 private static  class WlsUserContextMapper extends AbstractContextMapper
 {
     protected Object doMapFromContext(DirContextOperations context)
     {
      WlsLdapUser user = new WlsLdapUser();
      user.setId(context.getStringAttribute("uid"));
      user.setDnFull(context.getNameInNamespace());
      user.setDn(context.getDn());
      return user;
     }
 }
}


Step 4: Add your spring application context XML file
The connection details to the Embedded LDAP is put inside this file.


<bean id="contextSource" class="org.springframework.ldap.core.support.DirContextSource">
  <property name="url" value="ldap://127.0.0.1:7001">
  </property>
  <property name="userDn" value="cn=Admin"> 
  </property>
  <property name="password" value="welcome1">
  </property>
  <property name="base" value="ou=myrealm,dc=base_domain">
  </property>
 </bean>

 <bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
  <constructor-arg ref="contextSource" />
 </bean>
 
 <bean id="wlsLdapUserRepository" class="org.borislam.ldap.repository.WlsLdapUserRepository">
  <property name="ldapTemplate" ref="ldapTemplate" />
 </bean>


Step 5: Test your application with the following code

@Named
public class TestLdapService {
 @Inject
 private WlsLdapUserRepository wlsLdapUserRepository;
 
 public void createWlsUser(){
  WlsLdapUser u = new WlsLdapUser();
     u.setId("leonardmessi");
     u.setPassword("welcome1");
     u = this.wlsLdapUserRepository.create(u);
     this.wlsLdapUserRepository.addToGroup(u.getDnFull(), "testGroup");
     this.wlsLdapUserRepository.addToGroup(u.getDnFull(), "Administrators");
  System.out.println("create user :" + u.toString());
 }
 
 public void findWlsUser(String userId){
  WlsLdapUser u = this.wlsLdapUserRepository.find(userId);
  System.out.println("create user :" + u.toString());
 }
}


You can verify the user created by the above program through the Weblogic administration console.


Besides, you can also verify by directly login the newly created user "leonardmessi" with password "welcome1".

Friday, August 17, 2012

Writing your spring security expression language annotation - PART 3

In the last part of tutorial, I will discuss how to override the behaviour of defualt spring security method expression. You may wonder why I need to override the default behaviour of these methods. The reason behind is that, in recent development project, we are reviewing the developer's code and we hope to maintain a standard coding practice. We find that the default method expression is too flexible. In our case, under similar coding scenario, some developers use hasRole() for security checking while other developers using hasPermission() for security checking. In order to keep the maintainability of the program, we thus have an idea to disallow developer to use certain secruity method expression. That's why we have the crazy idea of overriding the default behaviour of these methods. (This may not be a good idea :P. But anyway, we have implement it :D)

In this example, I simply show how to override the default behaviour of hasRole() method. You can not do this by override the hasRole() method of SecurityExpressionRoot directly because most of the method in this class is marked as final. To archive it, we have to create another new expression root class and expression handler.

Step 1: Create your Expression Root class and Evaluation Context Class with your own expression method

In my case, I want to not allow developer to use certain method (e.g. hasRole()). I can do this by simpily not include this method. Or I can simpily throw exception to alert use not to use it.

You can see the following code that I have involved custom behaviour (i.e. simply throw exception) of hasAuthority(), hasAnyAuthority() , hasRole(), hasAnyRole(). You can add more logic to these method to suite your application requirement.

public class RestrictedSecurityExpressionRoot {

  protected final Authentication authentication;
  private AuthenticationTrustResolver trustResolver;
  private RoleHierarchy roleHierarchy;
  private Set<String> roles;
  public final boolean permitAll = true;

  public final boolean denyAll = false;
  private PermissionEvaluator permissionEvaluator;
  public final String read = "read";
  public final String write = "write";
  public final String create = "create";
  public final String delete = "delete";
  public final String admin = "administration";

  public RestrictedSecurityExpressionRoot(Authentication a) {
    if (a == null) {
      throw new IllegalArgumentException("Authentication object cannot be null");
    }
    this.authentication = a;
  }
  
  //Default behaviour changed
  public final boolean hasAuthority(String authority) {
  //or your can do your own application logic
  throw new RuntimeException("Role Security Checking is not allow in this framework", null);
  }

  //Default behaviour changed
  public final boolean hasAnyAuthority(String[] authorities) {
   //or your can do your own application logic
   throw new RuntimeException("Role Security Checking is not allow in this application framework");

  }

  //Default behaviour changed
  public final boolean hasRole(String role) {
  //or your can do your own application logic
   throw new RuntimeException("Role Security Checking is not allow in this application framework");
   
  }

  //Default behaviour changed
  public final boolean hasAnyRole(String[] roles) {
  //or your can do your own application logic
   throw new RuntimeException("Role Security Checking is not allow in this application framework");
  }

  public final Authentication getAuthentication() {
    return this.authentication;
  }

  public final boolean permitAll() {
    return true;
  }

  public final boolean denyAll() {
    return false;
  }

  public final boolean isAnonymous() {
    return this.trustResolver.isAnonymous(this.authentication);
  }

  public final boolean isAuthenticated() {
    return (!(isAnonymous()));
  }

  public final boolean isRememberMe() {
    return this.trustResolver.isRememberMe(this.authentication);
  }

  public final boolean isFullyAuthenticated() {
    return ((!(this.trustResolver.isAnonymous(this.authentication))) && (!(this.trustResolver.isRememberMe(this.authentication))));
  }

  public Object getPrincipal() {
    return this.authentication.getPrincipal();
  }

  public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
    this.trustResolver = trustResolver;
  }

  public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
    this.roleHierarchy = roleHierarchy;
  }

  private Set<String> getAuthoritySet() {
    if (this.roles == null) {
      this.roles = new HashSet();
      Collection userAuthorities = this.authentication.getAuthorities();

      if (this.roleHierarchy != null) {
        userAuthorities = this.roleHierarchy.getReachableGrantedAuthorities(userAuthorities);
      }

      this.roles = AuthorityUtils.authorityListToSet(userAuthorities);
    }

    return this.roles;
  }

  public boolean hasPermission(Object target, Object permission) {
    return this.permissionEvaluator.hasPermission(this.authentication, target, permission);
  }

  public boolean hasPermission(Object targetId, String targetType, Object permission) {
    return this.permissionEvaluator.hasPermission(this.authentication, (Serializable)targetId, targetType, permission);
  }

  public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {
    this.permissionEvaluator = permissionEvaluator;
  }
 
  private Object filterObject;
  private Object returnObject;
  private Object target;

  public void setFilterObject(Object filterObject) {
    this.filterObject = filterObject;
  }

  public Object getFilterObject() {
    return this.filterObject;
  }

  public void setReturnObject(Object returnObject) {
    this.returnObject = returnObject;
  }

  public Object getReturnObject() {
    return this.returnObject;
  }

  void setThis(Object target) {
    this.target = target;
  }

  public Object getThis() {
    return this.target;
  } 
}


public class RestrictedMethodSecurityEvaluationContext extends StandardEvaluationContext {
 
    private static final Log logger = LogFactory.getLog(RestrictedMethodSecurityEvaluationContext.class);
    private ParameterNameDiscoverer parameterNameDiscoverer;
    private final MethodInvocation mi;
    private boolean argumentsAdded;
  
    public RestrictedMethodSecurityEvaluationContext(Authentication user, MethodInvocation mi)
    {
      this(user, mi, new LocalVariableTableParameterNameDiscoverer());
    }
  
    public RestrictedMethodSecurityEvaluationContext(Authentication user, MethodInvocation mi, ParameterNameDiscoverer parameterNameDiscoverer)
    {
      this.mi = mi;
      this.parameterNameDiscoverer = parameterNameDiscoverer;
    }
  
    public Object lookupVariable(String name)
    {
      Object variable = super.lookupVariable(name);
  
      if (variable != null) {
        return variable;
      }
  
      if (!(this.argumentsAdded)) {
        addArgumentsAsVariables();
        this.argumentsAdded = true;
      }
  
      variable = super.lookupVariable(name);
  
      if (variable != null) {
        return variable;
      }
  
      return null;
    }
  
    public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
      this.parameterNameDiscoverer = parameterNameDiscoverer;
    }
  
    private void addArgumentsAsVariables() {
      Object[] args = this.mi.getArguments();
  
      if (args.length == 0) {
        return;
      }
  
      Object targetObject = this.mi.getThis();
      Class targetClass = AopProxyUtils.ultimateTargetClass(targetObject);
  
      if (targetClass == null)
      {
        targetClass = targetObject.getClass();
      }
  
      Method method = AopUtils.getMostSpecificMethod(this.mi.getMethod(), targetClass);
      String[] paramNames = this.parameterNameDiscoverer.getParameterNames(method);
  
      if (paramNames == null) {
        logger.warn("Unable to resolve method parameter names for method: " + method + ". Debug symbol information is required if you are using parameter names in expressions.");
  
        return;
      }
  
      for (int i = 0; i < args.length; ++i)
        super.setVariable(paramNames[i], args[i]);
   }
}


Step 2: Create your Expression Handler class which implements SecurityExpressionHandler and ApplicationContextAware.

In this class, you have to implements your createEvaluationContext(). This is the key method to create your newly defined security expression root class (i.e. RestrictedSecurityExpressionRoot) and evaluation context class (i.e. RestrictedMethodSecurityEvaluationContext).

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.security.access.expression.ExpressionUtils;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.security.access.PermissionCacheOptimizer;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.DenyAllPermissionEvaluator;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
/**
 * 
 * Extended expression-handler facade which create RestrictedSecurityExpressionRoot instead of SecurityExpressionRoot
 * More restriction will be placed the use of Security Expression.
 * 
 * @author 
 */
public class RestrictedMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler, SecurityExpressionHandler<MethodInvocation>, ApplicationContextAware {
 
 
 private final AuthenticationTrustResolver trustResolver;
 private final ExpressionParser expressionParser;
 private BeanResolver br;
 private RoleHierarchy roleHierarchy;
 private PermissionEvaluator permissionEvaluator;
 
 public RestrictedMethodSecurityExpressionHandler()
 {
   this.trustResolver = new AuthenticationTrustResolverImpl();
   this.expressionParser = new SpelExpressionParser();
 
   this.permissionEvaluator = new DenyAllPermissionEvaluator(); }
 
 public final ExpressionParser getExpressionParser() {
   return this.expressionParser;
 }
 
 public final EvaluationContext createEvaluationContext(Authentication authentication, MethodInvocation invocation)
 {
   RestrictedSecurityExpressionRoot root = createSecurityExpressionRoot(authentication, invocation);
   root.setTrustResolver(this.trustResolver);
   root.setRoleHierarchy(this.roleHierarchy);
   StandardEvaluationContext ctx = createEvaluationContextInternal(authentication, invocation);
   ctx.setBeanResolver(this.br);
   ctx.setRootObject(root);
 
   return ctx;
 }
 
 
 protected RestrictedSecurityExpressionRoot createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation)
 {
     RestrictedSecurityExpressionRoot root = new RestrictedSecurityExpressionRoot(authentication);
     root.setThis(invocation.getThis());
     root.setPermissionEvaluator(getPermissionEvaluator());
     return root;
   }
 public void setRoleHierarchy(RoleHierarchy roleHierarchy)
 {
   this.roleHierarchy = roleHierarchy;
 }
 
 protected PermissionEvaluator getPermissionEvaluator() {
   return this.permissionEvaluator;
 }
 
 public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {
   this.permissionEvaluator = permissionEvaluator;
 }
 
 public void setApplicationContext(ApplicationContext applicationContext) {
   this.br = new BeanFactoryResolver(applicationContext);
 }
 
 protected final Log logger = LogFactory.getLog(super.getClass());

 private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
 private PermissionCacheOptimizer permissionCacheOptimizer = null;

 public StandardEvaluationContext createEvaluationContextInternal(Authentication auth, MethodInvocation mi)
 {
   return new RestrictedMethodSecurityEvaluationContext(auth, mi, this.parameterNameDiscoverer);
 }


 public Object filter(Object filterTarget, Expression filterExpression, EvaluationContext ctx)
 {
  RestrictedSecurityExpressionRoot rootObject = (RestrictedSecurityExpressionRoot)ctx.getRootObject().getValue();
   boolean debug = this.logger.isDebugEnabled();

   if (debug) {
     this.logger.debug("Filtering with expression: " + filterExpression.getExpressionString());
   }

   if (filterTarget instanceof Collection) {
     Collection collection = (Collection)filterTarget;
     List retainList = new ArrayList(collection.size());

     if (debug) {
       this.logger.debug("Filtering collection with " + collection.size() + " elements");
     }

     if (this.permissionCacheOptimizer != null) {
       this.permissionCacheOptimizer.cachePermissionsFor(rootObject.getAuthentication(), collection);
     }

     for (Iterator i$ = ((Collection)filterTarget).iterator(); i$.hasNext(); ) { Object filterObject = i$.next();
       rootObject.setFilterObject(filterObject);

       if (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) {
         retainList.add(filterObject);
       }
     }

     if (debug) {
       this.logger.debug("Retaining elements: " + retainList);
     }

     collection.clear();
     collection.addAll(retainList);

     return filterTarget;
   }

   if (filterTarget.getClass().isArray()) {
     Object[] array = (Object[])(Object[])filterTarget;
     List retainList = new ArrayList(array.length);

     if (debug) {
       this.logger.debug("Filtering array with " + array.length + " elements");
     }

     if (this.permissionCacheOptimizer != null) {
       this.permissionCacheOptimizer.cachePermissionsFor(rootObject.getAuthentication(), Arrays.asList(array));
     }

     for (Object o : array) {
       rootObject.setFilterObject(o);

       if (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) {
         retainList.add(o);
       }
     }

     if (debug) {
       this.logger.debug("Retaining elements: " + retainList);
     }

     Object[] filtered = (Object[])(Object[])Array.newInstance(filterTarget.getClass().getComponentType(), retainList.size());

     for (int i = 0; i < retainList.size(); ++i) {
       filtered[i] = retainList.get(i);
     }

     return filtered;
   }

   throw new IllegalArgumentException("Filter target must be a collection or array type, but was " + filterTarget);
 }

 public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
   this.parameterNameDiscoverer = parameterNameDiscoverer;
 }

 public void setPermissionCacheOptimizer(PermissionCacheOptimizer permissionCacheOptimizer) {
   this.permissionCacheOptimizer = permissionCacheOptimizer;
 }

 public void setReturnObject(Object returnObject, EvaluationContext ctx) {
   ((RestrictedSecurityExpressionRoot)ctx.getRootObject().getValue()).setReturnObject(returnObject);
 }
 
}


Step 3: Add your custom expression handler to your configuration file
The last step is to add your custom security expression handler to the XML file. The permissionEvaluator is created in previous post. You could see the souce code in this link "PART 1"

Now, when developer use the annotation @PreAuthorize("hasRole('XXX')"), it will throws exception. This is only an simple example. You could apply the same idea here and build your own custom logic inside the spring's default method (hasRole(), hasAuthority() , etc.) to suit your application requirement and logic.


     
  

Monday, August 13, 2012

Writing your spring security expression language annotation - PART 2

We are now going into the second part of the tutorial. In this post, it will show you how to add a new custom expression for @PreAuthorize annotation. For example, I will show how to add a adminOnly() expression language to the security expression root.


Step 1: Define your custom security expression root class
You have to first create a new security expression root class. This class should be extended from the abstract class org.springframework.security.access.expression.SecurityExpressionRoot. You can add your custom

This class is similar to org.springframework.security.access.expression.method.MethodSecurityExpressionRoot but with your new custom method added. As an example, I just add a very simple mehod adminOnly() which check if the user has admin role.


public class MyMethodSecurityExpressionRoot extends SecurityExpressionRoot {
 
    private static  Logger logger = LoggerFactory.getLogger(MyMethodSecurityExpressionRoot.class);
 
    private Object filterObject;
    private Object returnObject;
    private Object target;
    
     
    public  boolean adminOnly() {
     logger.debug("haha -- check if this function is used by admin role only");
     return  this.hasAuthority("ADMIN");
    }
    
    public MyMethodSecurityExpressionRoot(Authentication a) {
  super(a);
    }

    public void setFilterObject(Object filterObject) {
        this.filterObject = filterObject;
    }

    public Object getFilterObject() {
        return filterObject;
    }

    public void setReturnObject(Object returnObject) {
        this.returnObject = returnObject;
    }

    public Object getReturnObject() {
        return returnObject;
    }

    void setThis(Object target) {
        this.target = target;
    }

    public Object getThis() {
        return target;
    }

}


Step 2: Define your custom expression handler class
To add custom security expression method, you cannot use the DefaultMethodSecurityExpressionHandler. You need to define a new expression handler class by extends the DefaultMethodSecurityExpressionHandler.

You have to override the createSecurityExpressionRoot() method to create your custom security expression root class.


public class MyMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler  {

   @Override
   protected SecurityExpressionRoot createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
  MyMethodSecurityExpressionRoot root = new MyMethodSecurityExpressionRoot(authentication);
         root.setThis(invocation.getThis());
         root.setPermissionEvaluator(getPermissionEvaluator());
         return root;
   }
}


Step 3: Register the custom expression handler in XML


<sec:global-method-security pre-post-annotations="enabled">
  <sec:expression-handler ref="expressionHandler"/>
</sec:global-method-security>

<bean id="expressionHandler" class="org.borislam.security.ExtendedMethodSecurityExpressionHandler">
  <property name="permissionEvaluator" ref="permissionEvaluator"/>
</bean>  

<bean id="permissionEvaluator" class="org.borislam.security.BasePermissionEvaluator"/>





Example usage:

@PreAuthorize("adminOnly()")
 public void doSomething() {
  System.out.println("doSomething!!!"); 
 }

Sunday, August 5, 2012

Writing your spring security expression language annotation - PART 1

Spring security expression language is very useful. It helpes to secure your service/web methods with one line of code. It supports @PreAuthorize and @Secured. In the comming three posts, I will talk about how to add custom behaviour to the @PreAuthorize annotation.

Part 1 - Customize "hasPermission()" expression
Part 2 - Add new customize method security expression
Part 3 - Override default behaviour of spring security expression (e.g. hasRole() , permitAll() ...)

In this post, I will discuss how to add custom rule for permission checking in your application. This is somewhat similar to what describe in Sold Craft's post. You can reference it for more details.


Step 1: Add configuration in your spring security xml file.

You should first add the DefaultMethodSecurityExpressionHandler. It will instantiate a default MethodSecurityExpressionRoot which provides you all the default security expression (e.g. isAutghenticated(), isAnonymous() ,etc ) .

Besides, you have to add a permsisionEvaluator for that ExpressionHandler. If you are using spring security ACL, you could use AclPermissionEvaluator. In our case, we would create a BasePermissionEvaluator as our permission evaluator. You will see in step 2 that we would define custom rules in this permission evaluator.


<sec:global-method-security pre-post-annotations="enabled">
  <sec:expression-handler ref="expressionHandler"/>
</sec:global-method-security>

<bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandlerr">
    <property name="permissionEvaluator" ref="permissionEvaluator"/>
</bean>  

<bean id="permissionEvaluator" class="org.borislam.security.BasePermissionEvaluator"/>



Step 2: Create your PermissionEvaluator class
You must define a class that implements the org.springframework.security.access.PermissionEvaluator. You have to override the hasPermission() method and define custom rule in this class.

In my example, the user object contains a HashMap which stored the permissions of the user. I will perform checking the permission String against this Hashmap. This HashMap is populated during login by a filter. This part will not be skipped in this example.

For similicity, I just ignore targetDomainObject parameter in my example. By using the targetDomainObject, you can further define security rules on certian domain object of your application.

public class BasePermissionEvaluator implements PermissionEvaluator{
 
 
 @Override
 public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
  boolean hasPermission = false;
  if ( authentication != null &&  permission instanceof String){

   //implement the permission checking of your application here   
   //you can just check if the input permission is within your permission list

   //In my example, the user object contains a HashMap which stored the permission of the user.
   //The HashMap<String, PrivilegeResult> is populated during using login by filter. This will not be shown in this example 

   User user = SecurityUtil.getUserCredential();
   HashMap<String, PrivilegeResult> pMap =user.getPrivilegeMap();
   PrivilegeResult privResult = pMap.get(permission); 
   hasPermission =  privResult.isAllowAccess();

  } else {
   hasPermission =false; 
  }
  return hasPermission;
 }

 @Override
 public boolean hasPermission(Authentication authentication,
   Serializable targetId, String targetType, Object permission) {
    throw new RimtimeException("Id and Class permissions are not supperted by this application");
 }
}
\
Step 3: Example usage
You could simply add your the @PreAuthorize("hasPermission()") to secure your method.

@PreAuthorize("hasPermission(#user, 'allowDoSomething')")
 public String doSomething()
 {
  //do something
  System.out.println("Do something");
 }

In the next part of this series of tutorial, I will further discuss how to add your new custom method to the security expression root.

Friday, July 20, 2012

Adding Hibernate native SQL features into your Spring Data Repository

JPA provides @NamedNativeQuery for you to use native SQL. However, the usage is not so convenient, especially when you need to map multiple entities in your native SQL. You have to define a set of SqlResultSetMapping mapping which is quite error prone.

For those who have used Hibernate native SQL features before, you will find that it is much more easier to use than JPA's @NamedNativeQuery. In recent projects, I am using Spring Data JPA. I have added hibernate native query features to my Spring Data base repostory. You can now perform native query in JPA without SqlResultSetMapping.

1. Add you cusomt annotation @NativeQueries and @NativeQuery

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NativeQueries {
 NativeQuery[] queries() default  {};
}

@Retention(RetentionPolicy.RUNTIME)
public @interface NativeQuery {
 String name()  default "";
 String sql()  default "";
}

2. Add the method "queryNatively" in base Spring Data Repository.

If you do not know how to add custom behaviour to your Spring data JPA base repository, please see my previous post for how to customize your Spring data JPA base repository for detail. You can see in previous post that I intentionally expose the repository interface (i.e. the springDataRepositoryInterface property) in the GenericRepositoryImpl. This small tricks enable me to access the annotation in the repository interface easily.

public List queryNatively(String nativeQueryName, LinkedHashMap<String,Class<?>> inEntityClasses, Map inParams ){
 SQLQuery query =  this.createHibernateNativeQuery( nativeQueryName,  inParams );
     //add entities
     if (inEntityClasses!=null) {      
      for (Object key: inEntityClasses.keySet()) {
    String entityClassAlias = key.toString();
    Class<?> entityClass = (Class<?>)inEntityClasses.get(key);  
    query.addEntity(entityClassAlias,entityClass);
         }    
     }
             
     //add parameter
     if (inParams != null){
      for (Object key: inParams.keySet()) {
    String queryParamName = key.toString();
    Object queryParamValue = inParams.get(key);
    query.setParameter(queryParamName, queryParamValue);
         }
     }  
     return (query!=null)? query.list() : null ;
  }
    

  private SQLQuery createHibernateNativeQuery (String nativeQueryName, Map inParams ){
     if (GenericRepository.class.isAssignableFrom(getSpringDataRepositoryInterface())) {
      
      Annotation nativeQueryAnn = getSpringDataRepositoryInterface().getAnnotation(NativeQueries.class);
      if(nativeQueryAnn != null){
       NativeQueries nativeQueries = (NativeQueries)nativeQueryAnn;
       NativeQuery[] queries  = nativeQueries.queries();
    for (NativeQuery sqlquery : queries) { 
          
     if (StringUtils.equals(nativeQueryName, sqlquery.name())) {
      String sql  = sqlquery.sql();
      
      Session hiernateSess = em.unwrap(Session.class);
         SQLQuery query = hiernateSess.createSQLQuery(sql);
         
         //add parameter
         if (inParams != null){
          for (Object key: inParams.keySet()) {
        String queryParamName = key.toString();
        Object queryParamValue = inParams.get(key);
        query.setParameter(queryParamName, queryParamValue);
             }
         }  
         
         return query;
     }    
    }
      }
     
     }
     return null;
  }


3. Example Usage

In your repositry interface, define which native SQL queries you want to use through your @NativeQueries and @NativeQuery annotation. The usage is just similar to calling hibernate native query features. You could just see the hibernate alias and property references.

@NativeQueries (
 queries = {
   @NativeQuery(name="query1", 
         sql="SELECT {cat.*}, {mother.*}  FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID and c.name =  :catName "),
   @NativeQuery(name="query2", 
         sql="SELECT {cat.*} FROM CATS where c.ID =  :catName")       
 }
)
public interface CatRepository extends GenericRepository<Cat, Long> {
}

In your service or business class that inject your repository, you could just simply call the queryNatively() method to perform native SQL query.

@Service
public class CatService {
 
 @Inject
 private CatRepository catRepository;
 
 public List<Cat> searchCat( String catName) {
    
  List<Cat> catList;
 
  // Add entity mapping for your query
  HashMap<String, Object> inParams = new HashMap<String, Object>();
  inParams.put("catName", "Felix");
  
  
  // Prepare parameters for your native sql
  LinkedHashMap<String, Object> entityMap = new LinkedHashMap<String, Object>();
  entityMap.put("cat", Cat.class);
  entityMap.put("mother",Mother.class);
 

 
  catList = catRepository.queryNatively(
    "query1", "",entityParam);
 
  return catList;
 }
}

Monday, July 16, 2012

Inject SLF4J logger by annotation

Actually, the following class is not written by me. It is written my colleage. I know he is referencing from another post from Java Geeks. I found this is quite handy so I put it here as a reference.

The main idea is make use of the BeanPostProcessors interface. We first create an "LoggableInjector" class which implement the BeanPostProcessors interface.
This injector class gets the SLF4J logger and assign to the beans property after the bean creation by the Spring IOC continaer.

1. Create Loggable annotation
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.FIELD)  
@Documented  
public @interface Loggable {
 //for slf4j
}  

2. Create LoggableInjector class to add logger to the bean
@Component
public class LoggableInjector implements BeanPostProcessor {        
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {  
  return bean;  
 }  
      
 public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {  
  ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {  
   public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {  
    // make the field accessible if defined private  
    ReflectionUtils.makeAccessible(field);  
    if (field.getAnnotation(Loggable.class) != null) {
     Logger log = LoggerFactory.getLogger(bean.getClass());      
     field.set(bean, log);  
    }  
   }  
  });  
  return bean;  
 }  
} 

3. Usage Example

@Service
public class ReportServiceImpl {

 @Loggable
 private Logger logger;
 
}

Saturday, July 14, 2012

Adding Hibernate Entity Level Filtering feature to Spring Data JPA Repository

Those who have used data filtering features of hibernate should know that it is very powerful. You could define a set of filtering criteria to an entity class or a collection. Spring data JPA is a very handy library but it does not have fitering features. In this post, I will demonstarte how to add the hibernate filter features at entity level. We can just use annotation in your repositoy interface to enable this features.


Step 1. Define filter at entity level as usual. Just use hibernate @FilterDef annotation.


@Entity
@Table(name = "STUDENT")
@FilterDef(name="filterBySchoolAndClass", parameters={@ParamDef(name="school", type="string"),@ParamDef(name="class", type="integer")})
public class Student extends GenericEntity implements Serializable {
  ...
}


Step2. Define two custom annotations.

These two annotations are to be used in your repository interfaces. You could apply the hibernate filter defined in step 1 to specific query through these annotations.


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityFilter {
 FilterQuery[] filterQueries() default  {};
}

@Retention(RetentionPolicy.RUNTIME)
public @interface FilterQuery {
 String name()  default "";
 String jpql()  default "";
}



Step3. Add a method to your Spring data JPA base repository.

This method will read the annotation you defined (i.e. @FilterQuery) and apply hibernate filter to the query by just simply unwrap the EntityManager. You could specify the parameter in your hibernate filter and also the parameter in you query in this method.

If you do not know how to add custom method to your Spring data JPA base repository, please see my previous post for how to customize your Spring data JPA base repository for detail. You can see in previous post that I intentionally expose the repository interface (i.e. the springDataRepositoryInterface property) in the GenericRepositoryImpl. This small tricks enable me to access the annotation in the repository interface easily.


public List<T> doQueryWithFilter( String filterName, String filterQueryName, Map inFilterParams, Map inQueryParams){
    if (GenericRepository.class.isAssignableFrom(getSpringDataRepositoryInterface())) {
       Annotation entityFilterAnn = getSpringDataRepositoryInterface().getAnnotation(EntityFilter.class);
       if(entityFilterAnn != null){
        EntityFilter entityFilter = (EntityFilter)entityFilterAnn;
        FilterQuery[] filterQuerys  = entityFilter.filterQueries() ;
        for (FilterQuery fQuery : filterQuerys) { 
         if (StringUtils.equals(filterQueryName, fQuery.name())) {
          String jpql = fQuery.jpql();
          Filter filter = em.unwrap(Session.class).enableFilter(filterName);
          
          //set filter parameter
          for (Object key: inFilterParams.keySet()) {
           String filterParamName = key.toString();
           Object filterParamValue = inFilterParams.get(key);
           filter.setParameter(filterParamName, filterParamValue);
                }
          
          //set query parameter
          Query query= em.createQuery(jpql);
          for (Object key: inQueryParams.keySet()) {
           String queryParamName = key.toString();
           Object queryParamValue = inQueryParams.get(key);
           query.setParameter(queryParamName, queryParamValue);
                }
          return query.getResultList();
         }
        }
       }
      }
     }
     return null;
    }


Usage example:

In your repositry, define which query you would like to apply hibernate filter through your @EntityFilter and @FilterQuery annotation.

@EntityFilter (
 filterQueries = {
   @FilterQuery(name="query1", 
       jpql="SELECT s FROM Student LEFT JOIN FETCH s.Subject where s.subject = :subject" ),
   @FilterQuery(name="query2", 
       jpql="SELECT s FROM Student LEFT JOIN s.TeacherSubject where s.teacher =  :teacher")       
 }
)
public interface StudentRepository extends GenericRepository<Student, Long> {
}


In your service or business class that inject your repository, you could just simply call the doQueryWithFilter() method to enable the filtering function.


@Service
public class StudentService {

 @Inject
 private StudentRepository studentRepository;

 public List<Student> searchStudent( String subject, String school, String class) {
   
  List<Student> studentList;

  // Prepare parameters for query filter
  HashMap<String, Object> inFilterParams = new HashMap<String, Object>();
  inFilterParams.put("school", "Hong Kong Secondary School");
  inFilterParams.put("class", "S5");

  // Prepare parameters for query
  HashMap<String, Object> inParams = new HashMap<String, Object>();
  inParams.put("subject", "Physics");

  studentList = studentRepository.doQueryWithFilter(
    "filterBySchoolAndClass", "query1",
    inFilterParams, inParams);

  return studentList;
 }
}








Saturday, July 7, 2012

Customizing Spring Data JPA Repository

Spring Data is a very convenient library. However, as the project as quite new, it is not well featured. By default, Spring Data JPA will provide implementation of the DAO based on SimpleJpaRepository. In recent project, I have developed a customize repository base class so that I could add more features on it. You could add vendor specific features to this repository base class as you like.

Configuration
You have to add the following configuration to you spring beans configuration file. You have to specified a new repository factory class. We will develop the class later.


<jpa:repositories base-package="example.borislam.dao" 
factory-class="example.borislam.data.springData.DefaultRepositoryFactoryBean/>



Just develop an interface extending JpaRepository. You should remember to annotate it with @NoRepositoryBean.


@NoRepositoryBean
public interface GenericRepository <T, ID extends Serializable> 
 extends JpaRepository<T, ID> {    
}



Define Custom repository base implementation class
Next step is to develop the customized base repository class. You can see that I just one property (i.e. springDataRepositoryInterface) inside this customized base repository. I just want to get more control on the behaviour of the customized behaviour of the repository interface. I will show how to add more features of this base repository class in the next post.





import static org.springframework.data.jpa.repository.query.QueryUtils.toOrders;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.Filter;
import org.hibernate.Session;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.util.Assert;

@SuppressWarnings("unchecked")
@NoRepositoryBean
public class GenericRepositoryImpl<T, ID extends Serializable> 
 extends SimpleJpaRepository<T, ID>  implements GenericRepository<T, ID> , Serializable{
 
 private static final long serialVersionUID = 1L;

 static Logger logger = Logger.getLogger(GenericRepositoryImpl.class);
 
    private final JpaEntityInformation<T, ?> entityInformation;
    private final EntityManager em;
    private final DefaultPersistenceProvider provider;
     
    private  Class<?> springDataRepositoryInterface; 
 public Class<?> getSpringDataRepositoryInterface() {
  return springDataRepositoryInterface;
 }

 public void setSpringDataRepositoryInterface(
   Class<?> springDataRepositoryInterface) {
  this.springDataRepositoryInterface = springDataRepositoryInterface;
 }

 /**
     * Creates a new {@link SimpleJpaRepository} to manage objects of the given
     * {@link JpaEntityInformation}.
     * 
     * @param entityInformation
     * @param entityManager
     */
    public GenericRepositoryImpl (JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager , Class<?> springDataRepositoryInterface) {
     super(entityInformation, entityManager);
     this.entityInformation = entityInformation;
     this.em = entityManager;
     this.provider = DefaultPersistenceProvider.fromEntityManager(entityManager);
     this.springDataRepositoryInterface = springDataRepositoryInterface;
     }

    /**
     * Creates a new {@link SimpleJpaRepository} to manage objects of the given
     * domain type.
     * 
     * @param domainClass
     * @param em
     */
    public GenericRepositoryImpl(Class<T> domainClass, EntityManager em) {
        this(JpaEntityInformationSupport.getMetadata(domainClass, em), em, null);  
    }
 
    public <S extends T> S save(S entity)
    {     
        if (this.entityInformation.isNew(entity)) {
            this.em.persist(entity);
            flush();
            return entity;
          }
  entity = this.em.merge(entity);
  flush();
        return entity;
    }

   
    public T saveWithoutFlush(T entity)
    {
      return 
       super.save(entity);
    }
    
    public List<T> saveWithoutFlush(Iterable<? extends T> entities)
    {
     List<T> result = new ArrayList<T>();
  if (entities == null) {
   return result;
  }

  for (T entity : entities) {
   result.add(saveWithoutFlush(entity));
  }
  return result;
    }
} 




import static org.springframework.data.jpa.repository.utils.JpaClassUtils.isEntityManagerOfType;
import java.io.Serializable;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.spi.PersistenceProvider;

import org.hibernate.ejb.HibernateQuery;
import org.springframework.data.jpa.repository.query.QueryExtractor;

/**
* 
* @author Boris lam
* This class is use when you use  DefaultRepositoryFactory to override default repository factory class.
* 
*/

public enum DefaultPersistenceProvider implements QueryExtractor, Serializable{

/**
* Hibernate persistence provider.
*/
HIBERNATE("org.hibernate.ejb.HibernateEntityManager") {

public String extractQueryString(Query query) {

return ((HibernateQuery) query).getHibernateQuery()
.getQueryString();
}


/**
* Return custom placeholder ({@code *}) as Hibernate does create
* invalid queries for count queries for objects with compound keys.
* 
* @see HHH-4044
* @see HHH-3096
*/
@Override
protected String getCountQueryPlaceholder() {

return "*";
}
},

/**
* EclipseLink persistence provider.
*/
ECLIPSELINK("org.eclipse.persistence.jpa.JpaEntityManager") {

public String extractQueryString(Query query) {
//return ((JpaQuery<?>) query).getDatabaseQuery().getJPQLString();
return null;
}

},

/**
* OpenJpa persistence provider.
*/
OPEN_JPA("org.apache.openjpa.persistence.OpenJPAEntityManager") {

public String extractQueryString(Query query) {
//return ((OpenJPAQuery) query).getQueryString();
return null;
}
},

/**
* Unknown special provider. Use standard JPA.
*/
GENERIC_JPA("javax.persistence.EntityManager") {

public String extractQueryString(Query query) {

return null;
}


@Override
public boolean canExtractQuery() {

return false;
}
};

private String entityManagerClassName;


/**
* Creates a new {@link PersistenceProvider}.
* 
* @param entityManagerClassName the name of the provider specific
*            {@link EntityManager} implementation
*/
private DefaultPersistenceProvider(String entityManagerClassName) {

this.entityManagerClassName = entityManagerClassName;
}


/**
* Determines the {@link PersistenceProvider} from the given
* {@link EntityManager}. If no special one can be determined
* {@value #GENERIC_JPA} will be returned.
* 
* @param em
* @return
*/
public static DefaultPersistenceProvider fromEntityManager(EntityManager em) {

for (DefaultPersistenceProvider provider : values()) {
if (isEntityManagerOfType(em, provider.entityManagerClassName)) {
return provider;
}
}

return GENERIC_JPA;
}


/*
* (non-Javadoc)
* 
* @see
* org.springframework.data.jpa.repository.query.QueryExtractor#canExtractQuery
* ()
*/
public boolean canExtractQuery() {

return true;
}


/**
* Returns the placeholder to be used for simple count queries. Default
* implementation returns {@code *}.
* 
* @return
*/
protected String getCountQueryPlaceholder() {

return "x";
}

}



As a simple example here, I just override the default save method of the SimpleJPARepository. The default behaviour of the save method will not flush after persist. I modified to make it flush after persist. On the other hand, I add another method called saveWithoutFlush() to allow developer to call save the entity without flush. 

Define Custom repository factory bean
The last step is to create a factory bean class and factory class to produce repository based on your customized base repository class.



import java.io.Serializable;
import javax.persistence.EntityManager;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;

public class DefaultRepositoryFactoryBean <T extends JpaRepository<S, ID>, S, ID extends Serializable>
  extends JpaRepositoryFactoryBean<T, S, ID> {
    /**
     * Returns a {@link RepositoryFactorySupport}.
     * 
     * @param entityManager
     * @return
     */
    protected RepositoryFactorySupport createRepositoryFactory(
            EntityManager entityManager) {

        return new DefaultRepositoryFactory(entityManager);
    }
}


/**
 * 
 * The purpose of this class is to override the default behaviour of the spring JpaRepositoryFactory class.
 * It will produce a GenericRepositoryImpl object instead of SimpleJpaRepository. 
 * 
 */

import static org.springframework.data.querydsl.QueryDslUtils.QUERY_DSL_PRESENT;

import java.io.Serializable;
import javax.persistence.EntityManager;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.query.QueryExtractor;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.QueryDslJpaRepository;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.util.Assert;


public  class DefaultRepositoryFactory extends JpaRepositoryFactory{
    
 private final EntityManager entityManager;
    private final QueryExtractor extractor;


    public DefaultRepositoryFactory(EntityManager entityManager) {
     super(entityManager);
        Assert.notNull(entityManager);
        this.entityManager = entityManager;
        this.extractor = DefaultPersistenceProvider.fromEntityManager(entityManager);
    }
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected <T, ID extends Serializable> JpaRepository<?, ?> getTargetRepository(
            RepositoryMetadata metadata, EntityManager entityManager) {

        Class<?> repositoryInterface = metadata.getRepositoryInterface();
       
        JpaEntityInformation<?, Serializable> entityInformation =
                getEntityInformation(metadata.getDomainType());

        if (isQueryDslExecutor(repositoryInterface)) {
            return new QueryDslJpaRepository(entityInformation, entityManager);
        } else {
            return new GenericRepositoryImpl(entityInformation, entityManager, repositoryInterface); //custom implementation
        }
    }
 
    @Override
    protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {

        if (isQueryDslExecutor(metadata.getRepositoryInterface())) {
            return QueryDslJpaRepository.class;
        } else {
            return GenericRepositoryImpl.class;
        }
    }
    
    /**
     * Returns whether the given repository interface requires a QueryDsl
     * specific implementation to be chosen.
     * 
     * @param repositoryInterface
     * @return
     */
    private boolean isQueryDslExecutor(Class<?> repositoryInterface) {

        return QUERY_DSL_PRESENT
                && QueryDslPredicateExecutor.class
                        .isAssignableFrom(repositoryInterface);
    }   
}







Conclusion
You could now add more features to base repository class. In your program, you could now create your own repository interface extending GenericRepository instead of JpaRepository.


public interface MyRepository <T, ID extends Serializable>
  extends GenericRepository <T, ID> {
   void someCustomMethod(ID id);  
}


In next post, I will show you how to add hibernate filter features to this GenericRepository.


Friday, July 6, 2012

Welcome to Programming Peacefully!

I am an information technology professional from Hong Kong. In Hong Kong, it is so ridiculous that many IT professionals do not like programming. I had even heard that some analyst programmers said that they "hate" programming. Many of them will go to the area of project management and their technical skills may become outdated. 

I keep this blog just want to share some of my experience in software development and to exchange knowledge from technology lovers all over the world. If you think that my programme is written badly, please feel free to comment on it. I always want to get improvement on my technical skills :).

Apart from programming, I also love music. I like to play a musical instrument called "Ukulele". If you are interest in it, we can also share something about ukulele in this blog. :)