org.jtalks.poulpe.service.JcommuneHttpNotifier.java Source code

Java tutorial

Introduction

Here is the source code for org.jtalks.poulpe.service.JcommuneHttpNotifier.java

Source

/**
 * Copyright (C) 2011  JTalks.org Team
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.jtalks.poulpe.service;

import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.jtalks.poulpe.model.dao.UserDao;
import org.jtalks.poulpe.model.entity.PoulpeBranch;
import org.jtalks.poulpe.model.entity.PoulpeSection;
import org.jtalks.poulpe.service.exceptions.JcommuneRespondedWithErrorException;
import org.jtalks.poulpe.service.exceptions.JcommuneUrlNotConfiguredException;
import org.jtalks.poulpe.service.exceptions.NoConnectionToJcommuneException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
 * Notifier to notify JCommune component about elements' deleting. It is useful to help forum keep such information, as
 * user's messages count, up to date.
 *
 * @author Nickolay Polyarniy
 * @author Mikhail Zaitsev
 */
@Deprecated
public class JcommuneHttpNotifier implements JCommuneNotifier {
    /** Minimum value of a successful status, less than that is an error HTTP response. */
    private static final int MIN_HTTP_STATUS = 200;
    /** Maximum value of a successful status, starting from 300 means an error response. */
    private static final int MAX_HTTP_STATUS = 299;
    /**
     * A link which means 'delete the whole component' which will cause all the topics from all the branches to be
     * removed by JCommune.
     */
    private static final String WHOLEFORUM_URL_PART = "/component";
    /** A URL to trigger re-indexing of forum search engine. */
    private static final String REINDEX_URL_PART = "/search/index/rebuild";
    /** URL to ask JCommune to remove content of the specific section. */
    private static final String SECTIONS_URL_PART = "/sections/";
    /** URL to ask JCommune to remove content of the specific branch. */
    private static final String BRANCH_URL_PART = "/branches/";
    /**
     * If either connection can't be established (e.g. firewall drops it) or connection was established, but response is
     * not coming back during this timeout, then the connection will be dropped. Note, that these are milliseconds.
     */
    private static final int HTTP_CONNECTION_TIMEOUT = 5000;
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private final UserDao userDao;

    /** Constructor for initialization variables */
    public JcommuneHttpNotifier(UserDao userDao) {
        this.userDao = userDao;
    }

    /**
     * Notifies delete the section
     *
     * @param jCommuneUrl JCommune Url
     * @param section     which will be deleted
     * @throws NoConnectionToJcommuneException
     *          some connection problems happened, while trying to notify Jcommune
     * @throws JcommuneRespondedWithErrorException
     *          occurs when the response status is not in the interval {@link #MIN_HTTP_STATUS} and {@link
     *          #MAX_HTTP_STATUS}
     * @throws JcommuneUrlNotConfiguredException
     *          occurs when the {@code jCommuneUrl} is incorrect
     */
    public void notifyAboutSectionDelete(String jCommuneUrl, PoulpeSection section)
            throws NoConnectionToJcommuneException, JcommuneRespondedWithErrorException,
            JcommuneUrlNotConfiguredException {
        long id = section.getId();
        notifyAboutDeleteElement(jCommuneUrl + SECTIONS_URL_PART + id);
    }

    /**
     * Notifies delete the branch
     *
     * @param jCommuneUrl JCommune Url
     * @param branch      which will be deleted
     * @throws NoConnectionToJcommuneException
     *          some connection problems happened, while trying to notify Jcommune
     * @throws JcommuneRespondedWithErrorException
     *          occurs when the response status is not in the interval {@link #MIN_HTTP_STATUS} and {@link
     *          #MAX_HTTP_STATUS}
     * @throws JcommuneUrlNotConfiguredException
     *          occurs when the {@code jCommuneUrl} is incorrect
     */
    public void notifyAboutBranchDelete(String jCommuneUrl, PoulpeBranch branch)
            throws NoConnectionToJcommuneException, JcommuneRespondedWithErrorException,
            JcommuneUrlNotConfiguredException {
        long id = branch.getId();
        notifyAboutDeleteElement(jCommuneUrl + BRANCH_URL_PART + id);
    }

