Java tutorial
/** * Copyright (C) 2010-2014 Leon Blakey <lord.quackstar at gmail.com> * * This file is part of PircBotX. * * PircBotX is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * PircBotX 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along with * PircBotX. If not, see <http://www.gnu.org/licenses/>. */ package org.pircbotx; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.LinkedList; import java.util.Queue; import lombok.Cleanup; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.experimental.Delegate; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DurationFormatUtils; import org.apache.commons.lang3.time.StopWatch; import org.pircbotx.hooks.Event; import org.pircbotx.hooks.ListenerAdapter; import org.pircbotx.hooks.managers.GenericListenerManager; import org.pircbotx.hooks.managers.ListenerManager; import org.pircbotx.hooks.types.GenericMessageEvent; /** * Helpful server for replaying a raw log to the bot. * * @author Leon Blakey */ @Slf4j public class ReplayServer { /** * Redirect output to given queue and trick code to believe its connected to * the IRC server */ @Slf4j protected static class ReplayPircBotX extends PircBotX { protected final Queue<String> outputQueue; @Getter protected boolean closed = false; public ReplayPircBotX(Configuration configuration, Queue<String> outputQueue) { super(configuration); this.outputQueue = outputQueue; } @Override protected void sendRawLineToServer(String line) throws IOException { outputQueue.add(line); } @Override public boolean isConnected() { return true; } @Override public void close() { closed = true; } } /** * Run all listeners in main thread and seperately queue events */ @RequiredArgsConstructor @Slf4j protected static class WrapperListenerManager extends ListenerManager { private static interface ImplExclude { public void dispatchEvent(Event event); } @Delegate(excludes = ImplExclude.class) protected final ListenerManager impl; protected final Queue<Event> eventQueue; @Override public void dispatchEvent(Event event) { eventQueue.add(event); impl.dispatchEvent(event); } } public static void main(String[] args) throws Exception { try { //Make sure the user specified a file if (args.length != 1 || args[0].trim().length() == 0) { System.out.println("Usage: org.pircbotx.impl.ReplayServer [log]"); System.exit(1); } //Start replaying file File file = new File(args[0].trim()); replayFile(file); } catch (Exception t) { log.debug("Caught exception in main, closing", t); System.exit(3); } } static class ReplayListener extends ListenerAdapter { @Override public void onGenericMessage(GenericMessageEvent event) throws Exception { if (event.getMessage().startsWith("?dumpusers")) { System.out.println("===command dumpusers start==="); for (User curUser : event.getBot().getUserChannelDao().getAllUsers()) log.debug(curUser.getNick() + "!" + curUser.getLogin() + "@" + curUser.getHostname() + " - " + curUser.getHostmask()); System.out.println("===command dumpusers end==="); } } } public static void replayFile(File file) throws Exception { replayFile(file, generateConfig()); } public static void replayFile(File file, Configuration.Builder config) throws Exception { if (!file.exists()) { throw new IOException("File " + file + " does not exist"); } @Cleanup FileInputStream fileInput = new FileInputStream(file); replay(config, fileInput, "file " + file.getCanonicalPath()); } public static Configuration.Builder generateConfig() { return new Configuration.Builder().setName("QuackPirc").setLogin("QP").addServer("example.com") .setNickservPassword(System.getProperty("nickserv")).setMessageDelay(0) .setListenerManager(new GenericListenerManager()).setShutdownHookEnabled(false); } public static void replay(Configuration.Builder config, InputStream input, String title) throws Exception { log.info("---Replaying {}---", title); StopWatch timer = new StopWatch(); timer.start(); //Wrap listener manager with ours that siphons off events final Queue<Event> eventQueue = Lists.newLinkedList(); WrapperListenerManager newManager = new WrapperListenerManager(config.getListenerManager(), eventQueue); config.setListenerManager(newManager); config.addListener(new ReplayListener()); final LinkedList<String> outputQueue = Lists.newLinkedList(); ReplayPircBotX bot = new ReplayPircBotX(config.buildConfiguration(), outputQueue); BufferedReader fileInput = new BufferedReader(new InputStreamReader(input)); boolean skippedHeader = false; while (true) { String lineRaw = fileInput.readLine(); if (bot.isClosed() && StringUtils.isNotBlank(lineRaw)) { throw new RuntimeException("bot is closed but file still has line " + lineRaw); } else if (!bot.isClosed() && StringUtils.isBlank(lineRaw)) { throw new RuntimeException("bot is not closed but file doesn't have any more lines"); } else if (bot.isClosed() && StringUtils.isBlank(lineRaw)) { log.debug("(done) Bot is closed and file doesn't have any more lines"); break; } log.debug("(line) " + lineRaw); String[] lineParts = StringUtils.split(lineRaw, " ", 2); String command = lineParts[0]; String line = lineParts[1]; //For now skip the info lines PircBotX is supposed to send on connect //They are only sent when connect() is called which requires multithreading if (!skippedHeader) { if (command.equals("pircbotx.output")) continue; else if (command.equals("pircbotx.input")) { log.debug("Finished skipping header"); skippedHeader = true; } else throw new RuntimeException("Unknown line " + lineRaw); } if (command.equals("pircbotx.input")) { bot.getInputParser().handleLine(line); } else if (command.equals("pircbotx.output")) { String lastOutput = outputQueue.isEmpty() ? null : outputQueue.pop(); if (StringUtils.startsWith(line, "JOIN")) { log.debug("Skipping JOIN output, server should send its own JOIN"); } else if (StringUtils.startsWith(line, "QUIT")) { log.debug("Skipping QUIT output, server should send its own QUIT"); } else if (!line.equals(lastOutput)) { log.error("Expected last output: " + line); log.error("Given last output: " + lastOutput); for (String curOutput : outputQueue) { log.error("Queued output: " + curOutput); } throw new RuntimeException("Failed to verify output (see log)"); } } else { throw new RuntimeException("Unknown line " + lineRaw); } for (Event curEvent : Iterables.consumingIterable(eventQueue)) log.debug("(events) " + curEvent); log.debug(""); } timer.stop(); log.debug("---Replay successful in {}---", DurationFormatUtils.formatDuration(timer.getTime(), "mm'min'ss'sec'SSS'ms'")); } }