Package hamsam.protocol.msn

Source Code of hamsam.protocol.msn.MsnServer

/*
* Hamsam - Instant Messaging API
* Copyright (C) 2003 Raghu K
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package hamsam.protocol.msn;

import hamsam.exception.IllegalStateException;
import hamsam.net.Connection;
import hamsam.net.ProxyInfo;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.UnknownHostException;
import java.util.GregorianCalendar;
import java.util.Hashtable;
import java.util.Vector;

/**
* A server that connects to MSN and allows sending and receiving commands.
*/
abstract class MsnServer extends Thread
{
  /**
   * Connection used by this server.
   */
  private Connection conn;

  /**
   * The transaction ID to be used for next outgoing command.
   */
  private Integer nextTransactionID;

  /**
   * Mapping between an incoming command's transaction ID and the
   * method to invoke, in case of asynchronous commands.
   */
  private Hashtable callbackMap;

  /**
   * The transaction ID used for last synchronous command.
   */
  private Integer syncCallID;

  /**
   * The reply received for a synchronous command.
   */
  private AbstractCommand syncCallResult;

  /**
   * Buffer that holds all outgoing commands.
   */
  private Vector writeBuffer;
 
  /**
   * Buffer that holds all incoming commands.
   */
  private Vector readBuffer;

  /**
   * Thread that reads from conn and fills readBuffer.
   */
  private ReaderThread reader;

  /**
   * Thread that writes commands stored in writeBuffer to conn.
   */
  private WriterThread writer;

  /**
   * Constructs a MsnServer by connecting to a host at the specified
   * port using given information of the proxy server to be used.
   *
   * @param host the hostname of the server to connect to.
   * @param port the TCP/IP port number to connect to.
   * @param info the proxy server information.
   * @throws UnknownHostException if host is not known.
   * @throws IOException if an I/O error occurs while connecting to the host.
   * @throws IllegalStateException if info is not initialized properly.
   */
  protected MsnServer(String host, int port, ProxyInfo info) throws UnknownHostException, IOException, IllegalStateException
  {
    conn = info.getConnection(host, port);

    nextTransactionID = new Integer(1);
    callbackMap = new Hashtable();
    syncCallID = new Integer(0);

    readBuffer = new Vector();
    writeBuffer = new Vector();
    reader = new ReaderThread(this, conn, readBuffer);
    writer = new WriterThread(this, conn, writeBuffer);

    reader.start();
    writer.start();
    this.start();
  }

  /**
   * Sends this command to server by writing it to the write buffer. This method
   * does not wait for the reply from server. On the other hand, the method specified
   * will be invoked when a reply arrives.
   *
   * @param cmd the command to be sent.
   * @param method the name of the method that processes the reply for the command.
   */
  protected synchronized void sendToServer(AbstractCommand cmd, String method)
  {
    cmd.setTransactionID(nextTransactionID);
    callbackMap.put(nextTransactionID, method);
    int tmp = nextTransactionID.intValue();
    tmp = tmp == 2147483647 ? 1 : tmp + 1;
    nextTransactionID = new Integer(tmp);

    synchronized(writeBuffer)
    {
      writeBuffer.add(cmd);
      writeBuffer.notify();
    }
  }

  /**
   * Sends this command to server by writing it to the write buffer. This method
   * waits till a reply is received from server.
   *
   * @param cmd the command to be sent.
   * @return the reply received from the server, or null if the wait
   *         was interrupted.
   */
  protected synchronized AbstractCommand sendToServer(Command cmd)
  {
    cmd.setTransactionID(nextTransactionID);
    syncCallID = new Integer(nextTransactionID.intValue());

    int tmp = nextTransactionID.intValue();
    tmp = tmp == 2147483647 ? 1 : tmp + 1;
    nextTransactionID = new Integer(tmp);

    synchronized(writeBuffer)
    {
      writeBuffer.add(cmd);
      writeBuffer.notify();
    }

    synchronized(syncCallID)
    {
      try
      {
        syncCallID.wait();
      }
      catch(InterruptedException e)
      {
        return null;
      }
    }

    return syncCallResult;
  }