    /**
     * Notifies delete the component
     *
     * @param jCommuneUrl JCommune Url
     * @throws NoConnectionToJcommuneException
     *          some connection problems happend, while trying to notify Jcommune
     * @throws JcommuneRespondedWithErrorException
     *          occurs when the response status is not in the interval {@code MIN_HTTP_STATUS} and {@code
     *          MAX_HTTP_STATUS}
     * @throws JcommuneUrlNotConfiguredException
     *          occurs when the {@code jCommuneUrl} is incorrect
     */
    public void notifyAboutComponentDelete(String jCommuneUrl) throws NoConnectionToJcommuneException,
            JcommuneRespondedWithErrorException, JcommuneUrlNotConfiguredException {
        notifyAboutDeleteElement(jCommuneUrl + WHOLEFORUM_URL_PART);
    }

    /**
     * Notifies reindex ?omponent
     *
     * @param jCommuneUrl JCommune Url
     * @throws NoConnectionToJcommuneException
     *          some connection problems happened, while trying to notify Jcommune
     * @throws JcommuneRespondedWithErrorException
     *          occurs when the response status is not in the interval {@code MIN_HTTP_STATUS} and {@code
     *          MAX_HTTP_STATUS}
     * @throws org.jtalks.poulpe.service.exceptions.JcommuneUrlNotConfiguredException
     *          occurs when the {@code jCommuneUrl} is incorrect
     */
    public void notifyAboutReindexComponent(String jCommuneUrl) throws NoConnectionToJcommuneException,
            JcommuneRespondedWithErrorException, JcommuneUrlNotConfiguredException {
        checkUrlIsConfigured(jCommuneUrl);
        createAndSendRequest(jCommuneUrl + REINDEX_URL_PART, HttpPost.METHOD_NAME);
    }

    /**
     * Notifies JCommune that an element is about to be deleted (for instance Component, Branch, Section).
     *
     * @param url JCommune Url
     * @throws NoConnectionToJcommuneException
     *          some connection problems happened, while trying to notify Jcommune
     * @throws JcommuneRespondedWithErrorException
     *          occurs when the response status is not in the interval {@code MIN_HTTP_STATUS} and {@code
     *          MAX_HTTP_STATUS}
     * @throws JcommuneUrlNotConfiguredException
     *          occurs when the {@code url} is incorrect
     */
    protected void notifyAboutDeleteElement(String url) throws NoConnectionToJcommuneException,
            JcommuneRespondedWithErrorException, JcommuneUrlNotConfiguredException {
        checkUrlIsConfigured(url);
        createAndSendRequest(url, HttpDelete.METHOD_NAME);
    }

    /**
     * Checks the url
     *
     * @param jCommuneUrl JCommune Url
     * @throws JcommuneUrlNotConfiguredException
     *          occurs when the {@code jCommuneUrl is incorrect
     */
    protected void checkUrlIsConfigured(String jCommuneUrl) throws JcommuneUrlNotConfiguredException {
        if (StringUtils.isBlank(jCommuneUrl)) {
            throw new JcommuneUrlNotConfiguredException();
        }
    }

