About Me

My photo
I'm project manager of a software development team at www.researchspace.com. I've been developing bioinformatics software for the last 9 years or so, and I'm hoping to share some of the experience and knowledge I've gained with Eclipse RCP, Java web app development and software development in general on these blogs.

Tuesday 4 November 2014

Running multithreaded JUnit tests with Apache Shiro

In this post I'm  going to explain how to run JUnit tests that simulate multiple users logged in simultaneously in a Java web application, using the Apache Shiro security library. Although the motivation for this is based on a specific use case of a current project, the test framework should be useful for anyone wanting to perform multithreaded unit tests with users authenticated in concurrent sessions -  perhaps to test resource contention, permissions, or locking.

Background

In a web project I'm working on just now, users can share and edit documents. Because of the potential for people overwriting other people's edits, or even deleting a document whilst someone else is working on it, we use a locking mechanism to ensure that only one person can edit the document at a time. The lock is acquired when someone starts editing, and is released when any of these conditions are true:
  • The user logs out.
  • The user session expires.
  • The user saves and closes the document.
This is a tricky scenario to develop automated tests for; the calculation on whether someone can edit or not depends on a whole range of factors, such as authorization permissions, the state of document itself (documents can be signed, for example, which prevents further edits), and whether someone else is editing it or not.

Our project uses the Apache Shiro  security library, a very versatile library that can be used in web and non-web projects, and has good support for testing. Up till now, though, all our tests ran in a single thread, with the result that only one user could be logged in at a time.
For our integration tests we needed to have:
  • Several users logged on simultaneously, simulating concurrent web sessions.
  • One user active at a time, whilst the other users wait.
  • Active session management, so that session expiration and  logout listeners would be triggered after session lifecycle events.

Solution

In this solution, we build on a mechanism discussed in a StackOverflow post about sequencing of threads. All the code discussed here is available on Github as a Maven project at https://github.com/otter606/shiro-multithread-junit. Just import into your IDE and run 

mvn test 

to run.

Setting up Shiro.

Shiro provides a TestUtils class in its documentation. Our unit test class can extend this, or include it as a helper class. In our example, for ease of explanation, we'll extend from it.
First of all, we'll just initialise a  regular SecurityManager, using a configuration file, shiro.ini at the root of the classpath - this is a standard approach to initialising Shiro.

 @BeforeClass  
 public static void readShiroConfiguration() {  
           Factory factory = new IniSecurityManagerFactory("classpath:shiro.ini");  
           SecurityManager securityManager = factory.getInstance();  
           SecurityUtils.setSecurityManager(securityManager);  
           log.setLevel(Level.INFO);  
      }  

The shiro.ini file is very simple, it just defines three users and their passwords:
 [users]  
 user1 = password1  
 user2 = password2  
 user3 = password3  

The code to login a user, and bind to a particular thread is boiler-plate code that we can put in a utility method. 

 private Subject doLogin(User user) {  
      Subject subjectUnderTest = new Subject.Builder(SecurityUtils.getSecurityManager())  
                     .buildSubject();  
      subjectUnderTest.login(new UsernamePasswordToken(user.getUsername(), user.getPassword()));  
      setSubject(subjectUnderTest);  
      return subjectUnderTest;  
 }  

So, at the moment we just have some code to login a User - useful, but nothing new. Calling this method multiple times in the same thread is likely to lead to strange results, so now we need to set up a mechanism to run multiple threads in JUnit, where each thread performs actions for a particular user, in a sequenced manner, so that the other threads pause when one thread is active. In this manner, we can test a use case like:
  1. User1 logs in and accesses a resource R for editing.
  2. User2 logs in and and also requests to access R, but is rejected, as user 1 holds a lock,
  3. User1 closes resource R (but remains logged in).
  4. User2 now successfully accesses R.
  5. User1 logs out.
  6. User2 logs out.

 Invokables and CountDown latches.

To run the test, we'll use a simple interface, Invokable, in which to define callback functions that perform a single step in the use case.
 public interface Invokable {  
   void invoke() throws Exception;  
 }  

And let's define an array of these - 6 long - to hold each action:
 Invokable[] invokables = new Invokable[6];  
 invokables[0] = new Invokable() {  
             // annotate these inner class methods with @Test  
             @Test  
     public void invoke() throws Exception {  
         log.info("logging in user1");  
         doLogin(u1);  
         log.info(" user1 doing some actions..");  
         log.info(" user1 pausing but still logged in.");  
             }  
         };  
 invokables[1] = new Invokable() {  
     public void invoke() throws Exception {  
         log.info("logging in user2");  
         doLogin(u2);  
         log.info(" user2 doing some actions..");  
         // some action  
         log.info(" user2 pausing but still logged in.");  
     }  
 };  
 //.. + 4 more Invokables defined for subsequent steps.

