public void run() {
			runRunnable(r);
		}
	}
	
	/**
	 * Only called from the task but must be synchronized as other packets may be
	 * in process of a call to sslEngine.unwrap which may change the status of the engine.
	 * ie. This may check the status, while the call to unwrap changes the status and the
	 * if statement needs to be atomic with the method feedEncryptedPacketImpl!!!
	 * 
	 * @throws IOException
	 */
	private void continueFromTask() throws IOException {
		HandshakeStatus hsStatus = sslEngine.getHandshakeStatus();
		ByteBuffer b = socketToEngineData2;
		//here be very careful...if this is run while someone is calling 
		//feedEncryptedPacket(which DOES happen), we need to synchronize
		if(hsStatus == HandshakeStatus.NEED_UNWRAP && b.hasRemaining()) {
			if(log.isLoggable(Level.FINER)) {
				log.finer(id+"[Runnable][socketToEngine] refeeding myself pos="+b.position()+" lim="+b.limit());
			}
			feedEncryptedPacketImpl(null);
		} else {
			if(log.isLoggable(Level.FINER))
				log.finer(id+"[Runnable]continuing handshake");					
			continueHandShake(hsStatus);
		}
	}
	
	private void sendHandshakeMessage() {
		try {
			sendHandshakeMessageImpl();
		} catch (SSLException e) {
			throw new AsyncSSLEngineException(e);
		}
	}
	
	private void sendHandshakeMessageImpl() throws SSLException {
		if(log.isLoggable(Level.FINEST))
			log.finest(id+"sending handshake message");
		HELPER.eraseBuffer(empty);

		if(log.isLoggable(Level.FINEST))
			log.finest(id+"handshake pos="+engineToSocketData.position()+" lim="+engineToSocketData.limit());

		HandshakeStatus hsStatus = HandshakeStatus.NEED_WRAP;
		while(hsStatus == HandshakeStatus.NEED_WRAP) {
			HELPER.eraseBuffer(engineToSocketData);
			if(log.isLoggable(Level.FINEST))
				log.finest(id+"prepare packet pos="+engineToSocketData.position()+" lim="+engineToSocketData.limit());
			engineToSocketData.clear();
			SSLEngineResult result = sslEngine.wrap(empty, engineToSocketData);
			Status status = result.getStatus();
			hsStatus = result.getHandshakeStatus();
			HELPER.doneFillingBuffer(engineToSocketData);
			if(log.isLoggable(Level.FINEST))
				log.finest(id+"write packet pos="+engineToSocketData.position()+" lim="+
						engineToSocketData.limit()+" status="+status+" hs="+hsStatus);
			if(status == Status.BUFFER_OVERFLOW || status == Status.BUFFER_UNDERFLOW)
				throw new RuntimeException("status not right, status="+status);
			
			//realChannel.write(engineToSocketData);
			if(log.isLoggable(Level.FINE))
				log.fine(id+"SSLListener.packetEncrypted pos="+engineToSocketData.position()+
						" lim="+engineToSocketData.limit()+" status="+status+" hs="+hsStatus);			
			fireEncryptedPacketToListener(null);
			if(status == Status.CLOSED) {
				isClosed = true;
				closeInbound();
				sslEngine.closeOutbound();
				sslListener.closed(clientInitiated);
			}
		}

		if(hsStatus == HandshakeStatus.NEED_WRAP || hsStatus == HandshakeStatus.NEED_TASK)
			throw new RuntimeException(id+"BUG, need to implement more here status="+hsStatus);			

		if(log.isLoggable(Level.FINEST))
			log.finest(id+"status="+hsStatus+" isConn="+isConnected);
		if(hsStatus == HandshakeStatus.FINISHED && !isConnected) {
			//this is a sslserver side connect
			//sslserver may be client side :)
			fireConnected(); 
		}
	}

	private void fireEncryptedPacketToListener(Object passthrough) {
		try {
			sslListener.packetEncrypted(engineToSocketData, passthrough);
		} catch (IOException e) {
			throw new NioException(e);
		}
	}

	private void fireConnected() {
		if(!isConnected) {
			//else this is a rehandshake and we don't care!!!!
			isConnected = true;
			if(log.isLoggable(Level.FINE))
				log.fine(id+"SSLListener.encryptedLinkEstablished");			
			try {
				sslListener.encryptedLinkEstablished();
			} catch (IOException e) {
				throw new NioException(e);
			}
		}		
	}

	public synchronized void close() {
		try {
			closeImpl();
		} catch(Exception e) {
			//We expect exceptions when hard closing the SSL Engine as it prefers a handshake.
			log.log(Level.FINE, id+"Exception trying to close channel", e);			
		}
		
		sslListener.closed(clientInitiated);
	}
	
	public synchronized void closeImpl() throws IOException {
		clientInitiated = true;
		if(isClosed)
			return;

		if(log.isLoggable(Level.FINE))
			log.fine(id+"closing AsynchSSLEngine");
		
		sslEngine.closeOutbound(); //close outbound is like intiating a close
		try {
			initiateCloseImpl(); 
		} catch(IOException e) {
			log.log(Level.FINEST, id+"Typical exception as we may be called after farEndClosed", e);
		}
		isClosed = true; //set after as initiateClose check for this too!
		
		try {
			closeInbound();
		} catch(AsyncSSLEngineException e) {
			log.log(Level.FINEST, id+"Normal Expected Exception. Close packet already sent, not waiting for response", e);
		}
	}
	
	public synchronized void initiateClose() {
		try {
			initiateCloseImpl();
		} catch(Exception e) {
			log.log(Level.WARNING, id+"Exception trying to close channel", e);
		}	
	}

	private synchronized void initiateCloseImpl() throws IOException {
		clientInitiated = true;		
		if(isClosing || isClosed)
			return;
		
		if(log.isLoggable(Level.FINE))
			log.fine(id+"closing AsynchSSLEngine");
		isConnected = true;
		isClosing = true;

		sslEngine.closeOutbound();
		
		engineToSocketData.clear();
		if(log.isLoggable(Level.FINER))
			log.finer(id+"pos1="+engineToAppData.position()+" lim="+engineToSocketData.limit());
		sslEngine.wrap(empty, engineToSocketData);
		if(log.isLoggable(Level.FINER))
			log.finer(id+"pos2="+engineToAppData.position()+" lim="+engineToSocketData.limit());
		
		HELPER.doneFillingBuffer(engineToSocketData);
		if(log.isLoggable(Level.FINE))
			log.fine(id+"packetEncrypted pos="+engineToSocketData.position()+" lim="+engineToSocketData.limit());
		fireEncryptedPacketToListener(null);
	}

	public boolean isClosed() {
		return isClosed;
	}

	public boolean isClosing() {
		return isClosing;
	}
	
	public String toString() {
		return id+"";
	}