Captcha Buffering System

Interest of a buffering system

A captcha (picture or sound) needs a lot of computing time comparing to the time usually required to generate a web page. The more captchas are hard to crack by computers, the more it takes time to create.

Conceptual solution

Pre generating captcha can prevent web site overloading. Considering the need, there is two possible solutions:

  • Captchas can be stored in volatile memory to prevent overload, this solution fits well for quick response time but require lot of RAM.
  • Captchas can be stored in persistent memory, like database, or simple files, this solution fits well for large volume of captcha.

The JCaptcha buffering system implements the two solutions with

  • scheduled captcha swapping : captcha can be automaticaly moved between buffer
  • scheduled buffer feeding : automatic captcha generation

Technical solution

A component, BufferedEngineContainer, which implements the CaptchaEngine interface, produces captchas through a normal captcha engine.

It uses two buffers; one to store captchas in a persistent way, implementation of this buffer is either a file buffer on disk, or a database. Another buffer is used to quickly deliver captchas; this is implemented with a RAM buffer, like a stack, or a list.

The BufferedEngineContainer component has two main operations:

  • Feeding of the persistent buffer.
  • Swapping from the persistent buffer to the volatile buffer.

These operations are controlled by timing components. JCaptcha provide implementations of timing components :

  • A simple one with ClockDaemon API.
  • A much richer one with the Quartz API.

At least but not last, a management component provided real time control on the Quartz implementation:

  • Defining the max size of both buffers
  • Defining the number of captchas to feed the persistent buffer
  • Defining the number of captcha to be swapped between the two buffers.
  • Defining the time of feeding operations
  • Defining the time of swapping operations
  • Defining the ratio of Localized captchas (For instance: 25% of English, 75% of French ...)
  • Stopping and starting every different operation

Setting the Captcha buffering system in Spring

The captcha buffering system can be configured the same way as Captcha Engines, but it can be less explicit as not all components are part of JCaptcha.

Configuration samples

Some Spring configuration files are available as testing resources

The first root component is the BufferedEngineContainer, which feed a persistent buffer and swap captchas from this buffer to a volatile buffer. So to build this component you need :

  • a captcha engine, well known
  • a persistent buffer, where captchas are store from mass production
  • a volatile buffer, where captcha are stored just before been used
  • a configuration, to set some parameters like buffers limits, feed and swap size ...
<bean class="com.octo.captcha.engine.bufferedengine.QuartzBufferedEngineContainer" id="imageContainer" singleton="true">

	<constructor-arg type="com.octo.captcha.engine.CaptchaEngine" index="0"><ref bean="imageEngine"/></constructor-arg>

	<constructor-arg type="com.octo.captcha.engine.bufferedengine.buffer.CaptchaBuffer" index="1"><ref bean="imageMemBuffer"/></constructor-arg>

	<constructor-arg type="com.octo.captcha.engine.bufferedengine.buffer.CaptchaBuffer" index="2"><ref bean="imagePersistantBuffer"/></constructor-arg>

	<constructor-arg type="com.octo.captcha.engine.bufferedengine.ContainerConfiguration" index="3"><ref bean="imageConfiguration"/></constructor-arg>

</bean>

The ContainerConfiguration sets:

  1. The map of ratio corresponding to locales
  2. The maximal size of the volatile buffer
  3. The maximal size of the persistent buffer
  4. The size of the swap
  5. The size of the feed
  6. The Boolean to know if the container only serves configured locales
  7. The Default local to use
    <bean id="imageConfiguration" class="com.octo.captcha.engine.bufferedengine.ContainerConfiguration">
    
    	<constructor-arg index="0">
    		<map>
    			<entry key-ref="java.util.Locale.FRENCH" value-ref="ratioFR"/>
    		</map>
    	</constructor-arg>
    	<\!--memory size-->
    	<constructor-arg index="1"><value>1000</value></constructor-arg>
    	<\!--persistent size-->
    	<constructor-arg index="2"><value>100000</value></constructor-arg>
    	<\!--swap size-->
    	<constructor-arg index="3"><value>1000</value></constructor-arg>
    	<\!--feed size-->
    	<constructor-arg index="4"><value>100000</value></constructor-arg>
    	<\!--feed batch size-->
    	<constructor-arg index="5"><value>100</value></constructor-arg>
    	<\!--Serve only configured locales?-->
    	<constructor-arg index="6"><value>true</value></constructor-arg>
    	<\!--default locale to serve-->
    	<constructor-arg index="7"><ref bean="java.util.Locale.FRENCH"/></constructor-arg>
    
    </bean>
    
    And the declaration of the map of ratio corresponding to locales uses :
    <bean id="java.util.Locale.FRENCH" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />
    
    <bean id="ratioFR" class="java.lang.Double">
    	<constructor-arg index="0"><value>1.0</value></constructor-arg>
    </bean>
    
    Next we define buffers, the volatile buffer is simple:
    <bean class="com.octo.captcha.engine.bufferedengine.buffer.MemoryCaptchaBuffer" id="imageMemBuffer">
    </bean>
    
    The persistant buffer can be more complex, the following sample use a database implementation, with a data source:
