Как данные с сервиса передать в custom tag?

211
23 января 2019, 16:00

Я работаю с сервлетами и кастом тегами. Я занимаюсь созданием капчей. У меня есть мапа капчи где в качестве ключа лежить ее id а значением ее сгенерированое значение. Как мне вставить мою капчу в JSP с помощью кастом тега, я пытался как-то засетить но, что то я делаю не верно так как капча не появляется на страничке. Где я допустил ошибку, подскажите?

Мой класс где я генерирую капчу:

public class CaptchaGenerator {
    private static final Logger LOG = Logger.getLogger(CaptchaGenerator.class);
    private CaptchaService captchaService;
    public CaptchaGenerator(CaptchaService captchaService) {
        this.captchaService = captchaService;
    }
    public void generateCaptcha(HttpServletResponse response) throws IOException {
        int width = 535;
        int height = 90;
        long id = System.currentTimeMillis(); //here is my captcha id generated
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics2D = bufferedImage.createGraphics();
        Font font = new Font("Impact", Font.ITALIC, 34);
        graphics2D.setFont(font);
        graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        Random random = new Random();
        int x = 0;
        int y;
        String captchaId = captchaService.setId(String.valueOf(id));
        captchaService.put(captchaId, String.valueOf(new Random().nextInt(99999) + 10000)); // here is i put generated param into my captcha map
        char[] code = captchaService.getElement(String.valueOf(captchaService.getId())).toCharArray();
        for (int i = 0; i < code.length; i++) {
            x += 75 + (Math.abs(random.nextInt()) % 20);
            y = 50 + (Math.abs(random.nextInt()) % 20);
            graphics2D.drawChars(captchaService.getElement(String.valueOf(captchaService.getId())).toCharArray(), i, 1, x, y);
        }
        LOG.debug("Your captcha map " + captchaService);
        LOG.debug("Generated id " + id);
        graphics2D.dispose();
        response.setContentType("image/png");
        OutputStream os = response.getOutputStream();
        ImageIO.write(bufferedImage, "png", os);
        os.close();
    }

У меня вопрос, как мне можно отобразить мою капчу через тег по id который я сгенерировал, как верно это сделать в JSP и в где я ошибся?

Custom tag:

public class CaptchaCustomTag extends SimpleTagSupport {
    private static final Logger LOG = Logger.getLogger(CaptchaCustomTag.class);
    private String captchaId;
    public void setCaptchaId(String captchaId) {
      this.captchaId = captchaId;
    }
    private String getCaptchaPNG() {
        return "<img src=\"captcha?id=" + captchaId + "\">";
    }
    private String getHidden() {
        return "<input type=\"hidden\" name=\"captchaID\" value=\"" + captchaId + "\">\n";
    }
    @Override
    public void doTag() throws JspException, IOException {
        getJspContext().getOut().write(getCaptchaPNG() + getHidden());
        LOG.debug("Captcha id: " + captchaId);
    }
}

Если я создам здесь экземпляр моего сервиса капчи, то моя сгеннерированная капча исчезнет.

Captcha service:

public class CaptchaService implements service.CaptchaService {
    private Captcha captcha;
    private Map<String, String> captchaMap = new HashMap<>();
    public CaptchaService(Captcha captcha) {
        this.captcha = captcha;
    }
    @Override
    public boolean isExistValidCaptcha() {
        return !captchaMap.isEmpty();
    }
    @Override
    public String getCaptchaById(String id) {
        return captchaMap.get(id);
    }
    @Override
    public String setId(String id) {
        return captcha.setId(id);
    }
    @Override
    public String getId() {
        return captcha.getId();
    }
    @Override
    public String getElement(String key) {
        return captchaMap.get(key);
    }
    @Override
    public void put(String key, String value) {
        captchaMap.put(key, value);
    }
    @Override
    public void removeOldCaptcha(long captchaTime) {
        if (captchaMap.isEmpty()) {
            for (Map.Entry<String, String> entryMap : captchaMap.entrySet()) {
                long actualCaptchaTime = System.currentTimeMillis() - Long.valueOf(entryMap.getKey());
                if (actualCaptchaTime >= captchaTime) {
                    captchaMap.remove(entryMap.getKey());
                }
            }
        }
    }

Я вставляю свой тег таким образом, но каптча не отображается и мой сеттер показываеться, что он никогда не используется?

<MyTags:captcha captchaId = "${captchaId}" />

TLD:

<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
        version="2.0">
    <tlib-version>2.1</tlib-version>
    <short-name>MyTags</short-name>
    <uri>http://journaldev.com/jsp/tlds/MyTags</uri>
    <tag>
        <name>captcha</name>
        <tag-class>customtag.CaptchaCustomTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>captchaId</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>

Captcha Servlet:

private CaptchaService captchaService;
  @Override
  public void init() throws ServletException {
    captchaService = (CaptchaService) getServletContext().getAttribute("captchaService");
  }
   @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        CaptchaGenerator captchaGenerator = new CaptchaGenerator(captchaService);
        CaptchaTypeManager captchaTypeManager = new CaptchaTypeManager(req, resp, captchaService);
        captchaTypeManager.getType(getServletContext().getInitParameter(ConstantApp.ContextType.CONTEXT_TYPE));
        captchaGenerator.generateCaptcha(resp);
        LOG.debug(getServletContext().getInitParameter(ConstantApp.ContextType.CONTEXT_TYPE));
    }

registration form:

<form action="registration" method="post" id="registration_form">
        <h2><div align="center" style="color: #ff0000">${requestScope.error_time}</div></h2>
    <ul>
        <div id="error_first_name" style="color: #ff0000">${requestScope.error_first_name}</div>
        <li class="text-info">First Name:</li>
        <li><input type="text" id="fName" name="fName" placeholder="Your first name" value="${sessionScope.fName}"></li>
    </ul>
    <ul>
        <div id="error_last_name" style="color: #ff0000">${requestScope.error_last_name}</div>
        <li class="text-info">Last Name:</li>
        <li><input type="text" id="lName" name="lName" placeholder="Your last name" value="${sessionScope.lName}"></li>
    </ul>
    <ul>
        <div id="error_login" style="color: #ff0000">${requestScope.error_login}</div>
        <li class="text-info">Login:</li>
        <li><input type="text" id="login" name="login" placeholder="Your login" value="${sessionScope.login}"></li>
    </ul>
    <ul>
        <div id="error_email" style="color: #ff0000">${requestScope.error_email}</div>
        <li class="text-info">Email:</li>
        <li><input type="text" id="email" name="email" placeholder="Your email" value="${sessionScope.email}"></li>
    </ul>
    <ul>
        <div id="error_password" style="color: #ff0000">${requestScope.error_password}</div>
        <li class="text-info">Password:</li>
        <li><input type="password" id="pass" name="pass" placeholder="Your password"></li>
    </ul>
    <ul>
        <div id="error_confirmPassword" style="color: #ff0000">${requestScope.error_confirmPassword}</div>
        <li class="text-info">Re-enter Password:</li>
        <li><input type="password" id="confPass" name="confPass" placeholder="Confirm your password"></li>
    </ul>
     <MyTags:captcha captchaId = "${captchaId}"/>
     <br></br>
     <div style="color: #ff0000" id="error_captcha">${requestScope.error_captcha}</div>
     <input type="text" placeholder="Enter the code from the image" name="captchaCode" id="captchaCode"/>

