From d187c58f30b2ec2a1a1fa50208f7724a69f78390 Mon Sep 17 00:00:00 2001 From: snowleo Date: Wed, 12 Oct 2011 05:00:36 +0200 Subject: Replacing martyr with Pircbot 1.5 --- .../src/f00f/net/irc/martyr/IRCConnection.java | 1163 -------------------- 1 file changed, 1163 deletions(-) delete mode 100644 EssentialsUpdate/src/f00f/net/irc/martyr/IRCConnection.java (limited to 'EssentialsUpdate/src/f00f/net/irc/martyr/IRCConnection.java') diff --git a/EssentialsUpdate/src/f00f/net/irc/martyr/IRCConnection.java b/EssentialsUpdate/src/f00f/net/irc/martyr/IRCConnection.java deleted file mode 100644 index 159f533cd..000000000 --- a/EssentialsUpdate/src/f00f/net/irc/martyr/IRCConnection.java +++ /dev/null @@ -1,1163 +0,0 @@ -/* - * IRCConnection.java - * - * Copyright (C) 2000, 2001, 2002, 2003, 2004 Ben Damm - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * See: http://www.fsf.org/copyleft/lesser.txt - */ - -package f00f.net.irc.martyr; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.InetAddress; -import java.net.Socket; -import java.util.LinkedList; -import java.util.Observer; -import java.util.StringTokenizer; - -import f00f.net.irc.martyr.clientstate.ClientState; -import f00f.net.irc.martyr.commands.UnknownCommand; -import f00f.net.irc.martyr.errors.UnknownError; -import f00f.net.irc.martyr.replies.UnknownReply; -import java.util.logging.Level; -import java.util.logging.Logger; - -// TODO: -// -// Add synchronous disconnect. -// -/** - *

IRCConnection is the core class for Martyr. - * IRCConnection manages the socket, giving commands to the server - * and passing results to the parse engine. It manages passing information out - * to the application via the command listeners and state listeners. - * IRCConnection has no IRC intelligence of its own, that is left - * up to the classes on the command and state listener lists. A number of - * listeners that do various tasks are provided as part of the framework.

- * - *

Please read this entirely before using the framework. Or - * what the heck, try out the example below and see if it works for ya.

- * - *

States and State Listeners

- *

IRCConnection is always in one of three states. - * UNCONNECTED, UNREGISTERED or - * REGISTERED. It keeps a list of listeners that - * would like to know when a state change occurs. When a state change - * occurs each listener in the list, in the order they were added, is - * notified. If a listener early up on the list causes something to happen - * that changes the state before your listener gets notified, you will be - * notified of the state change even though the state has changed. You - * will be notified again of the new state. That is, state change - * notifications will always be in order, but they may not always reflect - * the "current" state.

- * - *

Commands and Command Listeners

- *

IRCConnection also keeps a list of listeners for - * when a command arrives from the server. When a command arrives, it - * is first parsed into an object. That object is then passed around - * to all the listeners, again, in order. Commands can be received - * and the socket closed before the commands are actually send to the - * listeners, so beware that even though you receive a command, you - * may not always be guaranteed to have an open socket to send a - * response back on. A consumer of the command should never modify - * the command object. If you try to send a command to a closed - * socket, IRCConnection will silently ignore your - * command. Commands should always be received in order by all - * listeners, even if a listener higher up in the list sends a - * response to the server which elicits a response back from the - * server before you've been told of the first command.

- * - *

Connecting and staying connected

- *

The AutoReconnect class can connect you and will try to stay - * connected. Using AutoReconnect to connect the - * first time is recommended, use the go(server,port) method once - * you are ready to start.

- * - *

Registration On The Network

- *

The AutoRegister class can register you automatically on the - * network. Otherwise, registration is left up to the consumer. - * Registration should occur any time the state changes to - * UNREGISTERED. The consumer will know this because it - * has registered some class as a state observer. - *

- * - *

Auto Response

- *

Some commands, such as Ping require an automatic response. - * Commands that fall into this category can be handled by the - * AutoResponder class. For a list of what commands - * AutoResponder auto responds to, see the source.

