Java tutorial
/* * 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); } }