org.xwiki.ircbot.internal.BrokenLinkEventListener.java Source code

Java tutorial

Introduction

Here is the source code for org.xwiki.ircbot.internal.BrokenLinkEventListener.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.ircbot.internal;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.apache.commons.collections.Buffer;
import org.apache.commons.collections.BufferUtils;
import org.apache.commons.collections.buffer.CircularFifoBuffer;
import org.xwiki.component.annotation.Component;
import org.xwiki.ircbot.BrokenLinkEventListenerConfiguration;
import org.xwiki.ircbot.IRCBot;
import org.xwiki.observation.EventListener;
import org.xwiki.observation.event.Event;
import org.xwiki.rendering.transformation.linkchecker.InvalidURLEvent;
import org.xwiki.rendering.transformation.linkchecker.LinkState;

/**
 * Whenever a broken link is found in the wiki, send the notification on the IRC channel.
 *
 * @version $Id: 90b18f2ea63c0288735dfa8092647ce15567d773 $
 * @since 4.0M2
 */
@Component
@Named("ircbrokenlink")
@Singleton
public class BrokenLinkEventListener implements EventListener {
    /**
     * Property name for the broken link URL in broken link data map.
     */
    private static final String URL = "url";

    /**
     * Used to verify if the Bot is connected since we don't want to send messages if the Bot isn't connected.
     */
    @Inject
    private IRCBot bot;

    /**
     * Used to decide if this listener is active or not.
     */
    @Inject
    private BrokenLinkEventListenerConfiguration configuration;

    /**
     * Save the last 4 broken links so that they can be printed quickly on the IRC Channel by the Broken Links Bot
     * Listener.
     */
    private Buffer lastBrokenLinks = BufferUtils.synchronizedBuffer(new CircularFifoBuffer(4));

    @Override
    public String getName() {
        return "ircbrokenlink";
    }

    @Override
    public List<Event> getEvents() {
        return Arrays.<Event>asList(new InvalidURLEvent());
    }

    @Override
    public void onEvent(Event event, Object source, Object data) {
        if (this.bot.isConnected() && this.configuration.isActive()) {
            Map<String, Object> brokenLinkData = (Map<String, Object>) source;

            // Make sure we don't save duplicates (this is because every time a page with a broken link is rendered
            // an event is sent so we can receive several events for the same broken link
            String linkURL = (String) brokenLinkData.get(URL);
            removeDuplicateLinkData(linkURL);

            // Save the broken link data for later retrieval by the Broken Links Bot Listener.
            this.lastBrokenLinks.add(brokenLinkData);

            String linkSource = (String) brokenLinkData.get("source");
            LinkState linkState = (LinkState) brokenLinkData.get("state");
            int responseCode = linkState.getResponseCode();

            // Get Link extra data and serialize them
            String dataString = getSerializedLinkData((Map<String, Object>) brokenLinkData.get("contextData"));

            String message;
            if (dataString.length() > 0) {
                message = String.format("Invalid link %s on page %s (code = %s, %s)", linkURL, linkSource,
                        responseCode, dataString);
            } else {
                message = String.format("Invalid link %s on page %s (code = %s)", linkURL, linkSource,
                        responseCode);
            }

            // Get the channel to which to send to. If there's no channel name it means the Bot hasn't joined
            // any channel yet so don't do anything!
            Iterator<String> channelNameItator = this.bot.getChannelsNames().iterator();
            if (channelNameItator.hasNext()) {
                this.bot.sendMessage(channelNameItator.next(), message);
            }
        }
    }

    /**
     * @param contextData the extra context data to serialize
     * @return the serialized version of context data, ready to be printed to the user, separated by commas
     *         (eg "param1 = value1, param2 = value2")
     */
    private String getSerializedLinkData(Map<String, Object> contextData) {
        StringBuffer dataString = new StringBuffer();
        if (contextData != null) {
            Iterator<Map.Entry<String, Object>> it = contextData.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry<String, Object> entry = it.next();
                dataString.append(entry.getKey()).append(" = ").append(entry.getValue());
                if (it.hasNext()) {
                    dataString.append(", ");
                }
            }
        }
        return dataString.toString();
    }

    /**
     * @param linkURL the link url for which we check if it's already saved in the Buffer and if so we remove it
     */
    private void removeDuplicateLinkData(String linkURL) {
        Iterator it = this.lastBrokenLinks.iterator();
        Map<String, Object> duplicateBrokenLinkData = null;
        while (it.hasNext()) {
            Map<String, Object> savedLinkData = (Map<String, Object>) it.next();
            String savedLinkURL = (String) savedLinkData.get(URL);
            if (linkURL.equals(savedLinkURL)) {
                duplicateBrokenLinkData = savedLinkData;
                break;
            }
        }
        if (duplicateBrokenLinkData != null) {
            this.lastBrokenLinks.remove(duplicateBrokenLinkData);
        }
    }

    /**
     * @return the latest broken links found in the wiki
     */
    // TODO: Expose this through a user-public interface since this is currently in the internal package only!
    public Buffer getLastBrokenLinks() {
        return BufferUtils.unmodifiableBuffer(this.lastBrokenLinks);
    }
}