public synchronized ConnectionInfo connect()

in cvs/trilead-ssh2-build213/src/com/trilead/ssh2/Connection.java [663:795]


	public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
			throws IOException
	{
		final class TimeoutState
		{
			boolean isCancelled = false;
			boolean timeoutSocketClosed = false;
		}

		if (tm != null)
			throw new IOException("Connection to " + hostname + " is already in connected state!");

		if (connectTimeout < 0)
			throw new IllegalArgumentException("connectTimeout must be non-negative!");

		if (kexTimeout < 0)
			throw new IllegalArgumentException("kexTimeout must be non-negative!");

		final TimeoutState state = new TimeoutState();

		tm = new TransportManager(hostname, port);

		tm.setConnectionMonitors(connectionMonitors);

		/*
		 * Make sure that the runnable below will observe the new value of "tm"
		 * and "state" (the runnable will be executed in a different thread,
		 * which may be already running, that is why we need a memory barrier
		 * here). See also the comment in Channel.java if you are interested in
		 * the details.
		 * 
		 * OKOK, this is paranoid since adding the runnable to the todo list of
		 * the TimeoutService will ensure that all writes have been flushed
		 * before the Runnable reads anything (there is a synchronized block in
		 * TimeoutService.addTimeoutHandler).
		 */

		synchronized (tm)
		{
			/* We could actually synchronize on anything. */
		}

		try
		{
			TimeoutToken token = null;

			if (kexTimeout > 0)
			{
				final Runnable timeoutHandler = new Runnable()
				{
					public void run()
					{
						synchronized (state)
						{
							if (state.isCancelled)
								return;
							state.timeoutSocketClosed = true;
							tm.close(new SocketTimeoutException("The connect timeout expired"), false);
						}
					}
				};

				long timeoutHorizont = System.currentTimeMillis() + kexTimeout;

				token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler);
			}

			try
			{
				tm.initialize(cryptoWishList, verifier, dhgexpara, connectTimeout, getOrCreateSecureRND(), proxyData);
			}
			catch (SocketTimeoutException se)
			{
				throw (SocketTimeoutException) new SocketTimeoutException(
						"The connect() operation on the socket timed out.").initCause(se);
			}

			tm.setTcpNoDelay(tcpNoDelay);

			/* Wait until first KEX has finished */

			ConnectionInfo ci = tm.getConnectionInfo(1);

			/* Now try to cancel the timeout, if needed */

			if (token != null)
			{
				TimeoutService.cancelTimeoutHandler(token);

				/* Were we too late? */

				synchronized (state)
				{
					if (state.timeoutSocketClosed)
						throw new IOException("This exception will be replaced by the one below =)");
					/*
					 * Just in case the "cancelTimeoutHandler" invocation came
					 * just a little bit too late but the handler did not enter
					 * the semaphore yet - we can still stop it.
					 */
					state.isCancelled = true;
				}
			}

			return ci;
		}
		catch (SocketTimeoutException ste)
		{
			throw ste;
		}
		catch (IOException e1)
		{
			/* This will also invoke any registered connection monitors */
			close(new Throwable("There was a problem during connect."), false);

			synchronized (state)
			{
				/*
				 * Show a clean exception, not something like "the socket is
				 * closed!?!"
				 */
				if (state.timeoutSocketClosed)
					throw new SocketTimeoutException("The kexTimeout (" + kexTimeout + " ms) expired.");
			}

			/* Do not wrap a HTTPProxyException */
			if (e1 instanceof HTTPProxyException)
				throw e1;

			throw (IOException) new IOException("There was a problem while connecting to " + hostname + ":" + port)
					.initCause(e1);
		}
	}