    <input type="submit" id="submit" value="Register Now">
    <p class="click">By clicking this button, you agree to my modern style <a href="terms.jsp">Policy Terms and
        Conditions</a> to Use</p>
</form>

Context listener:

public class ContextListener implements ServletContextListener {
    private static final Logger LOG = Logger.getLogger(ContextListener.class);
    @Override
    public void contextInitialized(ServletContextEvent event) {
        dao.UserDAO dao = new UserDAO();
        service.UserService userService = new UserService(dao);
        Captcha captcha = new Captcha();
        CaptchaService captchaService = new service.impl.CaptchaService(captcha);
        event.getServletContext().setAttribute("captchaService", captchaService);
        event.getServletContext().setAttribute("userService", userService);
        ServletContext servletContext = event.getServletContext();
        initLog4J(servletContext);
    }

В конце я получаю пустое поле когда нажимаю F12 для проверки.

<MyTags:captcha captchaId = ""/>

web.xml:

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         id="WebApp_ID" version="2.5">
    <display-name>Shopper</display-name>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <listener>
        <listener-class>listener.ContextListener</listener-class>
    </listener>
    <context-param>
        <param-name>type</param-name>
        <param-value>cookies</param-value>
    </context-param>
    <context-param>
        <param-name>registrationTime</param-name>
        <param-value>60</param-value>
    </context-param>
    <jsp-config>
        <taglib>
            <taglib-uri>http://journaldev.com/jsp/tlds/MyTags</taglib-uri>
            <taglib-location>/WEB-INF/captchaTag.tld</taglib-location>
        </taglib>
    </jsp-config>
</web-app>

RegistarionServlet:

@WebServlet("/registration")
public class RegistrationServlet extends HttpServlet {
    private static final Logger LOG = Logger.getLogger(RegistrationServlet.class);
    private long startTime;
    private UserService userService;
    private CaptchaService captchaService;
    @Override
    public void init() throws ServletException {
        userService = (UserService) getServletContext().getAttribute("userService");
        captchaService = (CaptchaService) getServletContext().getAttribute("captchaService");
    }
    @Override
    protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
        startTime = System.currentTimeMillis();
        httpServletRequest.getRequestDispatcher(ConstantApp.JSPPages.REGISTRATION_PAGE).forward(httpServletRequest, httpServletResponse);
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        RegistrationInfo registrationInfo = new RegistrationInfoImpl(request);
        Map<String, String> validationError = Validation.validate(registrationInfo);
        String contextType = getServletContext().getInitParameter(ConstantApp.ContextType.CONTEXT_TYPE);
        ChooseStrategy chooseStrategy = new ChooseStrategy(contextType, request, captchaService);
        boolean isValidateCaptcha = chooseStrategy.chooseStrategy(contextType);
        boolean isValidTime = RegistrationTimer.isValidRegistrationTime(startTime, Integer.parseInt(getServletContext().getInitParameter(ConstantApp.ContextType.CONTEXT_REGISTRATION_TIME)));
        LOG.debug("Validation error map: " + validationError.entrySet());
        if (validationError.isEmpty() && !userService.isExist(registrationInfo.getLogin()) && isValidateCaptcha && isValidTime) {
            userService.add(new User(registrationInfo.getLogin(), registrationInfo.getFirstName(), registrationInfo.getLastName(), registrationInfo.getEmail(), registrationInfo.getPassword()));
            LOG.debug("All users: " + userService.getAll());
            request.getRequestDispatcher(ConstantApp.JSPPages.INDEX_PAGE).forward(request, response);
        }

Я выложил основные классы, если нужно больше, я добавлю). Как я могу передать свой код id капчи в тег чтобы отобразить ее?

Answer 1

Генерировать капчу не значит писать ее в респонс. Вы можете поменять сигнатуру метода

