org.chenillekit.tapestry.core.components.Kaptcha.java Source code

Java tutorial

Introduction

Here is the source code for org.chenillekit.tapestry.core.components.Kaptcha.java

Source

/*
 * Apache License
 * Version 2.0, January 2004
 * http://www.apache.org/licenses/
 *
 * Copyright 2008-2010 by chenillekit.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 */

package org.chenillekit.tapestry.core.components;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.net.BCodec;
import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.StreamResponse;
import org.apache.tapestry5.annotations.OnEvent;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.Persist;
import org.apache.tapestry5.corelib.base.AbstractField;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.services.Request;

import org.chenillekit.image.services.CaptchaProducer;
import org.chenillekit.tapestry.core.utils.JPEGInline;

/**
 * A Captcha is a type of challenge-response test used in computing to ensure that the response is not generated by a computer.
 * The process usually involves one computer (a server) asking a user to complete a simple test which the computer is able to
 * generate and grade. Because other computers are unable to solve the CAPTCHA, any user entering a correct solution is presumed
 * to be human. Thus, it is sometimes described as a reverse Turing test, because it is administered by a machine and targeted to
 * a human, in contrast to the standard Turing test that is typically administered by a human and targeted to a machine. A common
 * type of CAPTCHA requires that the user type letters or digits from a distorted image that appears on the screen.
 * <p/>
 * This component based on <a href="http://code.google.com/p/kaptcha/">kaptcha library</a> and produce following HTML code:
 * <p/>
 * <pre>
 * &lt;span id="kaptcha1" class="ck-kaptcha"&gt;
 *   &lt;img id="kaptcha1_kaptcha" class="ck-kaptcha" src="..."/&gt;
 *   &lt;input id="kaptcha1_input" class="ck-kaptcha" type="text" name="kaptcha1"/&gt;
 * &lt;/span&gt;
 * </pre>
 * <p/>
 * so you can change the design by cascading style sheets by the "ck-kaptcha" class.
 * <br/>
 * To use this component, you need the <a href="http://www.chenillekit.org/chenillekit-image/index.html">chenillekit-image library</a> in you classpath.
 *
 * @version $Id$
 */
public class Kaptcha extends AbstractField {
    private static final String EVENT_NAME = "kaptchaEvent";

    @Parameter(required = true, defaultPrefix = BindingConstants.PROP)
    private boolean value;

    @Persist
    private String kaptchaValue;

    private String textFieldValue;

    /**
     * ComponentResources. For blocks, messages, crete actionlink, trigger event
     */
    @Inject
    private ComponentResources resources;

    /**
     * Request object for information on current request
     */
    @Inject
    private Request request;

    @Inject
    private CaptchaProducer kaptchaProducer;

    /**
     * Tapestry render phase method.
     * Start a tag here, end it in afterRender
     */
    void beginRender(MarkupWriter writer) {
        writer.element("span", "id", getClientId(), "class", "ck-kaptcha");

        writer.element("img", "id", String.format("%s_kaptcha", getClientId()), "src", getImageLink(), "class",
                "ck-kaptcha");
        writer.end(); //  img

        // <input t:id="textField" type="text" class="ck-kaptcha-input"/>

        writer.element("input", "id", String.format("%s_input", getClientId()), "type", "text", "name",
                getControlName(), "value", textFieldValue, "class", "ck-kaptcha");
        writer.end(); //  input
    }

    /**
     * Tapestry render phase method. End a tag here.
     */
    void afterRender(MarkupWriter writer) {
        writer.end(); //  span
    }

    private Link getImageLink() {
        BCodec bCodec = new BCodec();
        try {
            return resources.createEventLink(EVENT_NAME, bCodec.encode(kaptchaProducer.createText()));
        } catch (EncoderException e) {
            throw new RuntimeException(e);
        }
    }

    @OnEvent(value = EVENT_NAME)
    public StreamResponse onKaptchaImage(String kaptchaValue) {
        BCodec bCodec = new BCodec();
        try {
            this.kaptchaValue = bCodec.decode(kaptchaValue);
        } catch (DecoderException e) {
            throw new RuntimeException(e);
        }
        BufferedImage kapatchImage = kaptchaProducer.createImage(this.kaptchaValue);

        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ImageIO.write(kapatchImage, "jpg", byteArrayOutputStream);
            ByteArrayInputStream bais = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            return new JPEGInline(bais, (String[]) null);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Method implemented by subclasses to actually do the work of processing the submission of the form. The element's
     * elementName property will already have been set. This method is only invoked if the field is <strong>not {@link
     * #isDisabled() disabled}</strong>.
     *
     * @param elementName the name of the element (used to find the correct parameter in the request)
     */
    protected void processSubmission(String elementName) {
        String rawValue = request.getParameter(elementName);
        value = this.kaptchaValue.equals(rawValue);
    }
}