Package com.uwyn.drone.core

Source Code of com.uwyn.drone.core.Server

/*
* Copyright 2002-2005 Uwyn bvba/sprl <info[remove] at uwyn dot com>
* Distributed under the terms of the GNU Lesser General Public
* License, v2.1 or later
*
* $Id: Server.java 2286 2005-08-15 22:01:57Z gbevin $
*/
package com.uwyn.drone.core;

import com.uwyn.drone.core.exceptions.*;
import java.io.*;

import com.uwyn.drone.core.ServerListener;
import com.uwyn.drone.protocol.ServerMessage;
import com.uwyn.drone.protocol.commands.IrcCommand;
import com.uwyn.drone.protocol.commands.Ping;
import com.uwyn.rife.tools.ExceptionUtils;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CodingErrorAction;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.logging.Logger;

public class Server implements Runnable, TimedOutputStreamListener
{
  private String      mServerName = null;
  private ServerInfo    mServerInfo = null;
  private Thread      mServerThread = null;
  private Bot        mBot = null;
 
  private  Socket        mServerSocket = null;
  private BufferedReader    mInput = null;
  private BufferedWriter    mOutput = null;
  private TimedOutputStream  mTimedOutput = null;
  private HashMap        mChannels = null;
  private boolean        mConnected = false;
  private String        mConnectedHost = null;
  private int          mReadTimeOutCount = 0;
  private CharsetDecoder    mCharsetDecoder = null;
  private CharsetEncoder    mCharsetEncoder = null;

  private HashSet    mServerListeners = null;
  private HashSet    mCommandListeners = null;
  private HashSet    mResponseListeners = null;
  private Object    mServerListenersMonitor = new Object();
  private Object    mCommandListenersMonitor = new Object();
  private Object    mResponseListenersMonitor = new Object();
 
  Server(String serverName, ServerInfo serverInfo, Bot bot)
  {
    assert serverName != null;
    assert serverInfo != null;
    assert bot != null;
   
    mServerName = serverName.toLowerCase();
    mServerInfo = serverInfo;
    mBot = bot;
   
    mChannels = new HashMap();
    mServerListeners = new HashSet();
    mCommandListeners = new HashSet();
    mResponseListeners = new HashSet();

    cleanUp();
  }
 
  private void cleanUp()
  {
    mServerSocket = null;
    mInput = null;
    mOutput = null;
    mConnected = false;
    mConnectedHost = null;
  }
 
  public String getServerName()
  {
    return mServerName;
  }
 
  public ServerInfo getServerInfo()
  {
    return mServerInfo;
  }
 
  public Socket getServerSocket()
  {
    return mServerSocket;
  }
 
  public Channel getChannel(String name)
  {
    if (null == name)    throw new IllegalArgumentException("name can't be null.");
    if (0 == name.length())  throw new IllegalArgumentException("name can't be empty.");
   
    Channel channel = null;
   
    name = name.toLowerCase();
   
    synchronized (mChannels)
    {
      channel = (Channel)mChannels.get(name);
     
      if (null == channel)
      {
        channel = new Channel(name, this);
        mChannels.put(name, channel);
      }
    }
   
    assert channel != null;
    assert channel.getName().equals(name);
    assert channel.getServer() == this;
     
    return channel;
  }
 
  public synchronized void connect()
  throws CoreException
  {
    if (0 == mServerInfo.getAddresses().size())
    {
      throw new MissingAddressException(this);
    }
   
    InputStream      input_stream = null;
    Iterator      addresses_it = null;
    InetSocketAddress  address = null;
   
    boolean is_connected = false;
   
    while (!is_connected)
    {
      try
      {
        // try all addresses, one by one until a valid connection
        // could
        addresses_it = mServerInfo.getAddresses().iterator();
        while (addresses_it.hasNext())
        {
          address = (InetSocketAddress)addresses_it.next();
          try
          {
            mServerSocket = new Socket(address.getAddress(), address.getPort());
            break;
          }
          catch (IOException e)
          {
            mServerSocket = null;
          }
        }
       
        // no valid address was found, throw an error
        if (null == mServerSocket)
        {
          throw new NoValidAddressException(this);
        }
       
        // continue with the first valid address
        // and setup the socket timeout
        mServerSocket.setSoTimeout(mServerInfo.getTimeout());
       
        input_stream = mServerSocket.getInputStream();
        if (mTimedOutput != null)
        {
          mTimedOutput.close();
        }

        mTimedOutput = new TimedOutputStream(mServerSocket.getOutputStream(), mServerInfo.getMax(), mServerInfo.getAmount(), mServerInfo.getInterval());
        mTimedOutput.addTimedOutputStreamListener(this);
        is_connected = true;
      }
      catch (IOException e)
      {
        Logger.getLogger("com.uwyn.drone.core").severe("Error while connecting from the server '"+this+"', retrying : "+ExceptionUtils.getExceptionStackTrace(e));
      }
    }
   
    mCharsetDecoder = Charset.forName(mServerInfo.getCharset()).newDecoder();
    mCharsetDecoder.replaceWith("?");
    mCharsetDecoder.onMalformedInput(CodingErrorAction.REPLACE);
    mCharsetDecoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
    mCharsetEncoder = Charset.forName(mServerInfo.getCharset()).newEncoder();

    mInput = new BufferedReader(new InputStreamReader(input_stream, mCharsetDecoder));
    mOutput = new BufferedWriter(new OutputStreamWriter(mTimedOutput, mCharsetEncoder));

    mConnected = true;
    mServerThread = new Thread(this);
    mServerThread.start();
    fireConnected();
   
    assert mServerSocket.isConnected();
    assert mInput != null;
    assert mOutput != null;
  }
 