<bean class="com.octo.captcha.engine.bufferedengine.buffer.DatabaseCaptchaBuffer" id="imagePersistantBuffer">
	<constructor-arg index="0"><ref bean="dataSource"/></constructor-arg>

	<!--table name-->
	<constructor-arg type="java.lang.String" index="1"><value>jcaptcha_t</value></constructor-arg>
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" singleton="true">
	<property name="driverClassName">
		<value>org.hsqldb.jdbcDriver</value>
	</property>
	<property name="url">
		<value>jdbc:hsqldb:jcaptchaDB</value>
	</property>
	<property name="username">
		<value>jcaptcha</value>
	</property>
	<property name="password">
		<value>jcaptcha</value>
	</property>
</bean>

The next part is about Quartz, the scheduling framework.
We first define a Scheduler, which:

  • Start at start up of the container (tomcat/App server...)
  • Complete a job in process when it shut down, meaning it doesn't "cut" a process
  • Use 2 triggers for the feeding operation and for the swapping operation
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean" id="imageScheduler">
    
    <property name="autoStartup">
    	<value>true</value>
    </property>
    
    <property name="waitForJobsToCompleteOnShutdown">
    	<value>true</value>
    </property>
    
    <property name="triggers">
    	<list>
    		<ref local="imageCronTriggerFeeder" />
    		<ref local="imageCronTriggerSwapper" />
    	</list>
    </property>
    
    </bean>
    
    Each trigger are cron type, which means there use a Cron expression to define when operations have to be launch, (see Quartz documentation). These tow trigger use a JobDetail, because, after telling to the scheduler when to execute, we need to tell what.
    <bean id="imageCronTriggerFeeder" class="org.springframework.scheduling.quartz.CronTriggerBean">
    
    <property name="jobDetail">
    	<ref bean="imageFeederJobDetail" />
    </property>
    
    <property name="cronExpression">
    	<value>0 * * * * ?</value>
    </property>
    
    </bean>
    
    <bean id="imageCronTriggerSwapper" class="org.springframework.scheduling.quartz.CronTriggerBean">
    
    <property name="jobDetail">
    	<ref bean="imageSwapperJobDetail" />
    </property>
    
    <property name="cronExpression">
    	<value>0/1 * * * * ?</value>
    </property>
    
    </bean>
    
    As Quartz is fully intergrated in Spring, they have design a very usefull JobDetail, which launch a particular method of a bean. So both JobDetail are from this particular kind. You have to set three properties :
  • targetObject, the component which contains the method
  • targetMethod, the target method
  • concurrent, Boolean to know if two job of the same kind can happen at the same time.
    <bean id="imageFeederJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    	<property name="targetObject"><ref bean="imageContainer"/></property>
    	<property name="targetMethod"><value>feedPersistentBuffer</value></property>
    	<property name="concurrent"><value>false</value></property>
    </bean>
    
    <!-- Constructs job detail, which invoke a precise methode from a bean.-->
    <bean id="imageSwapperJobDetail"
    			class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    	<property name="targetObject"><ref bean="imageContainer"/></property>
    	<property name="targetMethod"><value>swapCaptchasFromPersistentToVolatileMemory</value></property>
    	<property name="concurrent"><value>false</value></property>
    </bean>
    
    A transverse, but very important, component is the ContainerManager, allowing real time management of the buffering system. The component can be exposed through JMX. It control most of components set in the upper part.
    <bean
    		class="com.octo.captcha.engine.bufferedengine.manager.QuartzBufferedEngineManager" id="imageContainerManager">
    	<constructor-arg type="com.octo.captcha.engine.bufferedengine.QuartzBufferedEngineContainer" index="0"><ref bean="imageContainer"/></constructor-arg>
    	<constructor-arg type="org.quartz.Scheduler" index="1"><ref bean="imageScheduler"/></constructor-arg>
    	<constructor-arg type="org.quartz.CronTrigger" index="2"><ref bean="imageCronTriggerFeeder"/></constructor-arg>
    	<constructor-arg type="org.quartz.CronTrigger" index="3"><ref bean="imageCronTriggerSwapper"/></constructor-arg>
    	<constructor-arg type="org.quartz.JobDetail" index="4"><ref bean="imageFeederJobDetail"/></constructor-arg>
    	<constructor-arg type="org.quartz.JobDetail" index="5"><ref bean="imageSwapperJobDetail"/></constructor-arg>
    </bean>