Package org.objectweb.joram.mom.proxies.tcp

Source Code of org.objectweb.joram.mom.proxies.tcp.TcpConnectionListener$NetOutputStream

/*
* JORAM: Java(TM) Open Reliable Asynchronous Messaging
* Copyright (C) 2004 - 2009 ScalAgent Distributed Technologies
* Copyright (C) 2004 France Telecom R&D
*
* 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 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.
*
* Initial developer(s): ScalAgent Distributed Technologies
* Contributor(s):
*/
package org.objectweb.joram.mom.proxies.tcp;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import org.objectweb.joram.mom.dest.AdminTopic;
import org.objectweb.joram.mom.notifications.GetProxyIdNot;
import org.objectweb.joram.mom.proxies.GetConnectionNot;
import org.objectweb.joram.mom.proxies.OpenConnectionNot;
import org.objectweb.joram.mom.proxies.ReliableConnectionContext;
import org.objectweb.joram.shared.security.Identity;
import org.objectweb.joram.shared.stream.MetaData;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

import fr.dyade.aaa.agent.AgentId;
import fr.dyade.aaa.common.Configuration;
import fr.dyade.aaa.common.Daemon;
import fr.dyade.aaa.common.Debug;
import fr.dyade.aaa.common.stream.StreamUtil;



/**
* Listens to the TCP connections from the JMS clients.
* Creates a <code>TcpConnection</code> for each
* accepted TCP connection.
* Opens the <code>UserConnection</code> with the
* right user's proxy.
*/
public class TcpConnectionListener extends Daemon {
  /** logger */
  public static Logger logger = Debug.getLogger(TcpConnectionListener.class.getName());

  /**
   * The TCP proxy service
   */
  private TcpProxyService proxyService;

  private int timeout;

  /**
   *  Name of the property allowing to change the threshold of warning for the
   * verification of the synchronization between the client and server clock.
   *  A warning is generated if there is more than this value in milliseconds
   * between the two clocks.
   * <p>
   *  By default the value is 1000 milliseconds.
   */
  public static final String CLOCK_SYNCHRO_THRESHOLD = "org.objectweb.joram.TcpConnection.ClockSynchro.Threshold";

  /**
   *  Value of the threshold of warning for the verification of the synchronization
   * between the client and server clock.
   *  A warning is generated if there is more than this value in milliseconds between
   * the two clocks.
   * <p>
   *  By default the value is 1000 milliseconds.
   */
  private long clockSynchroThreshold = 1000L;

  /**
   * Number of times this connection listener has encountered an erroneous
   * authentication.
   */
  private int failedLoginCount;

  /**
   * Number of connections started with this connection listener.
   */
  private int connectionCount;

  /**
   * Number of times this connection listener has encountered an erroneous magic
   * number or protocol version.
   */
  private int protocolErrorCount;

  /**
   * Creates a new connection listener
   *
   * @param proxyService  the TCP proxy service associated with this connection listener
   * @param timeout       the timeout
   */
  public TcpConnectionListener(TcpProxyService proxyService, int timeout) {
    super("TcpConnectionListener");
    this.proxyService = proxyService;
    this.timeout = timeout;
    this.clockSynchroThreshold = Configuration.getLong(CLOCK_SYNCHRO_THRESHOLD, clockSynchroThreshold).longValue();
  }

  public void run() {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, "TcpConnectionListener.run()");

    // Wait for the administration topic deployment.
    // TODO (AF): a synchronization would be much better.
    try {
      Thread.sleep(500);
    } catch (InterruptedException exc) {
      // continue
    }