  public synchronized void disconnect()
  throws CoreException
  {
    mConnected = false;
    mConnectedHost = null;
   
    try
    {
      mInput = null;
     
      if (mOutput != null)
      {
        mOutput.close();
      }

      if (mServerSocket != null)
      {
        mServerSocket.close();
      }
    }
    catch (IOException e)
    {
      Logger.getLogger("com.uwyn.drone.core").severe("Error while disconnecting from the server '"+this+"', reconnecting : "+ExceptionUtils.getExceptionStackTrace(e));
      cleanUp();
      try
      {
        connect();
      }
      catch (CoreException e2)
      {
        throw new ServerDisconnectionErrorException(this, e2);
      }
     
      return;
    }
   
    this.notifyAll();
   
    cleanUp();
    fireDisconnected();
  }
 
  private void handleTerminatedStream(Throwable e)
  {
    if (mConnected)
    {
      mConnected = false;
      mConnectedHost = null;
      while (!mConnected)
      {
        if (e != null)
        {
          Logger.getLogger("com.uwyn.drone.core").severe("Error during the server connection, reconnecting : "+ExceptionUtils.getExceptionStackTrace(e));
        }
        else
        {
          Logger.getLogger("com.uwyn.drone.core").severe("Stream to IRC server has terminated, reconnecting.");
        }
       
        try
        {
          reconnect();
        }
        catch (CoreException e2)
        {
          Logger.getLogger("com.uwyn.drone.core").severe("Unable to restore connection after a terminated server stream : "+ExceptionUtils.getExceptionStackTrace(e2));
          try
          {
            // sleep 30 seconds before trying again
            Thread.sleep(30000);
          }
          catch (InterruptedException e3)
          {
            return;
          }
        }
      }
    }
   
    return;
  }
 
    public void run()
    {
    while (mConnected)
    {
      try
      {
        if (mInput != null)
        {
          try
          {
            String message_string = mInput.readLine();
            mReadTimeOutCount = 0;
           
            // check if a message has been received, if this is not the case,
            // the connection has been terminated and the stream also
            // try to reconnect until this succeeds
            if (null == message_string)
            {
              handleTerminatedStream(null);
              return;
            }
            // handle the received message
            else
            {
              ServerMessage message = ServerMessage.parse(message_string);
              // detect the hostname of the irc server
              if (null == mConnectedHost &&
                message.getPrefix() != null)
              {
                mConnectedHost = message.getPrefix().getServerName();
              }
              fireServerMessageEvents(message);
            }
          }
          catch (SocketTimeoutException e)
          {
            // if there were two read timeouts in a row, the connection is closed
            // and let the exception bubble up
            if (mReadTimeOutCount > 1)
            {
              throw e;
            }
           
            mReadTimeOutCount++;
            try
            {
              send(new Ping(mConnectedHost));
            }
            catch (CoreException e2)
            {
              throw e;
            }
          }
        }
      }
      catch (IOException e)
      {
        handleTerminatedStream(e);
        return;
      }
     
      Thread.yield();
    }
   
    mServerThread = null;
  }
 
  private void fireServerMessageEvents(ServerMessage message)
  {
    if (message.isResponse())
    {
      fireReceivedResponse(message);
    }
    if (message.isCommand())
    {
      fireReceivedCommand(message);
    }
  }
 
  public boolean isConnected()
  {
    return mConnected;
  }
 
  synchronized boolean send(IrcCommand command)
  throws CoreException
  {
    if (null == mServerSocket ||
      !mServerSocket.isConnected() ||
      mServerSocket.isOutputShutdown() ||
        !mServerSocket.isBound())
    {
      return false;
    }
   
    try
    {
      mOutput.write(command.getCommand());
      mOutput.newLine();
    }
    catch(IOException e)
    {
      throw new SendErrorException(this, command, e);
    }
   
    flush();
   
    fireServerMessageEvents(mBot.createServerMessage(command));
 
    return true;
  }
 
  public void reconnect()
  throws CoreException
  {
    synchronized (this)
    {
      disconnect();
      connect();
    }
  }
 