    /**
     * Creates the HTTP request from specified data, adds admin password to the URL and sends to JCommune. The password
     * is required to be set in order to secure this invocation, otherwise anyway would be able to send such request to
     * JCommune.
     *
     * @param url        an address to send the request to
     * @param httpMethod delete or post request, see {@link HttpDelete}, {@link HttpPost}
     * @throws JcommuneRespondedWithErrorException
     *          if HTTP request reached JCommune, but JCommune responded with error code, such situation may happen for
     *          instance when we're deleting some branch, but it was already deleted, or JCommune has troubles removing
     *          that branch (database connection lost). Note that if we reach some other site and it responds with 404
     *          for example, this will be still this error.
     * @throws NoConnectionToJcommuneException
     *          if nothing was found at the specified URL, note that if URL was set incorrectly to point to another
     *          site, this can't be figured out by us, we just operate with HTTP codes, which means that either the
     *          request will be fine or {@link JcommuneRespondedWithErrorException} might be thrown in case if some
     *          other site was specified and it returned 404
     */
    private void createAndSendRequest(String url, String httpMethod)
            throws JcommuneRespondedWithErrorException, NoConnectionToJcommuneException {
        logger.info("Sending [{}] request to JCommune: [{}]", httpMethod, url);
        String adminPassword = userDao.getByUsername("admin").getPassword();
        HttpRequestBase request = createWithHttpMethod(httpMethod, url + "?password=" + adminPassword);
        try {
            HttpResponse response = doSendRequest(request);
            assertStatusSuccessful(response);
        } catch (IOException e) {
            logger.info("Was not possible to send request since [{}] does not respond", url);
            throw new NoConnectionToJcommuneException(e);
        } finally {
            request.releaseConnection();
        }
    }

    /**
     * Figures out whether the HTTP response was successful or not, if not than exception is thrown.
     *
     * @param response JCommune's HTTP response
     * @throws JcommuneRespondedWithErrorException
     *          if the HTTP code in the response appeared to be error one
     */
    private void assertStatusSuccessful(HttpResponse response) throws JcommuneRespondedWithErrorException {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode < MIN_HTTP_STATUS || statusCode > MAX_HTTP_STATUS) {
            logErrorResponse(response);
            throw new JcommuneRespondedWithErrorException(String.valueOf(statusCode));
        }
    }

    /**
     * Logs an error that was received from JCommune in the HTTP response body. Note that this doesn't check whether the
     * response was actually error one or not, so caller methods should ensure this.
     *
     * @param response HTTP response from JCommune to log its body
     */
    private void logErrorResponse(HttpResponse response) {
        try {
            logger.info("Error HTTP code was returned by JCommune: [{}]. Response body: [{}]",
                    response.getStatusLine().getStatusCode(), IOUtils.toString(response.getEntity().getContent()));
        } catch (IOException e) {
            logger.warn("Was not possible to read JCommune error response due to exception.", e);
        }
    }

    /**
     * Creates HTTP request based on the specified method.
     *
     * @param httpMethod either {@link HttpDelete#getMethod()} or {@link HttpPost#getMethod()}
     * @param url        address of JCommune
     * @return constructed HTTP request based on specified method
     * @throws IllegalArgumentException if the method was neither DELETE nor POST
     */
    private HttpRequestBase createWithHttpMethod(String httpMethod, String url) {
        if (HttpDelete.METHOD_NAME.equals(httpMethod)) {
            return new HttpDelete(url);
        } else if (HttpPost.METHOD_NAME.equals(httpMethod)) {
            return new HttpPost(url);
        } else {
            throw new IllegalArgumentException("Wrong HTTP method name was specified: [" + httpMethod + "]");
        }
    }

    /**
     * Sets http-specific parameters and actually sends the request, this was factored out in a separate method in order
     * to be mocked out since it's impossible to mock the HTTP call itself.
     *
     * @param request prepared http request that has to be only sent, no URL or other configuration should be required
     * @return the HTTP response that actually came from the host
     * @throws IOException if problems (e.g. no connection) were found while sending request
     */
    @VisibleForTesting
    HttpResponse doSendRequest(HttpUriRequest request) throws IOException {
        HttpClient httpClient = new DefaultHttpClient();
        httpClient.getParams().setParameter("http.socket.timeout", HTTP_CONNECTION_TIMEOUT);
        httpClient.getParams().setParameter("http.connection.timeout", HTTP_CONNECTION_TIMEOUT);
        return httpClient.execute(request);
    }

}