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.
Pre generating captcha can prevent web site overloading. Considering the need, there is two possible solutions:
The JCaptcha buffering system implements the two solutions with
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:
These operations are controlled by timing components. JCaptcha provide implementations of timing components :
At least but not last, a management component provided real time control on the Quartz implementation:
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.
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 :
<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:
<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> |
<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> |
<bean class="com.octo.captcha.engine.bufferedengine.buffer.MemoryCaptchaBuffer" id="imageMemBuffer"> </bean> |
<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:
<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> |
<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> |
<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> |
<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> |