  /**
   * Shutdown this server by closing all connections.
   */
  protected void shutdown()
  {
    reader.stopThread();
    writer.stopThread();
    this.interrupt();
  }

  /**
   * Processes messages received from MSN server.
   */
  protected abstract void processMessage(MsnMessage msg);

  /**
   * Process asynchronous commands received from MSN server.
   */
  protected abstract void processAsyncCommand(AbstractCommand cmd);

  /**
   * Invoked when the reader thread associtated with this server exits
   * abnormally.
   */
  protected abstract void readerExited();

  /**
   * Invoked when the writer thread associtated with this server exits
   * abnormally.
   */
  protected abstract void writerExited();

  /**
   * Runs the thread that polls for incoming commands.
   */
  public void run()
  {
    GregorianCalendar prevPingTime = new GregorianCalendar();
    while(true)
    {
      if(this instanceof NotificationServer)
        pingIfRequired(prevPingTime);

      AbstractCommand[] commands = null;
      synchronized(readBuffer)
      {
        try
        {
          readBuffer.wait(2000);

          int size = readBuffer.size();
          if(size <= 0)
            continue;
          else
          {
            commands = (AbstractCommand[])readBuffer.toArray(new AbstractCommand[0]);
            readBuffer.removeAllElements();
          }
        }
        catch(InterruptedException e)
        {
          // Its time to quit
          return;
        }
      }

      for(int i = 0; i < commands.length; i++)
      {
        AbstractCommand cmd = commands[i];

        Integer transactionID = cmd.getTransactionID();
        if(cmd instanceof MsnMessage)
        {
          processMessage((MsnMessage) cmd);
        }
        else if(transactionID != null)
        {
          // See if this is a synchronous command
          synchronized(syncCallID)
          {
            if(transactionID.equals(syncCallID))
            {
              syncCallResult = cmd;
              syncCallID.notify();
              syncCallID = new Integer(0);
            }
          }

          String methodName = (String) callbackMap.get(transactionID);
          if(methodName != null)
            invokeMethod(methodName, cmd);
        }
        else
          processAsyncCommand(cmd);
      }
    }
  }

  /**
   * This method pings MSN server every two minutes.
   *
   * @param prevPingTime the last time when ping command was sent
   */
  private void pingIfRequired(GregorianCalendar prevPingTime)
  {
    GregorianCalendar now = new GregorianCalendar();
    prevPingTime.add(GregorianCalendar.SECOND, 119);
    if(now.after(prevPingTime))
    {
      Command png = new Command("PNG");

      synchronized(writeBuffer)
      {
        writeBuffer.add(png);
        writeBuffer.notify();
      }
      prevPingTime = now;
    }
    else
      prevPingTime.add(GregorianCalendar.SECOND, -119);
  }

  /**
   * Invokes a method defined in this instance (probably defined in a subclass),
   * using reflection. The method is expected to accept a single argument of type
   * AbstractCommand.
   *
   * @param name name of the method to be invoked.
   * @param cmd an abstract command to be passed to that method.
   */
  private void invokeMethod(String name, AbstractCommand cmd)
  {
    try
    {
      Class[] paramTypes = { AbstractCommand.class };
      Method method = this.getClass().getDeclaredMethod(name, paramTypes);

      Object[] args = { cmd };
      method.invoke(this, args);
    }
    catch(NoSuchMethodException e)
    {
      // This means we have not tested it properly
      e.printStackTrace();
      System.exit(1);
    }
    catch(IllegalAccessException e)
    {
      // This means we have not tested it properly
      e.printStackTrace();
      System.exit(1);
    }
    catch(InvocationTargetException e)
    {
      // This means we have not tested it properly
      e.printStackTrace();
      System.exit(1);
    }
  }
}
TOP

Related Classes of hamsam.protocol.msn.MsnServer

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.