Now, we'll set up a mechanism to sequence the execution of these Invokables in different threads using a CountDown Latch mechanism. Here's how we'll call it:
 Map config = new TreeMap<>();  
 // these are the array indices of the Invokable [].  
 config.put("t1", new Integer[] { 0, 3 });  
 config.put("t2", new Integer[] { 1, 5, });  
 config.put("t3", new Integer[] { 2, 4 });  
 SequencedRunnableRunner runner = new SequencedRunnableRunner(config, invokables);  
 runner.runSequence();  

In the code above, we define that  we want to run 3 threads, and specify the indices of the Invokable [] that will run in each thread. I.e., we want Invokable[0] to run in thread t1, then invokable 1 to run in thread t2, etc.,

What happens under the hood in runSequence is as follows. We define an array of CountDownLatch objects. Each Invokable will wait on a CountDownLatch, and each latch will be counted down by the completion of its predecessor:

 public void runSequence() throws InterruptedException {  
 // Lock l = new ReentrantLock(true);  
 CountDownLatch[] conditions = new CountDownLatch[actions.length];  
 for (int i = 0; i < actions.length; i++) {  
 // each latch will be counted down by the action of its predecessor  
 conditions[i] = new CountDownLatch(1);  
 }  
 Thread[] threads = new Thread[nameToSequence.size()];  
 int i = 0;  
 for (String name : nameToSequence.keySet()) {  
    threads[i] = new Thread(new SequencedRunnable(name, conditions, actions,  
    nameToSequence.get(name)));  
    i++;  
 }  
 for (Thread t : threads) {  
     t.start();  
 }  
 try {  
 // tell the thread waiting for the first latch to wake up.  
     conditions[0].countDown();  
 } finally {  
   // l.unlock();  
 }  
 // wait for all threads to finish before leaving the test  
 for (Thread t : threads) {  
   t.join();  
 }  
 }  

In SequenceRunnable, we run an Invokable, and count down the next latch in the sequence:
 public void run() {  
   try {  
     for (int i = 0; i < sequence.length; i++) {  
       int toWaitForIndx = sequence[i];  
       try {  
         log.debug(name + ": waiting for event " + toWaitForIndx);  
         toWaitFor[toWaitForIndx].await();  
       } catch (InterruptedException e) {  
         e.printStackTrace();  
       }  
     log.debug(name + ": invoking action " + toWaitForIndx);  
     actions[toWaitForIndx].invoke();  
     if (toWaitForIndx < toWaitFor.length - 1) {  
       log.debug(name + "counting down for next latch " + (toWaitForIndx + 1));  
       toWaitFor[++toWaitForIndx].countDown();  
     } else  
       log.debug(name + " executed last invokable!");  
     }  
   } catch (Exception e) {  
     e.printStackTrace();  
   }  
 }  

That's it! Using this setup, we can login multiple users simultaneously in concurrent sessions, and perform actions for any user  in a guaranteed order, thus being able to test thoroughly functionality that is affected by resource contention or locking.

Conclusion

In this blog, I've described how you can combine Shiro and JUnit to develop realistic integration tests for functionality that is  affected by concurrency. Thanks for reading!

Wednesday 22 October 2014

Apache Shiro and Spring Boot

Spring Boot is a great way to get a Spring web application up and running, with many default settings to make the configuration of  standard functionality such as logging, view resolution, and database configuration as painless as possible.

It's also possible to add in SpringSecurity. However, since I've been using Apache Shiro for some time in other projects, and didn't particularly want to learn a new security library, I wanted to see if I could get it set up with  a Spring Boot application.

Basic setup

My environment is Java 7, Spring 4.0.5, using Shiro 1.2,  deploying  to a Servlet 3 container.

Differences between configuration described in current Shiro 1.2 documentation and Spring Boot.

 Boot encourages pure Java configuration, with no Spring XML files or even a web.xml file. So we need to declare all the beans needed for Shiro in a class annotated with Spring's @Configuration annotation.
Here is my class to set up Shiro:
 @Configuration  
 public class SecurityConfig {  
      @Bean()  
      public ShiroFilterFactoryBean shiroFilter (){  
           ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean ();  
           factory.setSecurityManager(securityManager());  
           factory.setLoginUrl("/ui/login");  
           factory.setSuccessUrl("/ui/listView");  
           factory.setUnauthorizedUrl("/ui/login");   
           factory.setFilterChainDefinitions(  
                 "/assets/scripts/**=anon\n"+  
                 "/license/**=anon\n"+  
                 "/manage/health/=anon\n"+  
              "/assets/static/*=authc\n"+  
                 "/manage/metrics/**=authc\n"+  
                 "/manage/beans/**=authc\n"+  
                 "/manage/trace/**=authc\n"+  
                 "/manage/mappings/**=authc\n"+  
                 "/manage/dump/**=authc\n"+  
                 "/manage/autoconfig/**=authc\n"+  
                 "/manage/env/**=authc\n"+  
                 "/manage/info/**=authc");  
           return factory;  
      }  
      @Bean  
      public SecurityManager securityManager() {  
           DefaultWebSecurityManager rc = new DefaultWebSecurityManager();  
           rc.setRealm(realm());  
           return rc;  
      }  
     @Bean public AuthorizingRealm realm() {  
           AuthorizingRealm realm = new AuthorizingRealm() {  
                @Override  
                protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)  
                          throws AuthenticationException {  
                     return new SimpleAuthenticationInfo("user", "password", "login");  
                }  
                @Override  
                protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
                     // TODO Auto-generated method stub  
                     return null;  
                }  
           };  
           realm.setName("login");  
           return realm;  
      }  
 }  

