Wednesday, December 29, 2010

Work with sound jCaptcha in servlet, Part 2 - Customize Voice class in FreeTTS

I created a sound jCaptcha with FreeTTS in part 1. Now I need to add the sound captcha in a web application. There were three pieces to accomplish that.

Firstly, create a servlet.
public class AudioCaptchaServlet extends HttpServlet {

/**
* Serial uid.
*/
private static final long serialVersionUID = 1L;

/**
* Logger.
*/
private static Log logger = LogFactory.getLog(AudioCaptchaServlet.class);

private static final SoundCaptchaService AUDIO_CAPTCHA_SERVICE =
AudioCaptchaService.getInstance();

/**
* Init.
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
super.init(servletConfig);
logger.info("Initalizing AudioCaptchaServlet...");
}

/**
*
*/
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {

logger.info("Generating sound captcha for session id "
+ request.getSession().getId());

AudioInputStream challenge = null;
try {
// get the session id that will identify the generated captcha.
// the same id must be used to validate the response, the session id
// is a good candidate!
String captchaId = request.getSession().getId();

// call the AudioCaptchaService getChallenge method
challenge = AUDIO_CAPTCHA_SERVICE.getSoundChallengeForID(captchaId);
} catch (IllegalArgumentException e) {
logger.error("Illegal argument exception occured.", e);
throw new RuntimeException(e);
} catch (CaptchaServiceException e) {
logger.error("Captcha service exception occured.", e);
throw new RuntimeException(e);
} catch (Exception e) {
logger.error("An exception occured.", e);
throw new RuntimeException(e);
}

response.setDateHeader("Expires", 0);
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
response.addHeader("Cache-Control", "post-check=0, pre-check=0");
response.setHeader("Pragma", "no-cache");
response.setContentType("audio/wav");

ServletOutputStream out = response.getOutputStream();
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
AudioSystem.write(challenge,
javax.sound.sampled.AudioFileFormat.Type.WAVE,
byteOutputStream);
out.write(byteOutputStream.toByteArray());
out.flush();
out.close();

logger.info("Generating sound captcha completed");
}

}


Secondly, add the servlet configuration in web.xml.
    <servlet>
<servlet-name>Audio Captcha Servlet</servlet-name>
<servlet-class>myapp.servlet.AudioCaptchaServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>Audio Captcha Servlet</servlet-name>
<url-pattern>/jcaptcha.wav</url-pattern>
</servlet-mapping>


Thirdly, add the sound captcha in jsp.
<a href="myapp/jcaptcha.wav"><img src="myapp/resources/images/speaker1.gif" alt="Sound Captcha" /></a>


Everything seemed to be working together nicely so far. I was able to see the sound captcha icon once the web application is deployed. However, when I clicked the icon, it did not play the sound and an error message was found in the log.
Trouble while processing utterance java.lang.IllegalStateException: Syllable relation has already been set.


I googled around and felt lucky to found this page. I downloaded FreeTTS 1.2.2 source release, updated Voice.java as below, and rebuilt the binaries using maven.
    public void deallocate() {
setLoaded(false);

//TODO consider removing utteranceProcessors.clear();
utteranceProcessors.clear();

if (!externalAudioPlayer) {
if (audioPlayer != null) {
audioPlayer.close();
audioPlayer = null;
}
}

if (!externalOutputQueue) {
//TODO Consider removing comment
//outputQueue.close();
}
}


Once I replaced FreeTTS with the new binary in the web app, everything worked as expected.

In retrospect, it was still a mystery why I had to customize Voice.java to make FreeTTS work sound jCaptcha in servlet. In addition, I was not a big fan that there is no out-of-box support for image and sound captcha to use the same characters. But I was happy that I managed to get jCaptcha work to meet the requirements. This was quite a learning experience.

4 comments:

  1. When trying to work with your code I got an error on this line:

    private static final SoundCaptchaService AUDIO_CAPTCHA_SERVICE = AudioCaptchaService.getInstance();

    How did you define that singleton class?

    ReplyDelete
    Replies
    1. hey did u solved tht problem .. thank you in advance.. if posible share the code

      Delete
  2. Hi, can you give me FreeTTS.jar modified. Thank you so much!

    ReplyDelete
  3. It's not work on firefox and IE. It say the same thing?

    ReplyDelete