com.octo.captcha.service.EhcacheManageableCaptchaService.java Source code

Java tutorial

Introduction

Here is the source code for com.octo.captcha.service.EhcacheManageableCaptchaService.java

Source

/*
 * JCaptcha, the open source java framework for captcha definition and integration
 * Copyright (c)  2007 jcaptcha.net. All Rights Reserved.
 * See the LICENSE.txt file distributed with this package.
 */

package com.octo.captcha.service;

import com.octo.captcha.Captcha;
import com.octo.captcha.service.captchastore.EhcacheCaptchaStore;
import com.octo.captcha.service.captchastore.MapCaptchaStore;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;

/**
 * This class provides an implementation for the ehcache enhanced management interface. It uses the self managed cache
 * ehcache as CaptchaStore
 *
 * @author <a href="mailto:mag@jcaptcha.net">Marc-Antoine Garrigue</a>
 * @version 1.0
 * @deprecated
 */
public abstract class EhcacheManageableCaptchaService extends AbstractCaptchaService
        implements EhcacheManageableCaptchaServiceMBean {

    private static Log log = LogFactory.getLog(EhcacheManageableCaptchaService.class);
    private CacheManager cacheManager;
    private int minGuarantedStorageDelayInSeconds;

    private int captchaStoreMaxSize;

    private int numberOfGeneratedCaptchas = 0;
    private int numberOfCorrectResponse = 0;
    private int numberOfUncorrectResponse = 0;
    public static final String CACHE_NAME_PREFIX = "jcaptcha.store.";
    public static final String DEFAULT_CACHE_NAME = "default";

    public String captchaStoreCacheName;

    protected EhcacheManageableCaptchaService(com.octo.captcha.engine.CaptchaEngine captchaEngine,
            int minGuarantedStorageDelayInSeconds, int maxCaptchaStoreSize) {
        this(captchaEngine, minGuarantedStorageDelayInSeconds, maxCaptchaStoreSize, DEFAULT_CACHE_NAME);
    }

    protected EhcacheManageableCaptchaService(com.octo.captcha.engine.CaptchaEngine captchaEngine,
            int minGuarantedStorageDelayInSeconds, int maxCaptchaStoreSize, String cacheName) {
        //just to compile : call super first
        super(new MapCaptchaStore(), captchaEngine);

        //if name is null, use the default one
        if (cacheName == null || "".equals(cacheName)) {
            cacheName = DEFAULT_CACHE_NAME;
        }
        //set the cache name
        captchaStoreCacheName = CACHE_NAME_PREFIX + cacheName;

        //creates the manager
        try {
            this.cacheManager = CacheManager.getInstance();
        } catch (CacheException e) {
            log.error(e);
        }
        // create a cache with overflow on disk,
        Cache cache = new Cache(captchaStoreCacheName, maxCaptchaStoreSize, true, false,
                minGuarantedStorageDelayInSeconds, minGuarantedStorageDelayInSeconds);
        //store the cache

        try {
            if (cacheManager.cacheExists(captchaStoreCacheName)) {
                cacheManager.removeCache(captchaStoreCacheName);
            }
            cacheManager.addCache(cache);
        } catch (CacheException e) {
            log.error(e);
        }
        //change the super store
        super.store = new EhcacheCaptchaStore(cache);

        this.captchaStoreMaxSize = maxCaptchaStoreSize;
        this.minGuarantedStorageDelayInSeconds = minGuarantedStorageDelayInSeconds;

    }

    /**
     * Get the fully qualified class name of the concrete CaptchaEngine used by the service.
     *
     * @return the fully qualified class name of the concrete CaptchaEngine used by the service.
     */
    public String getCaptchaEngineClass() {
        return this.engine.getClass().getName();
    }

    /**
     * Set the fully qualified class name of the concrete CaptchaEngine used by the service
     *
     * @param theClassName the fully qualified class name of the CaptchaEngine used by the service
     *
     * @throws IllegalArgumentException if className can't be used as the service CaptchaEngine, either because it can't
     *                                  be instanciated by the service or it is not a ImageCaptchaEngine concrete
     *                                  class.
     */
    public void setCaptchaEngineClass(String theClassName) throws IllegalArgumentException {
        try {
            Object engine = Class.forName(theClassName).newInstance();
            if (engine instanceof com.octo.captcha.engine.CaptchaEngine) {
                this.engine = (com.octo.captcha.engine.CaptchaEngine) engine;
            } else {
                throw new IllegalArgumentException("Class is not instance of CaptchaEngine! " + theClassName);
            }
        } catch (InstantiationException e) {
            throw new IllegalArgumentException(e.getMessage());
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException(e.getMessage());
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException(e.getMessage());
        } catch (RuntimeException e) {
            throw new IllegalArgumentException(e.getMessage());
        }
    }

    /**
     * Get the minimum delay (in seconds) a client can be assured that a captcha generated by the service can be
     * retrieved and a response to its challenge tested
     *
     * @return the maximum delay in seconds
     */
    public int getMinGuarantedStorageDelayInSeconds() {
        return minGuarantedStorageDelayInSeconds;
    }

    /**
     * set the minimum delay (in seconds)a client can be assured that a captcha generated by the service can be
     * retrieved and a response to its challenge tested
     *
     * @param theMinGuarantedStorageDelayInSeconds
     *         the minimum guaranted delay
     */
    public void setMinGuarantedStorageDelayInSeconds(int theMinGuarantedStorageDelayInSeconds) {
        this.minGuarantedStorageDelayInSeconds = theMinGuarantedStorageDelayInSeconds;

        updateCache();
    }

    /**
     * Get the number of captcha generated since the service is up WARNING : this value won't be significant if the real
     * number is > Long.MAX_VALUE
     *
     * @return the number of captcha generated since the service is up
     */
    public long getNumberOfGeneratedCaptchas() {
        return numberOfGeneratedCaptchas;
    }

    /**
     * Get the number of correct responses to captcha challenges since the service is up. WARNING : this value won't be
     * significant if the real number is > Long.MAX_VALUE
     *
     * @return the number of correct responses since the service is up
     */
    public long getNumberOfCorrectResponses() {
        return numberOfCorrectResponse;
    }

    /**
     * Get the number of uncorrect responses to captcha challenges since the service is up. WARNING : this value won't
     * be significant if the real number is > Long.MAX_VALUE
     *
     * @return the number of uncorrect responses since the service is up
     */
    public long getNumberOfUncorrectResponses() {
        return numberOfUncorrectResponse;
    }

    /**
     * Get the curent size of the captcha store
     *
     * @return the size of the captcha store
     */
    public int getCaptchaStoreSize() {
        return this.store.getSize();
    }

    /**
     * Get the number of captchas that can be garbage collected in the captcha store
     *
     * @return the number of captchas that can be garbage collected in the captcha store
     */
    public int getNumberOfGarbageCollectableCaptchas() {
        return 0;
    }

    /**
     * Get the number of captcha garbage collected since the service is up WARNING : this value won't be significant if
     * the real number is > Long.MAX_VALUE
     *
     * @return the number of captcha garbage collected since the service is up
     */
    public long getNumberOfGarbageCollectedCaptcha() {
        return 0;
    }

    /**
     * @return the max captchaStore load before garbage collection of the store
     */
    public int getCaptchaStoreSizeBeforeGarbageCollection() {
        return 0;
    }

    /**
     * max captchaStore size before garbage collection of the store
     */
    public void setCaptchaStoreSizeBeforeGarbageCollection(int captchaStoreSizeBeforeGarbageCollection) {

        throw new UnsupportedOperationException("the max store size is useless for eh cache implementation");

    }

    /**
     * This max size is used by the service : it will throw a CaptchaServiceException if the store is full when a client
     * ask for a captcha.
     */
    public void setCaptchaStoreMaxSize(int size) {
        this.captchaStoreMaxSize = size;

        updateCache();

    }

    /**
     * @return the desired max size of the captcha store
     */
    public int getCaptchaStoreMaxSize() {
        return this.captchaStoreMaxSize;

    }

    /**
     * Garbage collect the captcha store, means all old capthca (captcha in the store wich has been stored more than the
     * MinGuarantedStorageDelayInSecond
     */
    public void garbageCollectCaptchaStore() {
        //to garbage collect, wait 5 minutes or get : see ehcache doco
        Iterator it = null;
        try {
            it = cacheManager.getCache(captchaStoreCacheName).getKeys().iterator();
        } catch (CacheException e) {
            log.error(e);
        }
        while (it.hasNext()) {
            try {
                cacheManager.getCache(captchaStoreCacheName).get(it.next().toString());
            } catch (CacheException e) {
                log.error(e);
            }
        }
    }

    /**
     * Empty the Store
     */
    public void emptyCaptchaStore() {
        //empty the store
        this.store.empty();
    }

    private void updateCache() {

        Cache cache = new Cache(captchaStoreCacheName, captchaStoreMaxSize, true, false,
                minGuarantedStorageDelayInSeconds, minGuarantedStorageDelayInSeconds);
        Iterator it = null;
        try {
            it = copyCacheContent().iterator();
        } catch (CacheException e) {
            log.error(e);
        }

        try {
            cacheManager.removeCache(captchaStoreCacheName);
            cacheManager.addCache(cache);
            this.store = new EhcacheCaptchaStore(cache);
            Cache myCache = cacheManager.getCache(captchaStoreCacheName);
            long now = System.currentTimeMillis();
            while (it.hasNext()) {
                Element el = (Element) it.next();
                if ((now - el.getCreationTime()) < cache.getTimeToLiveSeconds() * 1000) {
                    myCache.put(el);
                }
            }
        } catch (CacheException e) {
            log.error(e);
        }

    }

    private Collection copyCacheContent() throws CacheException {
        Cache currentcache = cacheManager.getCache(captchaStoreCacheName);
        Iterator it = store.getKeys().iterator();
        Collection els = new HashSet();
        while (it.hasNext()) {
            Element el = currentcache.get(it.next().toString());
            if (el != null) {
                els.add(el);
            }
        }
        return els;

    }

    //*******
    ///Overriding business methods to add some stats and store management hooks
    ///****

    protected Captcha generateAndStoreCaptcha(Locale locale, String ID) {
        if (store.getSize() >= this.captchaStoreMaxSize) {
            //impossible ! has to wait
            throw new CaptchaServiceException(
                    "Store is full," + " try to increase CaptchaStore Size or " + "to decrease time out");

        }

        Captcha captcha = this.engine.getNextCaptcha(locale);
        this.numberOfGeneratedCaptchas++;
        store.storeCaptcha(ID, captcha, locale);
        return captcha;
    }

    /**
     * Method to validate a response to the challenge corresponding to the given ticket and remove the coresponding
     * captcha from the store.
     *
     * @param ID the ticket provided by the buildCaptchaAndGetID method
     *
     * @return true if the response is correct, false otherwise.
     *
     * @throws CaptchaServiceException if the ticket is invalid
     */
    public Boolean validateResponseForID(String ID, Object response) throws CaptchaServiceException {

        Boolean valid = super.validateResponseForID(ID, response);

        if (valid.booleanValue()) {
            numberOfCorrectResponse++;
        } else {
            numberOfUncorrectResponse++;
        }
        return valid;
    }

}