- * - *

Joining and Staying Joined

- *

You can use the AutoJoin class to join a channel - * and stay there. AutoJoin will try to re-join if - * kicked or if the connection is lost and the server re-connects. - * AutoJoin can be used any time a join is desired. If - * the server is not connected, it will wait until the server - * connects. If the server is connected, it will try to join right - * away.

- * - *

Example Usage

- *

You will probably want to at least use the - * AutoRegister and AutoResponder classes. - * Example:

- * - *

Note that in the example, the first line is optional. - * IRCConnection can be called with its default - * constructor. See note below about why this is done. - * IRCConnection will instantiate its own - * ClientState object if you do not provide one.

- * - *
- * ClientState clientState = new MyAppClientState();
- * IRCConnection connection = new IRCConnection( clientState );
- *
- * // AutoRegister and AutoResponder both add themselves to the
- * // appropriate observerables.  Both will remove themselves with the
- * // disable() method.
- * 
- * AutoRegister autoReg
- *   = new AutoRegister( "repp", "bdamm", "Ben Damm", connection );
- * AutoReconnect autoRecon = new AutoReconnect( connection );
- * AutoResponder autoRes = new AutoResponder( connection );
- *
- * // Very important that the objects above get added before the connect.
- * // If done otherwise, AutoRegister will throw an
- * // IllegalStateException, as AutoRegister can't catch the
- * // state switch to UNREGISTERED from UNCONNECTED.
- *
- * autoRecon.go( server, port );
- * 
- * - *

Client State

- *

The ClientStateMonitor class tells commands to - * change the client state when they are received. - * ClientStateMonitor is automatically added to the - * command queue before any other command, so that you can be - * guaranteed that the ClientState is updated before any - * observer sees a command.

- * - *

So, how does an application know when a channel has been joined, - * a user has joined a channel we are already on, etc? How does the - * application get fine-grained access to client state change info? - * This is a tricky question, and the current solution is to sublcass - * the clientstate.ClientState and - * clientstate.Channel classes with your own, overriding - * the setXxxxXxxx methods. Each method would call - * super.setXxxXxxx and then proceed to change the - * application as required.

- * - *

Startup

- *

IRCConnection starts in the UNCONNECTED state and - * makes no attempt to connect until the connect(...) - * method is called.

- * - *

