| /******************************************************************************* |
| * Copyright (c) 2005, 2008 Remy Suen |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Remy Suen <remy.suen@gmail.com> - initial API and implementation |
| * Stoyan Boshev <s.boshev@prosyst.com> - [MSN] Session and subclasses needs to handle whitespace and exceptions better |
| ******************************************************************************/ |
| package org.eclipse.ecf.protocol.msn; |
| |
| import java.io.*; |
| import java.net.Socket; |
| import java.util.ArrayList; |
| |
| /** |
| * <p> |
| * An abstract base class that all other sessions should extend. This class |
| * provides common methods that a session will need to use such as reading and |
| * writing information from and to a socket. |
| * </p> |
| * |
| * <p> |
| * <b>Note:</b> This class/interface is part of an interim API that is still |
| * under development and expected to change significantly before reaching |
| * stability. It is being made available at this early stage to solicit feedback |
| * from pioneering adopters on the understanding that any code that uses this |
| * API will almost certainly be broken (repeatedly) as the API evolves. |
| * </p> |
| */ |
| abstract class Session { |
| |
| /** |
| * The client that this session is attached to. |
| */ |
| final MsnClient client; |
| |
| /** |
| * The list of listeners that have been connected to this session. |
| */ |
| ArrayList listeners; |
| |
| private final byte[] buffer = new byte[1024]; |
| |
| private Socket socket; |
| |
| private InputStream is; |
| |
| private OutputStream os; |
| |
| /** |
| * The number of transactions that has been transferred through this session |
| * thus far. This value will automatically increment when |
| * {@link #write(String, String)} or {@link #write(String, String, boolean)} |
| * has been invoked. |
| */ |
| private long transactionID = 0; |
| |
| /** |
| * A thread used to wait indefinitely for incoming messages from the server. |
| */ |
| private IdleThread idleThread; |
| |
| private boolean closed = false; |
| |
| Session(MsnClient client) { |
| this.client = client; |
| } |
| |
| /** |
| * Creates a session instance that will be connected to the given host. The |
| * string must be of the form '12.345.678.9:1234'. |
| * |
| * @param host |
| * the host to connect to |
| * @param client |
| * the client to hook onto |
| * @throws IOException |
| * If an I/O error occurred while attempting to connect to the |
| * given host. |
| */ |
| Session(String host, MsnClient client) throws IOException { |
| this.client = client; |
| openSocket(host); |
| } |
| |
| /** |
| * Creates a session instance that will be connected to the given host and |
| * port. |
| * |
| * @param ip |
| * the host to connect to |
| * @param port |
| * the port to connect to |
| * @param client |
| * the client to hook onto |
| * @throws IOException |
| * If an I/O error occurred while attempting to connect to the |
| * port at the given host. |
| */ |
| Session(String ip, int port, MsnClient client) throws IOException { |
| this.client = client; |
| openSocket(ip, port); |
| } |
| |
| /** |
| * Opens a connection to the specified host. |
| * |
| * @param host |
| * the host to connect to |
| * @throws IOException |
| * If an error occurs while attempting to connect to the host |
| */ |
| final void openSocket(String host) throws IOException { |
| closed = false; |
| int index = host.indexOf(':'); |
| openSocket(host.substring(0, index), Integer.parseInt(host.substring(index + 1))); |
| } |
| |
| final void openSocket(String ip, int port) throws IOException { |
| closed = false; |
| socket = new Socket(ip, port); |
| is = socket.getInputStream(); |
| os = socket.getOutputStream(); |
| } |
| |
| final InputStream getInputStream() { |
| return is; |
| } |
| |
| /** |
| * Reads data from the channel and returns it as a String. |
| * |
| * @return the contents that have been read, or <code>null</code> if |
| * nothing is currently available |
| * @throws IOException |
| * If an I/O error occurred while reading from the channel. |
| */ |
| String read() throws IOException { |
| int read = is.read(buffer); |
| if (read < 1) { |
| return null; |
| } |
| return new String(buffer, 0, read, "UTF-8"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * This method writes the given string input onto the channel. The carriage |
| * return and newline characters may be appended depending on the value of |
| * <code>newline</code>. |
| * |
| * @param input |
| * the String to be written |
| * @param newline |
| * <code>true</code> if a '\r\n' should be appended at the end |
| * of the write |
| * @throws IOException |
| * If an I/O error occurs while attempting to write to the |
| * channel. |
| */ |
| private final void write(String input, boolean newline) throws IOException { |
| byte[] bytes = newline ? (input + "\r\n").getBytes("UTF-8") : input.getBytes("UTF-8"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| os.write(bytes); |
| os.flush(); |
| } |
| |
| /** |
| * This method is synonymous with {@link #write(String, boolean)} with the |
| * exception that this method will always insert the carriage return and |
| * newline characters. |
| * |
| * @param input |
| * the String to be written |
| * @throws IOException |
| * If an I/O error occurs while attempting to write to the |
| * channel. |
| */ |
| final void write(String input) throws IOException { |
| write(input, true); |
| } |
| |
| /** |
| * Writes the given command with the specified parameters to the channel. A |
| * transaction identification number will also be inserted between the |
| * command and its parameters. A carriage return and a newline character |
| * will be inserted if <tt>newline</tt> is true. |
| * |
| * @param command |
| * the command to be inserted |
| * @param parameters |
| * additional parameters that are associated with the command |
| * @param newline |
| * <tt>true</tt> if a <tt>"\r\n"</tt> should be appended at |
| * the end of the write |
| * @throws IOException |
| * If an I/O error occurs while attempting to write to the |
| * channel. |
| */ |
| final void write(String command, String parameters, boolean newline) throws IOException { |
| transactionID++; |
| write(command + ' ' + transactionID + ' ' + parameters, newline); |
| } |
| |
| /** |
| * This method is synonymous with {@link #write(String, String, boolean)} |
| * with the exception that this method will always insert the carriage |
| * return and newline characters. |
| * |
| * @param command |
| * the command to be inserted |
| * @param parameters |
| * additional parameters that are associated with the command |
| * @throws IOException |
| * If an I/O error occurs while attempting to write to the |
| * channel. |
| */ |
| final void write(String command, String parameters) throws IOException { |
| write(command, parameters, true); |
| } |
| |
| void close() { |
| closed = true; |
| if (idleThread != null) { |
| idleThread.interrupt(); |
| idleThread = null; |
| } |
| |
| if (socket != null) { |
| try { |
| socket.close(); |
| } catch (Exception e) { |
| // ignored |
| } |
| socket = null; |
| } |
| |
| if (is != null) { |
| try { |
| is.close(); |
| } catch (Exception e) { |
| // ignored |
| } |
| is = null; |
| } |
| |
| if (os != null) { |
| try { |
| os.close(); |
| } catch (Exception e) { |
| // ignored |
| } |
| os = null; |
| } |
| } |
| |
| protected void finalize() throws Throwable { |
| close(); |
| super.finalize(); |
| } |
| |
| /** |
| * Wait in an infinite loop for information to be read in. |
| */ |
| final void idle() { |
| if (idleThread == null || !idleThread.isAlive()) { |
| idleThread = new IdleThread(); |
| idleThread.start(); |
| } |
| } |
| |
| final boolean isClosed() { |
| return closed; |
| } |
| |
| /** |
| * IdleThread waits for an indefinite amount of time for incoming messages. |
| */ |
| class IdleThread extends Thread { |
| |
| /** |
| * Begin waiting for incoming messages indefinitely. |
| */ |
| public void run() { |
| while (!isInterrupted()) { |
| try { |
| sleep(50); |
| read(); |
| } catch (InterruptedException e) { |
| return; |
| } catch (IOException e) { |
| return; |
| } catch (RuntimeException e) { |
| System.out.println("Exception occurred:"); //$NON-NLS-1$ |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
| } |