5 minutes with – Spring Object Pooling

Sometimes an object instance is not enough; specially, when you use a stateful classes on concurrency environment runtime, a pool of object is a good solution to achieve a multithreaded executions.

In this article, I’ll show you how to set up this configuration with Spring.

Singleton objects are common in the solution, specially in web solution, when stateless objects are the majority of which instanced.

This is the best solution, specially when you’ve to use class with @Controller,@Service or @Repository annotation.

However, sometimes objects cannot be singleton. For example, objects which purpose is to connecting resource (such database or jms queue), take advantage of object pooling creating a list of object that are used and released by the application.

At this point, the right question should be: Why should I use an object pool instead of instantiating the object when I need it?
Simply, the reason is, for some types of objects, the construction operations is a rather time expensive process.

Let’s see some code. I’ve an object stateful which reads all the file under a directory storing all the file’s name which are used for future directory search.


public class FileUtil {

  final static Logger log = Logger.getLogger("GTWLOGGER");
  
  private List<String> files;
  
  public FileUtil(){
    log.info("Object Created:" + this.hashCode());
  }
  
  public void listFilesForFolder(final File folder) {
    for (File fileEntry : folder.listFiles()) {
      if (fileEntry.isDirectory()) {
        listFilesForFolder(fileEntry);
      } else {
        files.add(fileEntry.getName());
      }
    }
  }
  
  public void initialize(String rootFolder)
  {
    files = new ArrayList<String>();
    
    File folder = new File(rootFolder);
    listFilesForFolder(folder);
  }
  
  public List<String> getFileByName(String fileName)
  {
    List<String> result = new ArrayList<String>();
    for(String file : files)
    {
      if (file.contains(fileName))
      result.add(file);
    }
    return result;
  }
}

The initialize method fills the file List and the getFileByName get the file found in the directories by giving a string to look into the name.

The configuration of the pool looks like that:


<!-- Object Pool -->
<bean id="fileUtil" class="it.blog.springobjectpool.FileUtil" scope="prototype" />

<bean id="poolTargetSourceFileUtil" class="it.blog.springobjectpool.FileUtilObjectPool" 
init-method="initializeObjects" >
  <property name="targetBeanName" value="fileUtil" />
  <property name="targetClass" value="it.blog.springobjectpool.FileUtil" />
  <property name="maxSize" value="8" />
  <property name="minIdle" value="3" />
  <!-- 10 seconds -->
  <property name="maxWait" value="10000" />
  <property name="rootFolder" value="/home/myfiles" />
</bean>


The class FileUtilObjectPool extends the class org.springframework.aop.target.CommonsPool2TargetSource in order to build the object pool.




public class FileUtilObjectPool extends CommonsPool2TargetSource {

  private String rootFolder;
  
  public void initializeObjects() throws Exception {
  
  List<FileUtil> pool = new ArrayList<FileUtil>();
  
  for(int i = 0; i < this.getMinIdle(); i++) {
    pool.add((FileUtil)this.getTarget());
  }
  for(FileUtil instance : pool) {
    /*
    * Initialize Object
    */
    instance.initialize(rootFolder);
    
    this.releaseTarget(instance);
  }
  pool.clear();
  }
  public String getRootFolder() {
    return rootFolder;
  }
  public void setRootFolder(String rootFolder) {
    this.rootFolder = rootFolder;
  }
}



The highlight row evidences the object initialization called when the pool is started up (Thanks to Andrei for this post).

The controller object is a very plain @RestController.

@RequestMapping(value = "/file", method = { RequestMethod.GET, RequestMethod.POST })
public List<String> file(@RequestParam("filename") String filename) {
  try {
    /*
    * Return Object from Object Pool
    */
    FileUtil simpleBean = (FileUtil)poolTargetSourceFileUtil.getTarget();
    log.info("Object Hashcode:" + simpleBean.hashCode());
    /*
    * Look for the file name contained
    */
    List<String> result = simpleBean.getFileByName(filename);
    log.info("Found:" + result);
    /*
    * Release the object
    */
    poolTargetSourceFileUtil.releaseTarget(simpleBean);
    
    return result;
  } catch (Exception e) {
    log.error("file", e);
    return null;
  }
}

Once deployed and invoked the Rest service, it shows this log:


2017-02-16 16:07:32 it.blog.springobjectpool.FileUtil <init> [main] Object Created:1434615677
2017-02-16 16:07:32 it.blog.springobjectpool.FileUtil <init> [main] Object Created:1815978141
2017-02-16 16:07:32 it.blog.springobjectpool.FileUtil <init> [main] Object Created:1107024623
2017-02-16 16:07:33 it.blog.springobjectpool.GatewayController file [Thread-3] Object Hashcode:1434615677
2017-02-16 16:07:33 it.blog.springobjectpool.GatewayController file [Thread-3] Found:[CaptchaEngine.class, GimpyEngine.class]

Three objects are created and one is used to serve the Rest service.

It can be useful to test it using JUnit test. I haven’t found a good way to run test in parallel, so I applied Michael Sokolov’s solution (here).



@RunWith(MultiThreadedRunner.class)
public class FileUtilTest {
	
	static ApplicationContext context;
		
	@BeforeClass
	public static void setup()
	{
	 context = 
    new ClassPathXmlApplicationContext("file:src/main/webapp/WEB-INF/springobjectpool-servlet.xml");		
	}
		
	@Test 
	public void test1()
	{		
		GatewayController controller = 
      (GatewayController)context.getBean(GatewayController.class);
		
		List<String> result = controller.file("messages.properties");

		assertNotNull(result);
	}
	
	@Test 
	public void test2()
	{		
		GatewayController controller = 
      (GatewayController)context.getBean(GatewayController.class);
		
		List<String> result = controller.file("MobileAction");

		assertNotNull(result);
	}

	@Test 
	public void test3()
	{		
		GatewayController controller = 
      (GatewayController)context.getBean(GatewayController.class);
		
		List<String> result = controller.file("Engine");

		assertNotNull(result);
	}
}

The JUnit test runs three test in parallel, so we can appreciate the object pool in the log file.


2017-02-16 16:07:32 it.blog.springobjectpool.FileUtil <init> [main] Object Created:1434615677
2017-02-16 16:07:32 it.blog.springobjectpool.FileUtil <init> [main] Object Created:1815978141
2017-02-16 16:07:32 it.blog.springobjectpool.FileUtil <init> [main] Object Created:1107024623
2017-02-16 16:07:33 it.blog.springobjectpool.GatewayController file [Thread-2] Object Hashcode:1107024623
2017-02-16 16:07:33 it.blog.springobjectpool.GatewayController file [Thread-3] Object Hashcode:1434615677
2017-02-16 16:07:33 it.blog.springobjectpool.GatewayController file [Thread-1] Object Hashcode:1815978141
2017-02-16 16:07:33 it.blog.springobjectpool.GatewayController file [Thread-3] Found:[CaptchaEngine.class, GimpyEngine.class]
2017-02-16 16:07:33 it.blog.springobjectpool.GatewayController file [Thread-2] Found:[InsertNickMobileAction.class, ModifyNickMobileAction.class]
2017-02-16 16:07:33 it.blog.springobjectpool.GatewayController file [Thread-1] Found:[error.messages.properties, messages.properties]

I have to be honest. I’m not very keen on object pooling. I find it not trivial the managing and tunning to maximize the performance.
I would use it only in very little cases and after a very accurate problem analysis.

Source code on gitHub (https://github.com/MarcoGhise/SpringObjectPool).

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s