IRCConnection starts a single thread at - * construction time. This thread simply waits for events. An event - * is a disconnection request or an incoming message. Events are - * dealt with by this thread. If connect is called, a second thread - * is created to listen for input from the server (InputHandler). - * - * @see f00f.net.irc.martyr.A_FAQ - * @see f00f.net.irc.martyr.clientstate.ClientState - * @see f00f.net.irc.martyr.services.AutoRegister - * @see f00f.net.irc.martyr.services.AutoResponder - * @see f00f.net.irc.martyr.State - * - */ -/* - * Event handling re-org - * - * - A message is an event - * - A disconnect request is an event, placed on the queue? - * -- Off I go to do other stuff. - */ -public class IRCConnection { - static Logger log = Logger.getLogger(IRCConnection.class.getName()); - - public IRCConnection() - { - this( new ClientState() ); - } - - public IRCConnection( ClientState clientState ) - { - // State observers are notified of state changes. - // Command observers are sent a copy of each message that arrives. - stateObservers = new StateObserver(); - commandObservers = new CommandObserver(); - this.clientState = clientState; - stateQueue = new LinkedList(); - - commandRegister = new CommandRegister(); - commandSender = new DefaultCommandSender(); - - setState( State.UNCONNECTED ); - - new ClientStateMonitor( this ); - - localEventQueue = new LinkedList(); - - eventThread = new EventThread(); - eventThread.setDaemon( true ); - startEventThread(); - } - - /** - * This method exists so that subclasses may perform operations before - * the event thread starts, but overriding this method. - * */ - protected void startEventThread() - { - eventThread.start(); - } - - /** - * In the event you want to stop martyr, call this. This asks the - * event thread to finish the current event, then die. - * */ - public void stop() - { - eventThread.doStop(); - } - - /** - * Performs a standard connection to the server and port. If we are already - * connected, this just returns. - * - * @param server Server to connect to - * @param port Port to connect to - * @throws IOException if we could not connect - */ - public void connect( String server, int port ) - throws IOException - { - synchronized( connectMonitor ) - { - //log.debug("IRCConnection: Connecting to " + server + ":" + port); - if( connected ) - { - log.severe("IRCConnection: Connect requested, but we are already connected!"); - return; - } - - connectUnsafe( new Socket( server, port ), server ); - } - } - - /** - * This allows the developer to provide a pre-connected socket, ready for use. - * This is so that any options that the developer wants to set on the socket - * can be set. The server parameter is passed in, rather than using the - * customSocket.getInetAddr() because a DNS lookup may be undesirable. Thus, - * the canonical server name, whatever that is, should be provided. This is - * then passed on to the client state. - * - * @param customSocket Custom socket that we will connect over - * @param server Server to connect to - * @throws IOException if we could not connect - * @throws IllegalStateException if we are already connected. - */ - public void connect( Socket customSocket, String server ) - throws IOException, IllegalStateException - { - synchronized( connectMonitor ) - { - if( connected ) - { - throw new IllegalStateException( "Connect requested, but we are already connected!" ); - } - - connectUnsafe( customSocket, server ); - } - - } - - /** - *

Orders the socket to disconnect. This doesn't actually disconnect, it - * merely schedules an event to disconnect. This way, pending incoming - * messages may be processed before a disconnect actually occurs.

- *

No errors are possible from the disconnect. If you try to disconnect an - * unconnected socket, your disconnect request will be silently ignored.

- */ - public void disconnect() - { - synchronized( eventMonitor ) - { - disconnectPending = true; - eventMonitor.notifyAll(); - } - } - - /** - * Sets the daemon status on the threads that IRCConnection - * creates. Default is true, that is, new InputHandler threads are - * daemon threads, although the event thread is always a daemon. The - * result is that as long as there is an active connection, the - * program will keep running. - * - * @param daemon Set if we are to be treated like a daemon - */ - public void setDaemon( boolean daemon ) - { - this.daemon = daemon; - } - - /** - * Signal threads to stop, and wait for them to do so. - * @param timeout *2 msec to wait at most for stop. - * - * */ - public void shutdown(long timeout) - { - // Note: UNTESTED! - try - { - // 1) shut down the input thread. - synchronized( inputHandlerMonitor ) - { - if( inputHandler != null ) - { - inputHandler.signalShutdown(); - } - synchronized( socketMonitor ) - { - if( socket != null ) - { - try - { - socket.close(); - } - catch (IOException e) - { - // surprising? - } - } - } - if( inputHandler != null ) - { - inputHandler.join(timeout); - } - } - - // 2) shut down the event thread. - eventThread.shutdown(); - eventThread.join(timeout); - } - catch( InterruptedException ie ) - { - // We got interrupted - while waiting for death. - // Shame that. - } - } - - public String toString() - { - return "IRCConnection"; - } - - public void addStateObserver( Observer observer ) - { - //log.debug("IRCConnection: Added state observer " + observer); - stateObservers.addObserver( observer ); - } - - public void removeStateObserver( Observer observer ) - { - //log.debug("IRCConnection: Removed state observer " + observer); - stateObservers.deleteObserver( observer ); - } - - public void addCommandObserver( Observer observer ) - { - //log.debug("IRCConnection: Added command observer " + observer); - commandObservers.addObserver( observer ); - } - - public void removeCommandObserver( Observer observer ) - { - //log.debug("IRCConnection: Removed command observer " + observer); - commandObservers.deleteObserver( observer ); - } - - - public State getState() - { - return state; - } - - public ClientState getClientState() - { - return clientState; - } - - /** - * Accepts a command to be sent. Sends the command to the - * CommandSender. - * - * @param command Command we will send - * */ - public void sendCommand( OutCommand command ) - { - commandSender.sendCommand( command ); - } - - /** - * @return the first class in a chain of OutCommandProcessors. - * */ - public CommandSender getCommandSender() - { - return commandSender; - } - - /** - * @param sender sets the class that is responsible for sending commands. - * */ - public void setCommandSender( CommandSender sender ) - { - this.commandSender = sender; - } - - /** - * @return the local address to which the socket is bound. - * */ - public InetAddress getLocalAddress() - { - return socket.getLocalAddress(); - } - - public String getRemotehost() - { - return clientState.getServer(); - } - - /** - * Sets the time in milliseconds we wait after each command is sent. - * - * @param sleepTime Length of time to sleep between commands - * */ - public void setSendDelay( int sleepTime ) - { - this.sendDelay = sleepTime; - } - - /** - * @since 0.3.2 - * @return a class that can schedule timer tasks. - * */ - public CronManager getCronManager() - { - if( cronManager == null ) - cronManager = new CronManager(); - return cronManager; - } - - /** - * Inserts into the event queue a command that was not directly - * received from the server. - * - * @param fakeCommand Fake command to inject into incoming queue - * */ - public void injectCommand( String fakeCommand ) - { - synchronized( eventMonitor ) - { - localEventQueue.add( fakeCommand ); - eventMonitor.notifyAll(); - } - } - - // ===== package methods ============================================= - - void socketError( IOException ioe ) - { - //log.debug("Socket error called."); - //log.debug("IRCConnection: The stack of the exception:", ioe); - - //log.log(Level.SEVERE, "Socket error", ioe); - disconnect(); - } - - /** - * Splits a raw IRC command into three parts, the prefix, identifier, - * and parameters. - * @param wholeString String to be parsed - * @return a String array with 3 components, {prefix,ident,params}. - * */ - public static String[] parseRawString( String wholeString ) - { - String prefix = ""; - String identifier; - String params = ""; - - StringTokenizer tokens = new StringTokenizer( wholeString, " " ); - - if( wholeString.charAt(0) == ':' ) - { - prefix = tokens.nextToken(); - prefix = prefix.substring( 1, prefix.length() ); - } - - identifier = tokens.nextToken(); - - if( tokens.hasMoreTokens() ) - { - // The rest of the string - params = tokens.nextToken(""); - } - - String[] result = new String[3]; - result[0] = prefix; - result[1] = identifier; - result[2] = params; - - return result; - } - - /** - * Given the three parts of an IRC command, generates an object to - * represent that command. - * - * @param prefix Prefix of command object - * @param identifier ID of command - * @param params Params of command - * @return An InCommand object for the given command object - * */ - protected InCommand getCommandObject( String prefix, String identifier, String params ) - { - InCommand command; - - // Remember that commands are also factories. - InCommand commandFactory = commandRegister.getCommand( identifier ); - if( commandFactory == null ) - { - if( UnknownError.isError( identifier ) ) - { - command = new UnknownError( identifier ); - log.warning("IRCConnection: Using " + command); - } - else if( UnknownReply.isReply( identifier ) ) - { - command = new UnknownReply( identifier ); - //log.warning("IRCConnection: Using " + command); - } - else - { - // The identifier doesn't map to a command. - log.warning("IRCConnection: Unknown command"); - command = new UnknownCommand(); - } - } - else - { - command = commandFactory.parse( prefix, identifier, params); - - if( command == null ) - { - log.severe("IRCConnection: CommandFactory[" + commandFactory + "] returned NULL"); - return null; - } - //log.debug("IRCConnection: Using " + command); - } - - return command; - } - - - - /** - * Executed by the event thread. - * - * @param wholeString String to be parsed and handled - * */ - void incomingCommand( String wholeString ) - { - //log.info("IRCConnection: RCV = " + wholeString); - - // 1) Parse out the command - String cmdBits[]; - - try - { - cmdBits = parseRawString( wholeString ); - } - catch( Exception e ) - { - // So.. we can't process the command. - // So we call the error handler. - handleUnparsableCommand( wholeString, e ); - return; - } - - String prefix = cmdBits[0]; - String identifier = cmdBits[1]; - String params = cmdBits[2]; - - // 2) Fetch command from factory - InCommand command = getCommandObject( prefix, identifier, params ); - command.setSourceString( wholeString ); - - // Update the state and send out to commandObservers - localCommandUpdate( command ); - } - - protected void handleUnparsableCommand( String wholeString, Exception e ) - { - log.log(Level.SEVERE, "Unable to parse server message.", e ); - } - - /** - * Called only in the event thread. - * - * @param command Command to update - * */ - private void localCommandUpdate( InCommand command ) - { - // 3) Change the connection state if required - // This allows us to change from UNREGISTERED to REGISTERED and - // possibly back. - State cmdState = command.getState(); - if( cmdState != State.UNKNOWN && cmdState != getState() ) - setState( cmdState ); - - // TODO: Bug here? - - // 4) Notify command observers - try - { - commandObservers.setChanged(); - commandObservers.notifyObservers( command ); - } - catch( Throwable e ) - { - log.log(Level.SEVERE, "IRCConnection: Command notify failed.", e); - } - - } - - // ===== private variables =========================================== - - /** Object used to send commands. */ - private CommandSender commandSender; - - private CronManager cronManager; - - /** State of the session. */ - private State state; - - /** - * Client state (our name, nick, groups, etc). Stored here mainly - * because there isn't anywhere else to stick it. - */ - private ClientState clientState; - - /** - * Maintains a list of classes observing the state and notifies them - * when it changes. - */ - private StateObserver stateObservers; - - /** - * Maintains a list of classes observing commands when they come in. - */ - private CommandObserver commandObservers; - - /** - * The actual socket used for communication. - */ - private Socket socket; - - /** - * Monitor access to socket. - * */ - private final Object socketMonitor = new Object(); - - /** - * We want to prevent connecting and disconnecting at the same time. - */ - private final Object connectMonitor = new Object(); - - /** - * This object should be notified if we want the main thread to check for - * events. An event is either an incoming message or a disconnect request. - * Sending commands to the server is synchronized by the eventMonitor. - */ - private final Object eventMonitor = new Object(); - - /** - * This tells the processEvents() method to check if we should disconnect - * after processing all incoming messages. - */ - // Protected by: - // inputHandlerMonitor - // eventMonitor - // connectMonitor - private boolean disconnectPending = false; - - /** - * The writer to use for output. - */ - private BufferedWriter socketWriter; - - /** - * Command register, contains a list of commands that can be received - * by the server and have matching Command objects. - */ - private CommandRegister commandRegister; - - /** - * Maintains a handle on the input handler. - */ - private InputHandler inputHandler; - - /** - * Access control for the input handler. - */ - private final Object inputHandlerMonitor = new Object(); - - /** - * State queue keeps a queue of requests to switch state. - */ - private LinkedList stateQueue; - - /** - * localEventQueue allows events not received from the server to be - * processed. - * */ - private LinkedList localEventQueue; - - /** - * Setting state prevents setState from recursing in an uncontrolled - * manner. - */ - private boolean settingState = false; - - /** - * Event thread waits for events and executes them. - */ - private EventThread eventThread; - - /** - * Determines the time to sleep every time we send a message. We do this - * so that the server doesn't boot us for flooding. - */ - private int sendDelay = 300; - - /** - * connected just keeps track of whether we are connected or not. - */ - private boolean connected = false; - - /** - * Are we set to be a daemon thread? - */ - private boolean daemon = false; - - // ===== private methods ============================================= - - /** - * Unsafe, because this method can only be called by a method that has a lock - * on connectMonitor. - * - * @param socket Socket to connect over - * @param server Server to connect to - * @throws IOException if connection fails - */ - private void connectUnsafe( Socket socket, String server ) - throws IOException - { - synchronized(socketMonitor) - { - this.socket = socket; - } - - socketWriter = - new BufferedWriter( new OutputStreamWriter( socket.getOutputStream() ) ); - - /** - * The reader to use for input. Managed by the InputHandler. - */ - BufferedReader socketReader = - new BufferedReader( new InputStreamReader( socket.getInputStream() ) ); - - // A simple thread that waits for input from the server and passes - // it off to the IRCConnection class. - //if( inputHandler != null ) - //{ - // log.fatal("IRCConnection: Non-null input handler on connect!!"); - // return; - //} - - synchronized( inputHandlerMonitor ) - { - // Pending events get processed after a disconnect call, and there - // shouldn't be any events generated while disconnected, so it makes - // sense to test for this condition. - if( inputHandler != null && inputHandler.pendingMessages() ) - { - log.severe("IRCConnection: Tried to connect, but there are pending messages!"); - return; - } - - if( inputHandler != null && inputHandler.isAlive() ) - { - log.severe("IRCConnection: Tried to connect, but the input handler is still alive!"); - return; - } - - clientState.setServer( server ); - clientState.setPort( socket.getPort() ); - - connected = true; - - inputHandler = new InputHandler( socketReader, this, eventMonitor ); - inputHandler.setDaemon( daemon ); - inputHandler.start(); - } - setState( State.UNREGISTERED ); - } - - private class EventThread extends Thread - { - private boolean doShutdown = false; - - public EventThread() - { - super("EventThread"); - } - - public void run() - { - handleEvents(); - } - - public void shutdown() - { - synchronized(eventMonitor) - { - this.doShutdown = true; - eventMonitor.notifyAll(); - } - } - - private void handleEvents() - { - try - { - while( true ) - { - // Process all events in the event queue. - //log.debug("IRCConnection: Processing events"); - while( processEvents() ) { } - - // We can't process events while synchronized on the - // eventMonitor because we may end up in deadlock. - synchronized( eventMonitor ) - { - if( !doShutdown && !pendingEvents() ) - { - eventMonitor.wait(); - } - - if( doShutdown ) - { - return; - } - } - } - } - catch( InterruptedException ie ) - { - log.log(Level.WARNING, "Interrupted while handling events.", ie ); - // And we do what? - // We die, that's what we do. - } - } - - public void doStop() - { - shutdown(); - } - - public String toString() - { - return "EventThread"; - } - } - - /** - * This method synchronizes on the inputHandlerMonitor. Note that if - * additional event types are processed, they also need to be added to - * pendingEvents(). - * @return true if events were processed, false if there were no events to - * process. - */ - private boolean processEvents() - { - boolean events = false; - - // the inputHandlerMonitor here serves two purposes: To protect - // from inputHandler changes and to ensure only one thread is - // operating in processEvents. - // - // Perhaps a different monitor should be used? - synchronized( inputHandlerMonitor ) - { - while( inputHandler != null && inputHandler.pendingMessages() ) - { - String msg = inputHandler.getMessage(); - incomingCommand( msg ); - events = true; - } - - while( localEventQueue != null && !localEventQueue.isEmpty() ) - { - String msg = localEventQueue.removeFirst(); - incomingCommand( msg ); - events = true; - } - - if( disconnectPending ) - { - //log.debug("IRCConnection: Process events: Disconnect pending."); - doDisconnect(); - events = true; - } - } - - return events; - } - - /** - * Does no synchronization on its own. This does not synchronize on - * any of the IRCConnection monitors or objects and returns after making a - * minimum of method calls. - * @return true if there are pending events that need processing. - */ - private boolean pendingEvents() - { - if( inputHandler != null && inputHandler.pendingMessages() ) - return true; - if( disconnectPending ) - return true; - if( localEventQueue != null && !localEventQueue.isEmpty() ) - return true; - - return false; - } - - // Synchronized by inputHandlerMonitor, called only from processEvents. - private void doDisconnect() - { - synchronized( connectMonitor ) - { - disconnectPending = false; - - if( !connected ) - { - return; - } - connected = false; - - try - { - final long startTime = System.currentTimeMillis(); - final long sleepTime = 1000; - final long stopTime = startTime + sleepTime; - //log.debug("IRCConnection: Sleeping for a bit (" - // + sleepTime + ").."); - // Slow things down a bit so the server doesn't kill us - // Also, we want to give a second to let any pending messages - // get processed and any pending disconnect() calls to be made. - // It is important that we use wait instead of sleep! - while( stopTime - System.currentTimeMillis() > 0 ) - { - connectMonitor.wait( stopTime - System.currentTimeMillis() ); - } - } - catch( InterruptedException ie ) - { - // Ignore - } - - //log.debug("IRCConnection: Stopping input handler."); - // Deprecated? - // inputHandler.stop(); - // inputHandler = null; - - //log.debug("IRCConnection: Closing socket."); - try - { - socket.close(); - } - catch( IOException ioe ) - { - // And we are supposed to do what? - // This probably means we've called disconnect on a closed - // socket. - handleSocketCloseException( ioe ); - return; - } - finally - { - connected = false; - } - } - - // The input handler should die, because we closed the socket. - // We'll wait for it to die. - synchronized( inputHandlerMonitor ) - { - //log.debug("IRCConnection: Waiting for the input handler to die.."); - try - { - // log.debug("IRCConnection: Stack:"); - - if( inputHandler.isAlive() ) - inputHandler.join(); - else - { - //log.debug("IRCConnection: No waiting required, input hander is already dead."); - } - } - catch( InterruptedException ie ) - { - //log.debug("IRCConnection: Error in join(): " + ie); - } - //log.debug("IRCConnection: Done waiting for the input handler to die."); - } - - - // There may be pending messages that we should process before we - // actually notify all the state listeners. - processEvents(); - - // It is important that the state be switched last. One of the - // state listeners may try to re-connect us. - setState( State.UNCONNECTED ); - - } - - protected void handleSocketCloseException( IOException ioe ) - { - log.log(Level.WARNING, "Error closing socket.", ioe ); - } - - /** - * Signals to trigger a state change. Won't actually send a state change - * until a previous attempt at changing the state finishes. This is - * important if one of the state listeners affects the state (ie tries to - * reconnect if we disconnect, etc). - * - * @param newState New state to set connection to. - */ - private void setState( State newState ) - { - if( settingState ) - { - // We are already setting the state. We want to complete changing - // to one state before changing to another, so that we don't have - // out-of-order state change signals. - stateQueue.addLast( newState ); - return; - } - - settingState = true; - - if( state == newState ) - return; - - while( true ) - { - state = newState; - - //log.debug("IRCConnection: State switch: " + state); - - try - { - stateObservers.setChanged(); - stateObservers.notifyObservers( newState ); - } - catch( Throwable e ) - { - log.log(Level.SEVERE, "IRCConnection: State update failed.", e); - } - - if( stateQueue.isEmpty() ) - break; - newState = stateQueue.removeFirst(); - } - - settingState = false; - } - - private class DefaultCommandSender implements CommandSender - { - public CommandSender getNextCommandSender() - { - return null; - } - - public void sendCommand( OutCommand oc ) - { - finalSendCommand( oc.render() ); - } - } - - /** - * Sends the command down the socket, with the required 'CRLF' on the - * end. Waits for a little bit after sending the command so that we don't - * accidentally flood the server. - * - * @param str String to send - */ - private void finalSendCommand( String str ) - { - try - { - synchronized( eventMonitor ) - { - //log.info("IRCConnection: SEND= " + str); - - if( disconnectPending ) - { - //log.debug("IRCConnection: Send cancelled, disconnect pending."); - return; - } - - socketWriter.write( str + "\r\n" ); - socketWriter.flush(); - - try - { - // Slow things down a bit so the server doesn't kill us - // We do this after the send so that if the send fails the - // exception is handled right away. - Thread.sleep( sendDelay ); - } - catch( InterruptedException ie ) - { - // Ignore - } - } - } - catch( IOException ioe ) - { - socketError( ioe ); - } - } - // ----- END IRCConnection ------------------------------------------- -} -- cgit v1.2.3