org.jasig.irclog.ChannelLogger.java Source code

Java tutorial

Introduction

Here is the source code for org.jasig.irclog.ChannelLogger.java

Source

/**
 * Licensed to Jasig under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Jasig licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a
 * copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.jasig.irclog;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;

import org.jasig.irclog.events.ChannelEvent;
import org.jasig.irclog.events.ConnectEvent;
import org.jasig.irclog.events.IrcEvent;
import org.jasig.irclog.events.JoinEvent;
import org.jasig.irclog.events.KickEvent;
import org.jasig.irclog.events.ModeEvent;
import org.jasig.irclog.events.PartEvent;
import org.jasig.irclog.events.TargetedEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.scheduling.annotation.Scheduled;

import com.google.common.collect.Sets;
import com.googlecode.shutdownlistener.ShutdownListener;

/**
 * Handles filtering, logging, queuing and flushing events for a specific IRC channel.
 * 
 * @author Eric Dalquist
 * @version $Revision$
 */
public class ChannelLogger implements ApplicationListener<IrcEvent>, ShutdownListener, Ordered {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final Queue<IrcEvent> eventQueue = new ConcurrentLinkedQueue<IrcEvent>();
    private volatile long nextFlush = System.currentTimeMillis()
            + new Random().nextInt((int) TimeUnit.SECONDS.toMillis(30));

    private IrcBot ircBot;
    private Collection<EventWriter> eventWriters;
    private String channel;
    private String notification;
    private long flushPeriod = TimeUnit.MINUTES.toMillis(1);
    @SuppressWarnings("unchecked")
    private Set<Class<? extends IrcEvent>> ignoredEvents = Sets.<Class<? extends IrcEvent>>newHashSet(
            JoinEvent.class, KickEvent.class, ModeEvent.class, PartEvent.class);

    private boolean logNonTargetedEvents = false;

    /**
     * The IrcBot to use for logging the channel.
     */
    public void setIrcServer(IrcBot ircBot) {
        this.ircBot = ircBot;
    }

    /**
     * The EventWriters to flush queued events to
     */
    public void setEventWriters(Collection<EventWriter> eventWriters) {
        this.eventWriters = eventWriters;
    }

    /**
     * The IRC channel to log
     */
    public void setChannel(String channel) {
        this.channel = channel;
    }

    /**
     * The notification to send to the channel when joining and to send to any users that join
     * the channel
     */
    public void setNotification(String notification) {
        this.notification = notification;
    }

    /**
     * Frequency with which to flush queued events to the EventWriter in milliseconds. Defaults to
     * 1 Minute, minimum value is 1 Second.
     */
    public void setFlushPeriod(long flushPeriod) {
        this.flushPeriod = Math.min(1000, flushPeriod);
    }

    /**
     * Set {@link IrcEvent} classes that should not be logged. By default {@link JoinEvent}, {@link KickEvent}
     * {@link PartEvent}, and {@link ModeEvent} are ignored. Providing any Set here overrides these defaults.
     */
    public void setIgnoredEvents(Set<Class<? extends IrcEvent>> ignoredEvents) {
        if (ignoredEvents == null) {
            this.ignoredEvents = Collections.emptySet();
        } else {
            this.ignoredEvents = ignoredEvents;
        }
    }

    /**
     * If events other than {@link ChannelEvent} and {@link TargetedEvent} that target the configured channel
     * should be logged. Defaults to false.
     * 
     * WARNING, if you set this to true all events the {@link IrcBot} recieves will be logged unless it is listed
     * in {{@link #setIgnoredEvents(Set)} 
     */
    public void setLogNonTargetedEvents(boolean logNonTargetedEvents) {
        this.logNonTargetedEvents = logNonTargetedEvents;
    }

    @Override
    public void onApplicationEvent(IrcEvent event) {
        //Only pay attention to events from the IRC server we're associated with
        if (!this.ircBot.equals(event.getIrcBot())) {
            return;
        }

        //Channel membership management
        if (event instanceof ConnectEvent) {
            this.logger.info("Joining channel: " + channel);
            this.ircBot.joinChannel(this.channel);
        } else if (event instanceof KickEvent) {
            final KickEvent kickEvent = (KickEvent) event;
            if (this.channel.equals(kickEvent.getChannel())
                    && this.ircBot.getNick().equalsIgnoreCase(kickEvent.getRecipientNick())) {
                this.logger.info("Kicked from channel '" + this.channel + "' attempting to rejoin.");
                this.ircBot.joinChannel(this.channel);
            }
        }

        //Send the notification if one is set and a JoinEvent happens for this channel
        if (this.notification != null && event instanceof JoinEvent) {
            final JoinEvent joinEvent = (JoinEvent) event;

            if (this.channel.equals(joinEvent.getChannel())) {
                final String sender = joinEvent.getSender();

                if (sender.equalsIgnoreCase(this.ircBot.getNick())) {
                    final String channel = joinEvent.getChannel();
                    this.ircBot.sendNotice(channel, this.notification);
                } else {
                    this.ircBot.sendNotice(sender, this.notification);
                }
            }
        }

        //Check if the log message is ignored
        if (this.ignoredEvents.contains(event.getClass())) {
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Ignoring event " + event + ". It is in the IgnoredEvents Set");
            }

            return;
        }

        //Event logging, only targeted events are logged
        if (this.logNonTargetedEvents
                || (event instanceof ChannelEvent && this.channel.equals(((ChannelEvent) event).getChannel()))
                || (event instanceof TargetedEvent && this.channel.equals(((TargetedEvent) event).getTarget()))) {

            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Logging event " + event + " for channel " + this.channel);
            }

            this.eventQueue.offer(event);
        }
    }

    /**
     * Write out any queued events on shutdown
     */
    @Override
    public void shutdown() {
        this.flushEvents();
    }

    @Override
    public int getOrder() {
        return 1000;
    }

    /* (non-Javadoc)
     * @see org.jasig.irclog.EventQueue#flushEvents()
     */
    @Scheduled(fixedDelay = 1000)
    public void flushEvents() {
        //If the queue is empty update the nextFlush time, this always waits about the flushPeriod after seeing
        //a new event before writing them out 
        if (this.eventQueue.isEmpty()) {
            this.nextFlush = System.currentTimeMillis() + this.flushPeriod;
            return;
        }

        if (this.nextFlush > System.currentTimeMillis()) {
            return;
        }

        //Update nextFlush at the start to prevent concurrent execution if the write takes too long
        this.nextFlush = System.currentTimeMillis() + this.flushPeriod;

        final List<IrcEvent> eventBuffer = new LinkedList<IrcEvent>();
        while (!this.eventQueue.isEmpty()) {
            final IrcEvent event = this.eventQueue.poll();
            eventBuffer.add(event);
        }

        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Flushing " + eventBuffer.size() + " events");
        }

        for (final EventWriter eventWriter : this.eventWriters) {
            eventWriter.write(eventBuffer);
        }

        //Update nextFlush after everything is done
        this.nextFlush = System.currentTimeMillis() + this.flushPeriod;
    }
}