diff --git a/src/fm/last/irccat/CatHandler.java b/src/fm/last/irccat/CatHandler.java index 8020bc0..b4ca34f 100644 --- a/src/fm/last/irccat/CatHandler.java +++ b/src/fm/last/irccat/CatHandler.java @@ -17,85 +17,109 @@ */ package fm.last.irccat; -import java.net.*; -import java.io.*; +import java.util.List; +import java.util.LinkedList; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.Socket; -// passes command to external program and returns results back to irc -class CatHandler extends Thread { - - IRCCat bot; - Socket sock; +/** + * Utility class to handle IRC cat output text. + * 1) Handle parsing of initial line of the outgoing text + * which can contain metadata about the recipients of + * the message (channels or users) and whether its a /topic msg. + * + * 2) call the IRC cat bot the caller passes in, sending the text lines. + */ +class CatHandler implements Runnable { + public static final String TOPIC_TOKEN = "%TOPIC"; + public static final String ALL_CHAN_TOKEN = "#*"; - CatHandler(Socket s, IRCCat b){ - sock = s; - bot = b; - } - - public void run(){ - try{ - BufferedReader in = new BufferedReader( - new InputStreamReader( - sock.getInputStream(), "UTF-8")); - String inputLine = new String(); - String recipients[] = null; - boolean all = false; - boolean topic = false; - int i = 0; - while ((inputLine = in.readLine()) != null) { - if(i++==0){ - String[] words = inputLine.split(" "); - if(words[0].equals("%TOPIC")) { - topic = true; - inputLine = inputLine.substring(7); - String[] newwords = new String[words.length-1]; - System.arraycopy(words, 1, newwords, 0, newwords.length); - words = newwords; - } - if(words[0].equals("#*")){ - // send to all channels - all = true; - inputLine = inputLine.substring(3); - }else - if(words[0].startsWith("#") || words[0].startsWith("@")){ - String addressees[] = words[0].split(","); - for(int j=0; j lines = new LinkedList(); + private final IRCCat bot; + private final Socket socket; -} + private boolean isBroadcastMessage; + private boolean isTopicMessage; + private String[] recipients = new String[0]; + + public CatHandler(Socket s, IRCCat b) { + this.socket = s; + this.bot = b; + } + + @Override + public void run() { + try { + getLinesFromStream(); + checkForTopicMessage(); + populateRecipientList(); + sendLines(); + //System.out.println("Handler finished."); + } catch(Exception e) { + e.printStackTrace(); + } + } + + private void sendLines() { + for ( String line : lines ) { + if ( isBroadcastMessage && isTopicMessage ) { + bot.catTopicToAll(line); + } else if ( isTopicMessage ) { + bot.catTopic(line, recipients); + } else if ( isBroadcastMessage ) { + bot.catStuffToAll(line); + } else { // neither topic or broadcast + bot.catStuff(line, recipients); + } + } + } + private void getLinesFromStream() throws Exception { + try { + BufferedReader in = new BufferedReader( + new InputStreamReader(socket.getInputStream(), "UTF-8") + ); + String line; + while ( null != (line = in.readLine()) ) { + lines.add( line.trim() ); + } + } finally { + if ( null != socket ) { + socket.close(); + } + } + } + + private void checkForTopicMessage() { + if ( lines.get(0).startsWith(TOPIC_TOKEN) ) { + isTopicMessage = true; + lines.set( 0, lines.get(0).substring(7) ); + } + } + + private void populateRecipientList() { + String firstLine = lines.get(0); + if( firstLine.startsWith(ALL_CHAN_TOKEN) ) { + isBroadcastMessage = true; + lines.set( 0, firstLine.substring(3) ); + } else if ( firstLine.startsWith("#") || firstLine.startsWith("@") ) { + int length = 0, index = 0; + recipients = firstLine.split(","); + while ( index < recipients.length ) { + length += recipients[index].length(); // track size of text block + recipients[index] = recipients[index].trim(); + if ( recipients[index].startsWith("@") ) { + // to a user, strip the @ for sendMessage() + recipients[index] = recipients[index].substring(1); + } + ++index; + } + lines.set( 0, lines.get(0).substring(length + index) ); + } else { + recipients = new String[1]; + recipients[0] = bot.getDefaultChannel(); + } + } +} diff --git a/src/fm/last/irccat/IRCCat.java b/src/fm/last/irccat/IRCCat.java index b269b67..860b9b5 100644 --- a/src/fm/last/irccat/IRCCat.java +++ b/src/fm/last/irccat/IRCCat.java @@ -21,18 +21,27 @@ import org.jibble.pircbot.*; import java.net.*; import java.io.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; import java.util.List; import java.util.Map; import java.util.HashMap; public class IRCCat extends PircBot { + // we could use this for situations where an ordered sequence of tasks should + // be run on a single worker thread in serial order of task submission. + //static final ExecutorService threadPoolManager = Executors.newSingleThreadedExecutor(); + // a thread pool manager to handle our thread lifecycles + static final ExecutorService threadPoolManager = Executors.newCachedThreadPool(); + private String nick; private String cmdScript; private String defaultChannel = null; private int maxCmdResponseLines = 26; private XMLConfiguration config; + public static void main(String[] args) throws Exception { try { if (args.length == 0) { @@ -79,8 +88,7 @@ public static void main(String[] args) throws Exception { Socket clientSocket = serverSocket.accept(); // System.out.println("Connection on catport from: " // + clientSocket.getInetAddress().toString()); - CatHandler handler = new CatHandler(clientSocket, bot); - handler.start(); + threadPoolManager.submit( new CatHandler(clientSocket, bot) ); } catch (Exception e) { e.printStackTrace(); } @@ -88,7 +96,11 @@ public static void main(String[] args) throws Exception { } catch (Exception e) { e.printStackTrace(); - } + } finally { + if (null != threadPoolManager) { + threadPoolManager.shutdown(); + } + } } @@ -104,7 +116,6 @@ public IRCCat(XMLConfiguration c) throws Exception { setFinger(config.getString("bot.finger", "IRCCat - a development support bot, used by Last.fm")); - try { // connect to server int tries =0 ; @@ -310,9 +321,8 @@ public void handleMessage(String channel_, String sender, String message) { // now "cmd" contains the message, minus the address prefix (eg: ?) // hand off msg to thread that executes shell script - System.out.println("Scripter: ["+respondTo+"] <"+sender+"> "+message); - Thread t = new Scripter(sender, channel_, respondTo, cmd, this); - t.run(); + System.out.println("Scripter: ["+respondTo+"] <"+sender+"> "+message); + threadPoolManager.submit( new Scripter(sender, channel_, respondTo, cmd, this) ); } /* diff --git a/src/fm/last/irccat/Scripter.java b/src/fm/last/irccat/Scripter.java index bc5d230..1d073c6 100644 --- a/src/fm/last/irccat/Scripter.java +++ b/src/fm/last/irccat/Scripter.java @@ -17,41 +17,55 @@ */ package fm.last.irccat; -import java.io.*; +import java.io.InputStreamReader; +import java.io.BufferedReader; +import java.util.List; +import java.util.LinkedList; // hands off cmd to shell script and returns stdout to the requester -class Scripter extends Thread { - IRCCat bot; - String nick, channel, returnName, cmd; - - Scripter(String nk, String ch, String r, String c, IRCCat b){ - nick = nk; - channel = ch; - cmd = c; - returnName = r; - bot = b; - } +class Scripter implements Runnable { + private final IRCCat bot; + private final String nick, channel, returnName, cmd; + + public Scripter( String nk, String ch, String r, String c, IRCCat b ) { + this.nick = nk; + this.channel = ch; + this.cmd = c; + this.returnName = r; + this.bot = b; + } + + private Process startProcess() throws Exception { + String message = nick + " " + channel + " " + returnName + " " + " " + cmd; + return new ProcessBuilder(bot.getCmdScript(), message).start(); + } - public void run(){ - try{ - Runtime runtime = Runtime.getRuntime(); - Process process = runtime.exec(new String[]{bot.getCmdScript() ,nick + " " + channel + " " + returnName+" "+cmd}); - InputStream is = process.getInputStream(); - InputStreamReader isr = new InputStreamReader(is, "UTF-8"); - BufferedReader br = new BufferedReader(isr); - String line; - int i=0; - while ((line = br.readLine()) != null) { - bot.sendMsg(returnName, line); - if(++i==bot.getCmdMaxResponseLines()){ - bot.sendMsg(returnName, ""); - break; - } - } - }catch(Exception e){ - e.printStackTrace(); - } - + @Override + public void run() { + BufferedReader reader = null; + Process process = null; + try { + process = startProcess(); + reader = new BufferedReader( new InputStreamReader(process.getInputStream(), "UTF-8") ); + String line; + int lineCount = 0; + while ( (line = reader.readLine()) != null) { + bot.sendMsg(returnName, line); + if ( ++lineCount == bot.getCmdMaxResponseLines() ) { + bot.sendMsg(returnName, ""); + break; } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if ( null != reader ) { + try { reader.close(); } catch (Exception ignored) { } + } + if ( null != process ) { + try { process.destroy(); } catch (Exception ignored) { } + } + } + } }