me.emily.irc.io.ServerConnection.java Source code

Java tutorial

Introduction

Here is the source code for me.emily.irc.io.ServerConnection.java

Source

/*
 * Copyright (C) 2012 Emily Soldal
 * Licensed 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 me.emily.irc.io;

import static com.google.common.base.Predicates.*;
import static com.google.common.collect.Iterables.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import me.emily.config.GuiceHolder;
import me.emily.irc.event.events.ConnectingEvent;
import me.emily.irc.event.events.DisconnectEvent;
import me.emily.irc.protocol.DelaySupplier;
import me.emily.irc.protocol.ImmediateDelay;
import me.emily.irc.protocol.Message;
import me.emily.irc.protocol.SimpleDelay;
import me.emily.irc.protocol.parser.Parser;
import me.emily.modules.Module;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.eventbus.EventBus;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import com.google.inject.Inject;
import com.google.inject.name.Named;

/**
 * @author Emily Soldal
 * @created 29 Jan 2012
 */
public class ServerConnection implements Runnable {

    private static final Logger log = LoggerFactory.getLogger(ServerConnection.class);

    DelayQueue<DelaySupplier<Message>> junk = new DelayQueue<DelaySupplier<Message>>();
    DelayQueue<DelaySupplier<Message>> normal = new DelayQueue<DelaySupplier<Message>>();
    DelayQueue<DelaySupplier<Message>> critical = new DelayQueue<DelaySupplier<Message>>();

    private int nonCriticalLimit = 4;
    private Socket sock;
    private PrintStream out;
    private BufferedReader in;
    private final Server server;
    private User user;
    private ScheduledFuture<?> runningState;

    private final Set<Module> modules = Sets.newHashSet();
    private final Set<Module> activeModules = Sets.newHashSet();

    @Inject
    ListeningScheduledExecutorService executor;

    @Inject
    @Named("eventHandlers")
    Iterable<Object> eventHandlers;

    @Inject
    @Named("parsers")
    Iterable<Parser> parsers;

    @Inject
    EventBus bus;

    private final Map<String, Channel> channels = Maps.newHashMap();
    private final Map<String, User> users = Maps.newHashMap();

    @VisibleForTesting
    protected ServerConnection() {
        server = null;
        user = null;
    }

    public ServerConnection(Server server, User user) {
        super();
        this.server = server;
        this.user = user;

        GuiceHolder.inject(this);

        for (Object handler : eventHandlers) {
            register(handler);
        }
    }

    public boolean activateModule(String id) {
        Module mod = findModule(id);
        if (mod != null) {
            if (activeModules.contains(mod)) {
                log.info("No such module. {}", id);
                return false;
            }
            log.info("Activating module: {} {}", id, mod);
            for (Object listener : mod.listeners()) {
                bus.register(listener);
            }
            activeModules.add(mod);
            return true;
        }
        return false;
    }

    public void connect() throws UnknownHostException, IOException {
        log.info("Connecting to {}", server);
        sock = new Socket(server.getHostname(), server.getPort());
        sock.setKeepAlive(true);
        out = new PrintStream(sock.getOutputStream());
        in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
        bus.post(new ConnectingEvent(this));
        runningState = executor.scheduleAtFixedRate(this, 0, 100, TimeUnit.MILLISECONDS);
    }

    public void critical(Message msg) {
        critical.add(ImmediateDelay.of(msg));
    }

    public void critical(Message msg, long delay, TimeUnit unit) {
        critical.add(SimpleDelay.of(msg, delay, unit));
    }

    public boolean deactivateModule(String id) {
        Module mod = findModule(id);
        if (mod != null && activeModules.contains(mod)) {
            for (Object listener : mod.listeners()) {
                bus.unregister(listener);
            }
            activeModules.remove(mod);
            return true;
        }
        return false;
    }

    public void disconnect() {
        log.error("Disconnecting!");
        runningState.cancel(true);
        try {
            sock.shutdownInput();
            sock.shutdownOutput();
        } catch (IOException e) {
        } finally {
            sock = null;
            out = null;
            in = null;
            runningState = null;
            bus.post(new DisconnectEvent(this));
        }

    }

    public Module findModule(String id) {
        Module mod = Iterables.find(modules, compose(equalTo(id), Module.IDAccessor.instance), null);
        return mod;
    }

    public User user(String nick) {
        return users.get(nick);
    }

    /**
     * Find the channel object, null if otherwise.
     */
    public Channel getChannel(String channel) {
        return channels.get(channel.toLowerCase());
    }

    public Server getServer() {
        return this.server;
    }

    public User getSelf() {
        return this.user;
    }

    public boolean isConnected() {
        return sock != null && sock.isConnected() && !sock.isInputShutdown() && !sock.isOutputShutdown();
    }

    /**
     * Find or create the channel object
     */
    public Channel joined(String channel) {
        log.info("~~~ Joined {}", channel);
        Channel chan = channels.get(channel.toLowerCase());
        if (chan == null) {
            chan = new Channel(channel, this);
            channels.put(channel.toLowerCase(), chan);
        }
        return chan;
    }

    public void junk(Message msg) {
        junk.add(ImmediateDelay.of(msg));
    }

    public void junk(Message msg, long delay, TimeUnit unit) {
        junk.add(SimpleDelay.of(msg, delay, unit));
    }

    public void normal(Message msg) {
        normal.add(ImmediateDelay.of(msg));
    }

    public void normal(Message msg, long delay, TimeUnit unit) {
        normal.add(SimpleDelay.of(msg, delay, unit));
    }

    public void post(Object event) {
        bus.post(event);
    }

    private void readIn() {

        if (sock != null && sock.isConnected()) {
            try {
                String str;
                while (in.ready() && (str = in.readLine()) != null) {
                    log.info(">>{}", str);
                    for (Parser parser : parsers) {
                        if (parser.recieve(str, this)) {
                            log.debug("handled by {}", parser);
                            break;
                        }
                    }
                }
            } catch (Exception e) {
                log.info(e.getMessage(), e);
                //            Throwables.propagate(e);
            }
        }
    }

    public void register(Object subscriber) {
        log.info("Registering listner {}", subscriber.getClass());
        bus.register(subscriber);
    }

    public boolean registerModule(Module module) {
        return this.modules.add(module);
    }

    @Override
    public void run() {
        if (sock != null && sock.isConnected()) {
            writeOut();
            readIn();
            writeOut();
        } else if (sock != null) {
            disconnect();
        } else {
            log.error("Interrupting thread!");
            Thread.currentThread().interrupt();
        }
    }

    public void setNonCriticalLimit(int nonCriticalLimit) {
        this.nonCriticalLimit = Math.max(nonCriticalLimit, 2);
    }

    public void setUser(User user) {
        this.user = user;
    }

    private void writeOut() {
        if (sock != null && sock.isConnected()) {
            for (Supplier<Message> msg : consumingIterable(critical)) {
                writeOut(msg.get());
            }

            // Throttling
            for (Supplier<Message> msg : limit(concat(consumingIterable(normal), consumingIterable(junk)),
                    nonCriticalLimit)) {
                writeOut(msg.get());
            }

        }
    }

    private void writeOut(Message msg) {
        String msgout = msg.toProtocolString();
        log.info("<< {}", msgout);
        out.println(msgout);
    }
}