/** * 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.rest; import org.apache.commons.lang3.StringUtils; 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.JCommuneNotifier; import org.jtalks.poulpe.service.exceptions.JcommuneRespondedWithErrorException; import org.jtalks.poulpe.service.exceptions.JcommuneUrlNotConfiguredException; import org.jtalks.poulpe.service.exceptions.NoConnectionToJcommuneException; import org.restlet.Context; import org.restlet.data.Method; import org.restlet.representation.EmptyRepresentation; import org.restlet.resource.ClientResource; import org.restlet.resource.ResourceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; /** * 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 Evgeny Kapinos */ public class JCommuneNotifierImpl implements JCommuneNotifier { /** * 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 CONNECTION_TIMEOUT = 5000; private final Logger logger = LoggerFactory.getLogger(getClass()); private final UserDao userDao; /** * {@inheritDoc} */ public JCommuneNotifierImpl(UserDao userDao) { this.userDao = userDao; } /** * {@inheritDoc} */ @Override public void notifyAboutSectionDelete(String jCommuneUrl, PoulpeSection section) throws NoConnectionToJcommuneException, JcommuneRespondedWithErrorException, JcommuneUrlNotConfiguredException { checkUrlIsConfigured(jCommuneUrl); notifyJCommune(jCommuneUrl + SECTIONS_URL_PART + section.getId(), Method.DELETE); } /** * {@inheritDoc} */ @Override public void notifyAboutBranchDelete(String jCommuneUrl, PoulpeBranch branch) throws NoConnectionToJcommuneException, JcommuneRespondedWithErrorException, JcommuneUrlNotConfiguredException { checkUrlIsConfigured(jCommuneUrl); notifyJCommune(jCommuneUrl + BRANCH_URL_PART + branch.getId(), Method.DELETE); } /** * {@inheritDoc} */ @Override public void notifyAboutComponentDelete(String jCommuneUrl) throws NoConnectionToJcommuneException, JcommuneRespondedWithErrorException, JcommuneUrlNotConfiguredException { checkUrlIsConfigured(jCommuneUrl); notifyJCommune(jCommuneUrl + WHOLEFORUM_URL_PART, Method.DELETE); } /** * {@inheritDoc} */ @Override public void notifyAboutReindexComponent(String jCommuneUrl) throws NoConnectionToJcommuneException, JcommuneRespondedWithErrorException, JcommuneUrlNotConfiguredException { checkUrlIsConfigured(jCommuneUrl); notifyJCommune(jCommuneUrl + REINDEX_URL_PART, Method.POST); } /** * Notifies JCommune that an element is about to be deleted (for instance Component, Branch, Section). * * @param url full URL for REST request * @param method delete or post method, see {@link Method} * @throws NoConnectionToJcommuneException some connection problems happened, while trying to notify JCommune * @throws JcommuneRespondedWithErrorException occurs when the response status is not {@code OK 200} */ @VisibleForTesting protected void notifyJCommune(String url, Method method) throws NoConnectionToJcommuneException, JcommuneRespondedWithErrorException { logSendRequest(url, method); String adminPassword = userDao.getByUsername("admin").getPassword(); ClientResource clientResource = new ClientResource(new Context(), url + "?password=" + adminPassword); /* * How to set parameters described here: * http://wiki.restlet.org/docs_2.1/13-restlet/27-restlet/325-restlet/37-restlet.html * Which parameters described here: * org.restlet.engine.connector.ClientConnectionHelper, string "socket.setSoTimeout(getMaxIoIdleTimeMs());" */ clientResource.getContext().getParameters().add("socketConnectTimeoutMs", String.valueOf(CONNECTION_TIMEOUT)); clientResource.getContext().getParameters().add("maxIoIdleTimeMs", String.valueOf(CONNECTION_TIMEOUT)); try{ if (method.equals(Method.DELETE)){ clientResource.delete(); } else { clientResource.post(new EmptyRepresentation()); } } catch (ResourceException e){ processResourceException(e); } } /** * Checks 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(); } } /** * Process an error that was receive from JCommune in the REST response. * * @param e exception from REST framework * @throws JcommuneRespondedWithErrorException * if REST 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 processResourceException(ResourceException e) throws NoConnectionToJcommuneException, JcommuneRespondedWithErrorException { logResponseError(e); if (e.getStatus().isConnectorError()){ throw new NoConnectionToJcommuneException(e); } else{ throw new JcommuneRespondedWithErrorException(e); } } /** * Logs all JCommune in the requests. * @param url full URL for REST request * @param method delete or post method, see {@link Method} */ private void logSendRequest(String url, Method method){ logger.info("Sending {} request to JCommune: [{}]", method, url); } /** * Logs an error that was receive from JCommune in the response. * @param e exception from REST */ private void logResponseError(ResourceException e){ logger.error("JCommune error: {}", e.getStatus()); } }