As you can see, you just need to set up 3 beans as a minimum, constructing a ShiroFilterFactoryBean, a SecurityManager and a Realm. In this example I've created a trivial Realm implementation, in practice you'll probably want to connect to a backend database to verify credentials. If you'r econfiguring a Realm that needs initialization, or want to add in any Spring Bean that implements the Initializable interface, you'll need to add in one more definition, e.g.,:

 @Bean  
 public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {  
     return new LifecycleBeanPostProcessor();  
 }  
  @Bean  
  @DependsOn("lifecycleBeanPostProcessor")  
  public TextConfigurationRealm realm() {  
     IniRealm realm = new IniRealm() ;  
     realm.setResourcePath("classpath:users.ini");      
     return realm;  
 }  


Instead of XML Configuration, we can just use setters in the classes to set property values.

SpringBoot can expose a set of URLs for monitoring and health checking. By default these are '/health', '/info' etc, but by setting a property in application.properties:
 management.context-path=/manage  

we can set these URLs with a prefix, and configure them to be authenticated. This is important as they give away a lot of sensitive information.

Customizing Shiro

For my application, I wanted to provide a custom filter that extended FormAuthenticationFilter .
So, I set a new @Bean definition to create my Filter. By giving it the name 'authc' it should replace the existing FormAuthenticationFilter  with my subclass.

In order to get this to work, it was crucial to define the filter after the ShiroFilterFactoryBean in the code. If it was defined first, then it seemed to prevent the correct behaviour of the FactoryBean to produce SpringShiroFilter instances. 
But, by defining after the Factory bean, everything works properly. The reason I'm stressing this point is that in the old  standard XML configuration, the order didn't seem to matter.
 management.context-path=/managepackage com.researchspace.licenseserver;  
 import java.util.HashMap;  
 import java.util.Map;  
 import javax.servlet.Filter;  
 import org.apache.shiro.authc.AuthenticationException;  
 import org.apache.shiro.authc.AuthenticationInfo;  
 import org.apache.shiro.authc.AuthenticationToken;  
 import org.apache.shiro.authc.SimpleAuthenticationInfo;  
 import org.apache.shiro.authz.AuthorizationInfo;  
 import org.apache.shiro.mgt.SecurityManager;  
 import org.apache.shiro.realm.AuthorizingRealm;  
 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;  
 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;  
 import org.apache.shiro.subject.PrincipalCollection;  
 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;  
 import org.springframework.context.annotation.Bean;  
 import org.springframework.context.annotation.Configuration;  
 import com.researchspace.licenseserver.controller.ShiroFormFilterExt;  
 @Configuration  
 public class SecurityConfig {  
      @Bean()  
      public ShiroFilterFactoryBean shiroFilter (){  
           ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean ();  
           factory.setSecurityManager(securityManager());  
           factory.setLoginUrl("/ui/login");  
           factory.setSuccessUrl("/ui/listView");  
           factory.setUnauthorizedUrl("/ui/login");  
           // this is ordered, better to do like this.  
           factory.setFilterChainDefinitions(  
                 "/assets/scripts/**=anon\n"+  
                 "/license/**=anon\n"+  
                 "/manage/health/=anon\n"+  
              "/assets/static/*=authc\n"+  
                 "/manage/metrics/**=authc\n"+  
                 "/manage/beans/**=authc\n"+  
                 "/manage/trace/**=authc\n"+  
                 "/manage/mappings/**=authc\n"+  
                 "/manage/dump/**=authc\n"+  
                 "/manage/autoconfig/**=authc\n"+  
                 "/manage/env/**=authc\n"+  
                 "/manage/info/**=authc");  
           Map<String,Filter> filters= new HashMap<>();  
           filters.put("authc", authc());  
           factory.setFilters(filters);  
           return factory;  
      }  
      @Bean  
      public SecurityManager securityManager() {  
           DefaultWebSecurityManager rc = new DefaultWebSecurityManager();  
           rc.setRealm(realm());  
           return rc;  
      }  
      @Bean public AuthorizingRealm realm() {  
           AuthorizingRealm realm = new AuthorizingRealm() {  
                @Override  
                protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)  
                          throws AuthenticationException {  
                     return new SimpleAuthenticationInfo("user", "password", "login");  
                }  
                @Override  
                protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
                     // TODO Auto-generated method stub  
                     return null;  
                }  
           };  
           realm.setName("login");  
           return realm;  
      }  
      @Bean(name="authc") // this must be AFTER the factory bean definition 
      public ShiroFormFilterExt authc(){  
           return new ShiroFormFilterExt();  
      }  
 }  

I hope other people trying to use Shiro with SpringBoot will find this useful.