/**
   * Start using stream compression since the server has acknowledged stream compression.
   *
   * @throws Exception if there is an exception starting stream compression.
   */
  void startStreamCompression() throws Exception {
    // Secure the plain connection
    usingCompression = true;
    // Initialize the reader and writer with the new secured version
    initReaderAndWriter();

    // Set the new  writer to use
    packetWriter.setWriter(writer);
    // Send a new opening stream to the server
    packetWriter.openStream();
    // Notify that compression is being used
    synchronized (this) {
      this.notify();
    }
  }
  /**
   * The server has indicated that TLS negotiation can start. We now need to secure the existing
   * plain connection and perform a handshake. This method won't return until the connection has
   * finished the handshake or an error occured while securing the connection.
   *
   * @throws Exception if an exception occurs.
   */
  void proceedTLSReceived() throws Exception {
    enableEncryption(true);
    // Initialize the reader and writer with the new secured version
    initReaderAndWriter();
    // Proceed to do the handshake
    ((SSLSocket) socket).startHandshake();
    // if (((SSLSocket) socket).getWantClientAuth()) {
    //    System.err.println("Connection wants client auth");
    // }
    // else if (((SSLSocket) socket).getNeedClientAuth()) {
    //    System.err.println("Connection needs client auth");
    // }
    // else {
    //    System.err.println("Connection does not require client auth");
    // }
    // Set that TLS was successful
    usingTLS = true;

    // Set the new  writer to use
    packetWriter.setWriter(writer);
    // Send a new opening stream to the server
    packetWriter.openStream();
  }
  /**
   * Initializes the connection by creating a packet reader and writer and opening a XMPP stream to
   * the server.
   *
   * @throws XMPPException if establishing a connection to the server fails.
   */
  private void initConnection() throws XMPPException {
    PacketReader packetReader = this.packetReader;
    PacketWriter packetWriter = this.packetWriter;
    boolean isFirstInitialization = packetReader == null || packetWriter == null;
    usingCompression = false;

    // Set the reader and writer instance variables
    initReaderAndWriter();

    try {
      if (isFirstInitialization) {
        this.packetWriter = packetWriter = new PacketWriter(this);
        this.packetReader = packetReader = new PacketReader(this);

        // If debugging is enabled, we should start the thread that will listen for
        // all packets and then log them.
        if (config.isDebuggerEnabled()) {
          addPacketListener(debugger.getReaderListener(), null);
          if (debugger.getWriterListener() != null) {
            addPacketSendingListener(debugger.getWriterListener(), null);
          }
        }
      } else {
        packetWriter.init();
        packetReader.init();
      }

      // Start the packet writer. This will open a XMPP stream to the server
      packetWriter.startup();
      // Start the packet reader. The startup() method will block until we
      // get an opening stream packet back from server.
      packetReader.startup();

      // Make note of the fact that we're now connected.
      connected = true;

      // Start keep alive process (after TLS was negotiated - if available)
      packetWriter.startKeepAliveProcess();

      if (isFirstInitialization) {
        // Notify listeners that a new connection has been established
        for (ConnectionCreationListener listener : getConnectionCreationListeners()) {
          listener.connectionCreated(this);
        }
      } else if (!wasAuthenticated) {
        packetReader.notifyReconnection();
      }

    } catch (XMPPException ex) {
      // An exception occurred in setting up the connection. Make sure we shut down the
      // readers and writers and close the socket.

      if (packetWriter != null) {
        try {
          packetWriter.shutdown();
        } catch (Throwable ignore) {
          /* ignore */
        }
        this.packetWriter = null;
      }
      if (packetReader != null) {
        try {
          packetReader.shutdown();
        } catch (Throwable ignore) {
          /* ignore */
        }
        this.packetReader = null;
      }
      if (reader != null) {
        try {
          reader.close();
        } catch (Throwable ignore) {
          /* ignore */
        }
        reader = null;
      }
      if (writer != null) {
        try {
          writer.close();
        } catch (Throwable ignore) {
          /* ignore */
        }
        writer = null;
      }
      if (socket != null) {
        try {
          socket.close();
        } catch (Exception e) {
          /* ignore */
        }
        socket = null;
      }
      this.setWasAuthenticated(authenticated);
      chatManager = null;
      authenticated = false;
      connected = false;

      throw ex; // Everything stoppped. Now throw the exception.
    }
  }