1
2
3
4
5
6
7 package com.octo.captcha.service;
8
9 import java.util.Locale;
10
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
13
14 import com.octo.captcha.Captcha;
15 import com.octo.captcha.engine.CaptchaEngine;
16 import com.octo.captcha.service.captchastore.CaptchaStore;
17
18 /***
19 * This is a base class for CaptchaService implementations. It implements the lyfe cycle stuff. It uses : a
20 * CaptchaStore to store captcha during the life cycle, and a CaptchaEngine to build captchas. All concrete
21 * implementation (that uses a specific capthcaStore and captchaEngine) should provide a default non argument
22 * constructor (by subclassing this class, and calling the constructor of the abstract class)
23 *
24 * @author Marc-Antoine Garrigue mailto:mag@jcaptcha.net
25 */
26 public abstract class AbstractCaptchaService implements CaptchaService {
27
28 protected CaptchaStore store;
29 protected CaptchaEngine engine;
30 protected Logger logger;
31
32
33 protected AbstractCaptchaService(CaptchaStore captchaStore,
34 CaptchaEngine captchaEngine) {
35 if (captchaEngine == null || captchaStore == null)
36 throw new IllegalArgumentException("Store or gimpy can't be null");
37 this.engine = captchaEngine;
38 this.store = captchaStore;
39
40 logger = LoggerFactory.getLogger(this.getClass());
41
42 logger.info("Init " + this.store.getClass().getName());
43 this.store.initAndStart();
44 }
45
46
47 /***
48 * Method to retrive the challenge corresponding to the given ticket from the store.
49 *
50 * @param ID the ticket provided by the buildCaptchaAndGetID method
51 * @return the challenge
52 * @throws CaptchaServiceException if the ticket is invalid
53 */
54 public Object getChallengeForID(String ID) throws CaptchaServiceException {
55 return this.getChallengeForID(ID, Locale.getDefault());
56 }
57
58 /***
59 * Method to retrive the challenge corresponding to the given ticket.
60 *
61 * @param ID ticket
62 * @param locale the desired localized capthca
63 * @return the localized challenge
64 * @throws CaptchaServiceException if the ticket is invalid
65 */
66 public Object getChallengeForID(String ID, Locale locale)
67 throws CaptchaServiceException {
68 Captcha captcha;
69 Object challenge;
70
71 if (!this.store.hasCaptcha(ID)) {
72
73 captcha = generateAndStoreCaptcha(locale, ID);
74 } else {
75
76 captcha = this.store.getCaptcha(ID);
77 if (captcha == null) {
78 captcha = generateAndStoreCaptcha(locale, ID);
79 } else {
80
81 if (captcha.hasGetChalengeBeenCalled().booleanValue()) {
82
83 captcha = generateAndStoreCaptcha(locale, ID);
84 }
85
86 }
87 }
88 challenge = getChallengeClone(captcha);
89 captcha.disposeChallenge();
90
91 return challenge;
92 }
93
94
95 /***
96 * Method to retrive the question corresponding to the given ticket.
97 *
98 * @param ID ticket
99 * @param locale the desired localized capthca
100 * @return the localized question
101 * @throws CaptchaServiceException if the ticket is invalid
102 */
103 public String getQuestionForID(String ID, Locale locale) throws CaptchaServiceException {
104 Captcha captcha;
105
106 if (!this.store.hasCaptcha(ID)) {
107
108 captcha = generateAndStoreCaptcha(locale, ID);
109 } else {
110 captcha = this.store.getCaptcha(ID);
111 if (captcha == null) {
112 captcha = generateAndStoreCaptcha(locale, ID);
113 }else if (locale != null) {
114 Locale storedlocale = this.store.getLocale(ID);
115 if (!locale.equals(storedlocale)) {
116 captcha = generateAndStoreCaptcha(locale, ID);
117 }
118 }
119
120 }
121 return captcha.getQuestion();
122 }
123
124 /***
125 * Method to retrive the question corresponding to the given ticket from the store.
126 *
127 * @param ID the ticket provided by the buildCaptchaAndGetID method
128 * @return the question
129 * @throws CaptchaServiceException if the ticket is invalid
130 */
131 public String getQuestionForID(String ID) throws CaptchaServiceException {
132 return this.getQuestionForID(ID, Locale.getDefault());
133 }
134
135 /***
136 * Method to validate a response to the challenge corresponding to the given ticket and remove the coresponding
137 * captcha from the store.
138 *
139 * @param ID the ticket provided by the buildCaptchaAndGetID method
140 * @return true if the response is correct, false otherwise.
141 * @throws CaptchaServiceException if the ticket is invalid
142 */
143 public Boolean validateResponseForID(String ID, Object response)
144 throws CaptchaServiceException {
145 if (!store.hasCaptcha(ID)) {
146 throw new CaptchaServiceException("Invalid ID, could not validate unexisting or already validated captcha");
147 } else {
148 Boolean valid = store.getCaptcha(ID).validateResponse(response);
149 store.removeCaptcha(ID);
150 return valid;
151 }
152 }
153
154
155 protected Captcha generateAndStoreCaptcha(Locale locale, String ID) {
156 Captcha captcha = engine.getNextCaptcha(locale);
157 this.store.storeCaptcha(ID, captcha, locale);
158 return captcha;
159 }
160
161
162 /***
163 * This method must be implemented by sublcasses and : Retrieve the challenge from the captcha Make and return a
164 * clone of the challenge Return the clone It has be design in order to let the service dispose the challenge of the
165 * captcha after rendering. It should be implemented for all captcha type (@see ImageCaptchaService implementations
166 * for exemple)
167 *
168 * @return a Challenge Clone
169 */
170 protected abstract Object getChallengeClone(Captcha captcha);
171
172
173 }