  public void exceptionThrow(IOException e)
  {
    try
    {
      reconnect();
    }
    catch (CoreException e2)
    {
      Logger.getLogger("com.uwyn.drone.core").severe("Unable to restore connection after socket error : "+ExceptionUtils.getExceptionStackTrace(e));
    }
  }
 
  synchronized private boolean flush()
  throws CoreException
  {
    if (null == mServerSocket ||
      !mServerSocket.isConnected() ||
      mServerSocket.isOutputShutdown())
    {
      return false;
    }
   
    try
    {
      mOutput.flush();
    }
    catch(IOException e)
    {
      throw new FlushErrorException(this, e);
    }
 
    return true;
  }

  private void fireConnected()
  throws CoreException
  {
    Iterator  listeners = mServerListeners.iterator();
   
    while (listeners.hasNext())
    {
      ((ServerListener)listeners.next()).connected(this);
    }
  }

  private void fireDisconnected()
  throws CoreException
  {
    Iterator  listeners = mServerListeners.iterator();
   
    while (listeners.hasNext())
    {
      ((ServerListener)listeners.next()).disconnected(this);
    }
  }

  private void fireReceivedCommand(ServerMessage command)
  {
    Iterator  listeners = mCommandListeners.iterator();
   
    while (listeners.hasNext())
    {
      try
      {
        ((CommandListener)listeners.next()).receivedCommand(command);
      }
      catch (CoreException e)
      {
        Logger.getLogger("com.uwyn.drone.core").severe(ExceptionUtils.getExceptionStackTrace(e));
      }
    }
  }

  private void fireReceivedResponse(ServerMessage Response)
  {
    Iterator  listeners = mResponseListeners.iterator();
   
    while (listeners.hasNext())
    {
      try
      {
        ((ResponseListener)listeners.next()).receivedResponse(Response);
      }
      catch (CoreException e)
      {
        Logger.getLogger("com.uwyn.drone.core").severe(ExceptionUtils.getExceptionStackTrace(e));
      }
    }
  }

  public boolean addServerListener(ServerListener listener)
  {
    if (null == listenerthrow new IllegalArgumentException("listener can't be null.");

    boolean result = false;
   
    synchronized (mServerListenersMonitor)
    {
      if (!mServerListeners.contains(listener))
      {
        HashSet clone = (HashSet)mServerListeners.clone();
        result = clone.add(listener);
        mServerListeners = clone;
      }
      else
      {
        result = true;
      }
    }
   
    assert true == mServerListeners.contains(listener);
   
    return result;
  }

  public boolean removeServerListener(ServerListener listener)
  {
    if (null == listenerthrow new IllegalArgumentException("listener can't be null.");

        boolean result = false;
   
    synchronized (mServerListenersMonitor)
    {
      HashSet clone = (HashSet)mServerListeners.clone();
      result = clone.remove(listener);
      mServerListeners = clone;
    }
   
    assert false == mServerListeners.contains(listener);
   
    return result;
  }

  public boolean addCommandListener(CommandListener listener)
  {
    if (null == listenerthrow new IllegalArgumentException("listener can't be null.");

    boolean result = false;
   
    synchronized (mCommandListenersMonitor)
    {
      if (!mCommandListeners.contains(listener))
      {
        HashSet clone = (HashSet)mCommandListeners.clone();
        result = clone.add(listener);
        mCommandListeners = clone;
      }
      else
      {
        result = true;
      }
    }
   
    assert true == mCommandListeners.contains(listener);
   
    return result;
  }

  public boolean removeCommandListener(CommandListener listener)
  {
    if (null == listenerthrow new IllegalArgumentException("listener can't be null.");

        boolean result = false;
   
    synchronized (mCommandListenersMonitor)
    {
      HashSet clone = (HashSet)mCommandListeners.clone();
      result = clone.remove(listener);
      mCommandListeners = clone;
    }
   
    assert false == mCommandListeners.contains(listener);
   
    return result;
  }

  public boolean addResponseListener(ResponseListener listener)
  {
    if (null == listenerthrow new IllegalArgumentException("listener can't be null.");

    boolean result = false;
   
    synchronized (mResponseListenersMonitor)
    {
      if (!mResponseListeners.contains(listener))
      {
        HashSet clone = (HashSet)mResponseListeners.clone();
        result = clone.add(listener);
        mResponseListeners = clone;
      }
      else
      {
        result = true;
      }
    }
   
    assert true == mResponseListeners.contains(listener);
   
    return result;
  }

  public boolean removeResponseListener(ResponseListener listener)
  {
    if (null == listenerthrow new IllegalArgumentException("listener can't be null.");

        boolean result = false;
   
    synchronized (mResponseListenersMonitor)
    {
      HashSet clone = (HashSet)mResponseListeners.clone();
      result = clone.remove(listener);
      mResponseListeners = clone;
    }
   
    assert false == mResponseListeners.contains(listener);
   
    return result;
  }
}
TOP

Related Classes of com.uwyn.drone.core.Server

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.