 public void generateCaptcha(OutputStream os) throws IOException { 
    ...
    response.setContentType("image/png");
    //OutputStream os = response.getOutputStream();
    ImageIO.write(bufferedImage, "png", os);
    os.close();
 }

тогда можно сгенерить капчу, передать ByteArrayOutputStream параметром в метод. Имидж в байтах положить в сессию, с атрибутом id.

RegistrationServlet:

@Override
protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
    startTime = System.currentTimeMillis();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    captchaGenerator.generateCaptcha(baos);
    httpServletRequest.getSession().setAttribute(captchaService.getId(), baos.toByteArray());
    httpServletRequest.getSession().setAttribute("id", captchaService.getId());
    httpServletRequest.getRequestDispatcher(ConstantApp.JSPPages.REGISTRATION_PAGE).forward(httpServletRequest, httpServletResponse);
}

CaptchaServlet:

  @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        CaptchaGenerator captchaGenerator = new CaptchaGenerator(captchaService);
        CaptchaTypeManager captchaTypeManager = new CaptchaTypeManager(req, resp, captchaService);
        captchaTypeManager.getType(getServletContext().getInitParameter(ConstantApp.ContextType.CONTEXT_TYPE));
        byte[] captcha = req.getSesssion().getAttribute(req.getParameter("id"));
        if (captcha == null) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        captchaGenerator.generateCaptcha(baos);
        captcha = baos.toByteArray();
req.getSession().setAttribute(captchaService.getId(), baos.toByteArray());
        req.getSession().setAttribute("id", captchaService.getId());
}
IOUtils.copy(new ByteArrayInputStream(captcha), res.getOputputStream());
        LOG.debug(getServletContext().getInitParameter(ConstantApp.ContextType.CONTEXT_TYPE));
    }
READ ALSO
Java многопоточный клиент

Java многопоточный клиент

Я разрабатываю программу которая работает с изображениями

166
Как найти предыдущий элемент?

Как найти предыдущий элемент?

Поиск предыдущего:

169
получение нового массива [дубликат]

получение нового массива [дубликат]

Данный вопрос является точным дубликатом:

151
Как отследить старую версию Safary в JS?

Как отследить старую версию Safary в JS?

Подскажите, как отследить что пользователь зашел на сайт с мобильного Сафари (Iphone 4-5 и ниже) *Насколько мне известно там Safary v5-7 и она больше...

159