    while (running) {
      canStop = true;
      try {
        acceptConnection();
      } catch (Exception exc) {}
    }
  }

  static class NetOutputStream extends ByteArrayOutputStream {
    private OutputStream os = null;

    NetOutputStream(Socket sock) throws IOException {
      super(1024);
      reset();
      os = sock.getOutputStream();
    }

    public void reset() {
      count = 4;
    }

    public void send() throws IOException {
      try {
        buf[0] = (byte) ((count -4) >>>  24);
        buf[1] = (byte) ((count -4) >>>  16);
        buf[2] = (byte) ((count -4) >>>  8);
        buf[3] = (byte) ((count -4) >>>  0);

        writeTo(os);
        os.flush();
      } finally {
        reset();
      }
    }
  }

  /**
   * Accepts a TCP connection. Opens the <code>UserConnection</code> with the
   * right user's proxy, creates and starts the <code>TcpConnection</code>.
   */
  private void acceptConnection() throws Exception {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, "TcpConnectionListener.acceptConnection()");

    Socket sock = proxyService.getServerSocket().accept();
    String inaddr = sock.getInetAddress().getHostAddress();

    connectionCount++;

    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, " -> accept connection from " + inaddr);

    try {
      sock.setTcpNoDelay(true);

      // Fix bug when the client doesn't use the right protocol (e.g. Telnet)
      // and blocks this listener.
      sock.setSoTimeout(timeout);

      InputStream is = sock.getInputStream();
      NetOutputStream nos = new NetOutputStream(sock);

      byte[] magic = StreamUtil.readByteArrayFrom(is, 8);
      for (int i = 0; i < 5; i++) {
        if (magic.length == i || magic[i] != MetaData.joramMagic[i] && magic[i] > 0) {
          String errorMsg = "Bad magic number. Client is not compatible with JORAM.";
          logger.log(BasicLevel.ERROR, errorMsg);
          protocolErrorCount++;
          throw new IllegalAccessException(errorMsg);
        }
      }
      if (magic[7] != MetaData.joramMagic[7]) {
        if (magic[7] > 0 && MetaData.joramMagic[7] > 0) {
          String errorMsg = "Bad protocol version number " + magic[7] + " != " + MetaData.joramMagic[7];
          logger.log(BasicLevel.ERROR, errorMsg);
          protocolErrorCount++;
          throw new IllegalAccessException(errorMsg);
        }
       
        logger.log(BasicLevel.WARN,
                   "Wildcard protocol version number: from stream = " + magic[7] + ", from MetaData = " + MetaData.joramMagic[7]);
      }

      long dt = Math.abs(StreamUtil.readLongFrom(is) - System.currentTimeMillis());
      if (dt > clockSynchroThreshold)
        logger.log(BasicLevel.WARN, " -> bad clock synchronization between client and server: " + dt);
      StreamUtil.writeTo(dt, nos);

      Identity identity = Identity.read(is);
      if (logger.isLoggable(BasicLevel.DEBUG))
        logger.log(BasicLevel.DEBUG, " -> read identity = " + identity);

      int key = StreamUtil.readIntFrom(is);
      if (logger.isLoggable(BasicLevel.DEBUG))
        logger.log(BasicLevel.DEBUG, " -> read key = " + key);

      int heartBeat = 0;
      if (key == -1) {
        heartBeat = StreamUtil.readIntFrom(is);
        if (logger.isLoggable(BasicLevel.DEBUG))
          logger.log(BasicLevel.DEBUG, " -> read heartBeat = " + heartBeat);
      }

      GetProxyIdNot gpin = new GetProxyIdNot(identity, inaddr);
      AgentId proxyId;
      try {
        gpin.invoke(AdminTopic.getDefault());
        proxyId = gpin.getProxyId();
      } catch (Exception exc) {
        if (logger.isLoggable(BasicLevel.DEBUG))
          logger.log(BasicLevel.DEBUG, "", exc);
        failedLoginCount++;
        StreamUtil.writeTo(1, nos);
        StreamUtil.writeTo(exc.getMessage(), nos);
        nos.send();
        return;
      }

      IOControl ioctrl;
      ReliableConnectionContext ctx;
      if (key == -1) {
        OpenConnectionNot ocn = new OpenConnectionNot(true, heartBeat);
        ocn.invoke(proxyId);
        StreamUtil.writeTo(0, nos);
        ctx = (ReliableConnectionContext) ocn.getConnectionContext();
        key = ctx.getKey();
        StreamUtil.writeTo(key, nos);
        nos.send();
        ioctrl = new IOControl(sock);
      } else {
        GetConnectionNot gcn = new GetConnectionNot(key);
        try {
          gcn.invoke(proxyId);
        } catch (Exception exc) {
          if (logger.isLoggable(BasicLevel.DEBUG))
            logger.log(BasicLevel.DEBUG, "", exc);
          StreamUtil.writeTo(1, nos);
          StreamUtil.writeTo(exc.getMessage(), nos);
          nos.send();
          return;
        }
        ctx = (ReliableConnectionContext) gcn.getConnectionContext();
        StreamUtil.writeTo(0, nos);
        nos.send();
        ioctrl = new IOControl(sock, ctx.getInputCounter());

        TcpConnection tcpConnection = proxyService.getConnection(proxyId, key);
        if (tcpConnection != null) {
          tcpConnection.close();
        }
      }

      // Reset the timeout in order to enable the server to indefinitely
      // wait for requests.
      sock.setSoTimeout(0);

      TcpConnection tcpConnection = new TcpConnection(ioctrl, ctx, proxyId, proxyService, identity);
      tcpConnection.start();
    } catch (IllegalAccessException exc) {
      if (logger.isLoggable(BasicLevel.ERROR))
        logger.log(BasicLevel.ERROR, "", exc);
      sock.close();
      throw exc;
    } catch (IOException exc) {
      if (logger.isLoggable(BasicLevel.DEBUG))
        logger.log(BasicLevel.DEBUG, "", exc);
      sock.close();
      throw exc;
    }
  }

  protected void shutdown() {
    close();
  }

  protected void close() {
    proxyService.resetServerSocket();
  }

  public int getFailedLoginCount() {
    return failedLoginCount;
  }

  public int getInitiatedConnectionCount() {
    return connectionCount;
  }

  public int getProtocolErrorCount() {
    return protocolErrorCount;
  }
}
TOP

Related Classes of org.objectweb.joram.mom.proxies.tcp.TcpConnectionListener$NetOutputStream

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.