sábado, 13 de março de 2010

Conectando com servidor remoto via SSH

Um dos padrões de projeto mais legais é o State, com ele, um objeto pode mudar seu comportamento em tempo de execução dependendo do estado corrente e cada implementação de estado preocupa-se apenas com o que deve fazer, deixando assim um código limpo e totalmente reutilizável.


state pattern

Cada ConcreteState implementa um comportamento específico permitindo, dessa forma, que novos comportamentos sejam anexados.

No caso de uma conexão SSH:


state




Nosso contexto inicia-se como SSHStateClosed, assim que executamos o método open() nosso estado passa a ser SSHStateEstabilished e podemos verificar a assinatura do host e autenticar; Assim que autenticado o estado passa à SSHStateListen para que possamos enviar comandos ao servidor remoto.

Como uma autenticação pode ser de várias formas, deixamos a implementação separada em um objeto específico, dessa forma, podemos autenticar com usuário e senha ou, se for o caso, utilizando certificados digitais (entre outras).

Um comando deveria ser implementado da mesma forma, porém, para simplificar as coisas, vamos trabalhar com strings mesmo:





com/ssh/auth/SSHAuthenticatePassword.php
<?php
/**
 * @author   João Batista Neto
 * @package  ssh
 * @category ssh, state, design pattern
 */
namespace com\ssh\auth;

/**
 * @see ISSHAuthentication
 */
use com\ssh\interfaces\ISSHAuthentication;

/**
 * Implementa uma autenticação utilizando nome de usuário e senha
 * @author   João Batista Neto
 * @final
 * @package  ssh
 * @category ssh, state, authentication, design pattern
 */
final class SSHAuthenticatePassword implements ISSHAuthentication {
        /**
         * @var string
         */
        private $user;
 
        /**
         * @var string
         */
        private $pswd;
 
        /**
         * Constroi o objeto de autenticação
         * @param string $user
         * @param string $pswd
         */
        public function __construct( $user , $pswd ){
                $this->user =& $user;
                $this->pswd =& $pswd;
        }
 
        /**
         * Efetua a autenticação
         * @param resource $resource
         * @throws InvalidArgumentException se o recurso não for válido
         */
        public function authenticate( &$resource ){
                if ( is_resource( $resource ) ){
                        return \ssh2_auth_password( $resource , $this->user , $this->pswd );
                } else {
                        throw new \InvalidArgumentException( 'Recurso inválido.' );
                }
 
                return false;
        }
 
        /**
         * Recupera o nome do usuário
         * @return string
         */
        public function getUser(){
                return $this->user;
        }
}



com/ssh/interfaces/AbstractSSHState.php
<?php
/**
 * @author     João Batista Neto
 * @package    ssh
 * @subpackage interfaces
 * @category   ssh, state, design pattern
 */
namespace com\ssh\interfaces;

/**
 * Interface para um estado de conexão
 * @abstract
 * @author     João Batista Neto
 * @package    ssh
 * @subpackage interfaces
 * @category   sh, state, design pattern
 */
abstract class AbstractSSHState implements ISSHState {
        /**
         * @var resource
         */
        protected $resource;
 
        /**
         * Autentica o usuário
         * @param ISSHAuthentication $auth
         * @param ISSHConnection $context
         * @return boolean
         * @throws BadMethodCallException se o estado não implementar o método authenticate
         */
        public function authenticate( ISSHAuthentication $auth , ISSHConnection $context ){
                throw new \BadMethodCallException( sprintf( '%s não implementa o método %s' , get_class( $this ) , __METHOD__ ) );
        }
 
        /**
         * Executa um comando no servidor remoto
         * @param string $command
         * @param ISSHConnection $context
         * @return boolean
         * @throws BadMethodCallException se o estado não implementar o método execute
         */
        public function execute( $command , ISSHConnection $context ){
                throw new \BadMethodCallException( sprintf( '%s não implementa o método %s' , get_class( $this ) , __METHOD__ ) );
        }
 
        /**
         * Recupera a fingerprint do servidor
         * @param ISSHConnection $context
         * @return string
         * @throws BadMethodCallException se o estado não implementar o método execute
         */
        public function getFingerprint( ISSHConnection $context ){
                throw new \BadMethodCallException( sprintf( '%s não implementa o método %s' , get_class( $this ) , __METHOD__ ) );
        }
 
        /**
         * Abre uma nova conexão
         * @param string $host
         * @param integer $port
         * @param ISSHConnection $context
         * @return boolean
         * @throws BadMethodCallException se o estado não implementar o método open
         */
        public function open( $host , $port = 22 , ISSHConnection $context ){
                throw new \BadMethodCallException( sprintf( '%s não implementa o método %s' , get_class( $this ) , __METHOD__ ) );
        }
 
        /**
         * Define o recurso da conexão
         * @param resource $resource
         * @throws InvalidArgumentException se o recurso não for válido
         */
        public function setResource( &$resource ){
                if ( !is_resource( $resource ) ){
                        throw new \InvalidArgumentException( 'Recurso inválido.' );
                } else {
                        $this->resource =& $resource;
                }
        }
}



com/ssh/interfaces/ISSHAuthentication.php
<?php
/**
 * @author     João Batista Neto
 * @package    ssh
 * @subpackage interfaces
 * @category   ssh, state, design pattern
 */
namespace com\ssh\interfaces;

/**
 * Interface para autenticação SSH
 * @author     João Batista Neto
 * @package    ssh
 * @subpackage interfaces
 * @category   ssh, state, design pattern
 */
interface ISSHAuthentication {
        /**
         * Efetua a autenticação do usuário
         * @param resource $resource
         */
        public function authenticate( &$resource );
 
        /**
         * Recupera o nome do usuário
         * @return string
         */
        public function getUser();
}



com/ssh/interfaces/ISSHConnection.php
<?php
/**
 * @author     João Batista Neto
 * @package    ssh
 * @subpackage interfaces
 * @category   ssh, state, design pattern
 */
namespace com\ssh\interfaces;

/**
 * Interface para uma conexão SSH
 * @author     João Batista Neto
 * @package    ssh
 * @subpackage interfaces
 * @category   ssh, state, design pattern
 */
interface ISSHConnection {
        /**
         * Autentica um usuário
         * @param ISSHAuthentication $auth
         * @return boolean
         */
        public function authenticate( ISSHAuthentication $auth );
 
        /**
         * Executa um comando no servidor remoto
         * @param string $command
         * @return boolean
         */
        public function execute( $command );
 
        /**
         * Recupera a fingerprint do servidor
         * @return string
         */
        public function getFingerprint();
 
        /**
         * Abre uma nova conexão
         * @param stirng $host
         * @param integer $port
         * @return boolean
         */
        public function open( $host , $port );
 
        /**
         * Modifica o estado da conexão
         * @param ISSHState $state
         */
        public function changeState( ISSHState $state );
}

com/ssh/interfaces/ISSHState.php
<?php
/**
 * @author     João Batista Neto
 * @package    ssh
 * @subpackage interfaces
 * @category   ssh, state, design pattern
 */
namespace com\ssh\interfaces;

/**
 * Interface para um estado de conexão
 * @author     João Batista Neto
 * @package    ssh
 * @subpackage interfaces
 * @category   ssh, state, design pattern
 */
interface ISSHState {
        /**
         * Autentica o usuário
         * @param ISSHAuthentication $auth
         * @param ISSHConnection $context
         */
        public function authenticate( ISSHAuthentication $auth , ISSHConnection $context );
 
        /**
         * Executa um comando no servidor remoto
         * @param string $command
         * @param ISSHConnection $context
         */
        public function execute( $command , ISSHConnection $context );
 
        /**
         * Recupera a fingerprint do servidor
         * @param ISSHConnection $context
         * @return string
         */
        public function getFingerprint( ISSHConnection $context );
 
        /**
         * Abre uma nova conexão
         * @param string $host
         * @param integer $port
         * @param ISSHConnection $context
         */
        public function open( $host , $port = 22 , ISSHConnection $context );
 
        /**
         * Define o recurso da conexão
         * @param resource $resource
         */
        public function setResource( &$resource );
}


com/ssh/state/SSHStateClosed.php
<?php
/**
 * @author   João Batista Neto
 * @package  ssh
 * @category ssh, state, design pattern
 */
namespace com\ssh\state;

/**
 * @see AbstractSSHState
 */
use com\ssh\interfaces\AbstractSSHState;

/**
 * @see ISSHConnection
 */
use com\ssh\interfaces\ISSHConnection;

/**
 * Implementa o estado para uma conexão fechada
 * @author   João Batista Neto
 * @final
 * @package  ssh
 * @category ssh, state, design pattern
 */
final class SSHStateClosed extends AbstractSSHState {
        /**
         * @staticvar
         * @var ISSHConnection
         */
        static private $context;
 
        /**
         * Abre uma nova conexão
         * @param string $host
         * @param integer $port
         * @param ISSHConnection $context
         * @return boolean
         * @throws RuntimeException se não for possível estabelecer a conexão
         * @uses SSHStateEstabilished
         */
        public function open( $host , $port = 22 , ISSHConnection $context ){
                $resource = \ssh2_connect( $host , $port , array(
                        'disconnect'    => array( 'SSHStateClosed' , 'disconnect' )
                ) );
 
                if ( !is_resource( $resource ) ){
                        throw new \RuntimeException( 'Não foi possível estabelecer a conexão.' );
                } else {
                        $estabilished = new SSHStateEstabilished();
                        $estabilished->setResource( $resource );
 
                        self::$context =& $context;
                        self::$context->changeState( $estabilished );
                }
 
                return true;
        }
 
        /**
         * Muda o estado da conexão para SSHStateClosed caso o servidor envie um disconnect
         * @static
         */
        static public function disconnect(){
                self::$context->changeState( new SSHStateClosed() );
        }
}

com/ssh/state/SSHStateEstabilished.php
<?php
/**
 * @author   João Batista Neto
 * @package  ssh
 * @category ssh, state, design pattern
 */
namespace com\ssh\state;

/**
 * @see AbstractSSHState
 */
use com\ssh\interfaces\AbstractSSHState;

/**
 * @see ISSHConnection
 */
use com\ssh\interfaces\ISSHConnection;

/**
 * @see ISSHAuthentication
 */
use com\ssh\interfaces\ISSHAuthentication;

/**
 * Implementa o estado para uma conexão estabelecida
 * @author   João Batista Neto
 * @final
 * @package  ssh
 * @category ssh, state, design pattern
 */
final class SSHStateEstabilished extends AbstractSSHState {
        /**
         * Efetua a autenticação do usuário
         * @param ISSHAuthentication $auth
         * @param ISSHConnection $context
         * @return boolean
         * @throws RuntimeException se não for possível autenticar o usuário
         * @uses SSHStateAuthenticated
         */
        public function authenticate( ISSHAuthentication $auth , ISSHConnection $context ){
                if ( !$auth->authenticate( $this->resource ) ){
                        throw new RuntimeException( sprintf( 'Não foi possível autenticar o usuário %s.' , $auth->getUser() ) );
                } else {
                        $authenticated = new SSHStateListen();
                        $authenticated->setResource( $this->resource );
                        $context->changeState( $authenticated );
                }
 
                return true;
        }
 
        /**
         * Recupera o hash da chave do servidor da conexão ativa
         * @return string
         * @throws LogicException se o estado não implementar o método execute
         */
        public function getFingerprint( ISSHConnection $context ){
                return \ssh2_fingerprint( $this->resource , \SSH2_FINGERPRINT_MD5 | \SSH2_FINGERPRINT_HEX );
        }
}

com/ssh/state/SSHStateListen.php
<?php
/**
 * @author              João Batista Neto
 * @package             ssh
 * @category    ssh, state, design pattern
 */
namespace com\ssh\state;

/**
 * @see AbstractSSHState
 */
use com\ssh\interfaces\AbstractSSHState;

/**
 * @see ISSHConnection
 */
use com\ssh\interfaces\ISSHConnection;

/**
 * Implementa o estado para uma conexão autenticada
 * @author   João Batista Neto
 * @final
 * @package  ssh
 * @category ssh, state, design pattern
 */
final class SSHStateListen extends AbstractSSHState {
        /**
         * @var resource
         */
        private $shell;
 
        /**
         * Executa um comando no servidor remoto
         * @param string $command
         * @param ISSHConnection $context
         * @return string A saída do servidor remoto
         * @throws RuntimeException se não for possível executar o comando
         */
        public function execute( $command , ISSHConnection $context ){
                $ret = null;
 
                $stream = \ssh2_exec( $this->resource , $command );
 
                if ( is_resource( $stream ) ){
                        \stream_set_blocking( $stream , true );
 
                        while ( ( $line = \fgets( $stream , 4096 ) ) !== false ){
                                $ret .= $line;
                        }
                } else {
                        throw new \RuntimeException( 'Não foi possível executar o comando.' );
                }
 
                return $ret;
        }
}

com/ssh/SSHConnection.php
<?php
/**
 * @author   João Batista Neto
 * @package  ssh
 * @category ssh, state, design pattern
 */
namespace com\ssh;

/**
 * @see ISSHConnection
 */
use com\ssh\interfaces\ISSHConnection;

/**
 * @see ISSHAuthentication
 */
use com\ssh\interfaces\ISSHAuthentication;

/**
 * @see ISSHState
 */
use com\ssh\interfaces\ISSHState;

/**
 * @see SSHStateClosed
 */
use com\ssh\state\SSHStateClosed;

/**
 * Implementa uma conexão com um servidor remoto via SSH
 * @author   João Batista Neto
 * @final
 * @package  ssh
 * @category ssh, state, design pattern
 */
class SSHConnection implements ISSHConnection {
        /**
         * @var ISSHState
         */
        private $state;
 
        /**
         * Cria o objeto de conexão
         */
        public function __construct(){
                $this->state = new SSHStateClosed();
        }
 
        /**
         * Faz a autenticação no servidor
         * @param ISSHAuthentication $auth
         * @return boolean
         */
        public function authenticate( ISSHAuthentication $auth ){
                return $this->state->authenticate( $auth , $this );
        }
 
        /**
         * Executa um comando no servidor
         * @param string $command
         * @return boolean
         */
        public function execute( $command ){
                return $this->state->execute( $command , $this );
        }
 
        /**
         * Recupera a fingerprint do servidor
         * @return string
         */
        public function getFingerprint(){
                return $this->state->getFingerprint( $this );
        }
 
        /**
         * Abre uma conexão com um servidor remoto
         * @param string $host
         * @param integer $port
         * @return boolean
         */
        public function open( $host , $port = 22 ){
                return $this->state->open( $host , $port , $this );
        }
 
        /**
         * Modifica o estado da conexão
         * @param ISSHState $state
         */
        public function changeState( ISSHState $state ){
                $this->state =& $state;
        }
}


Agora, para conectar em um servidor remoto via PHP é simples:


<?php
use com\ssh\SSHConnection;
use com\ssh\auth\SSHAuthenticatePassword;

$teste = new SSHConnection();
$teste->open( '127.0.0.1' );
$teste->authenticate( new SSHAuthenticatePassword( 'usuario' , 'senha' ) );
echo $teste
->execute( 'ls -l /var/www/html/xxx/com/ssh' );


No meu caso, a saída foi:

total 16
drwxr-xr-x. 3 neto apache 4096 Mar 11 18:18 auth
drwxr-xr-x. 3 neto apache 4096 Mar 11 18:18 interfaces
-rw-r--r--. 1 neto apache 1681 Mar 11 18:17 SSHConnection.php
drwxr-xr-x. 3 neto apache 4096 Mar 12 06:35 state

sábado, 27 de fevereiro de 2010

Colaboração de elementos com Mediator

É comum termos elementos na interface de usuário que colaboram entre si, ou que uma ação de um elemento causa uma reação em outro elemento. Para abstrair a complexabilidade dessa colaboração dos elementos em si, podemos utilizar um design pattern chamado Mediator.


Mediator Class


O Mediator, como o nome sugere, irá mediar essa colaboração, encapsulando toda a complexabilidade do comportamento conjunto em um objeto mediator.

Mediator Sequence


Um exemplo dessa colaboração é um sistema de auto-completar onde o usuário digita os termos em um campo de texto e um outro elemento vai recebendo as dicas, assim que o input tem seu estado modificado (evento onchange) ele avisa o seu mediator que fará o trabalho e repassará para o seu outro objeto que seria uma lista com as sugestões:

Utilizando Javascript para coordenar a interface de usuário, teríamos o seguinte:

index.html
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pt-br" lang="pt-br">
        <head>
                <title>Mediator</title>
 
                <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 
                <link rel="stylesheet" href="public/css/style.css" type="text/css" />
        </head>
        <body onload="main();">
                <form id="fMediator" action="" method="post" onsubmit="return false; void(0);">
                        <fieldset>
                                <label for="iNome">
                                        <span>Nome: </span>
                                        <input type="text" id="iNome" name="iNome" />
                                </label>
                                <ul id="lTips"></ul>
                        </fieldset>
                </form>
                <script type="text/javascript" src="public/js/Ajax.js"></script>
                <script type="text/javascript" src="public/js/AbstractColleague.js"></script>
                <script type="text/javascript" src="public/js/InputColleague.js"></script>
                <script type="text/javascript" src="public/js/ListColleague.js"></script>
                <script type="text/javascript" src="public/js/Mediator.js"></script>
                <script type="text/javascript">
                        function main(){
                                var server = new Mediator( 'test.php' );
                                var input = new InputColleague( document.getElementById( 'iNome' ) );
                                var list = new ListColleague( document.getElementById( 'lTips' ) );
 
                                input.setMediator( server );
                                list.setMediator( server );
 
                                server.add( input );
                                server.add( list );
                        }
 
                        function muda( nome ){
                                document.getElementById( 'iNome' ).value = nome;
                                document.getElementById( 'lTips' ).innerHTML = '';
                        }
                </script>
        </body>
</html>


AbstractColleague.js
/**
 * Interface para implementação de um Colleague
 * @constructor
 */
function AbstractColleague(){}; 
/**
 * Template method para envio de uma mensagem para o mediator
 * @param String message A mensagem que será enviada para o mediator
 */
AbstractColleague.prototype.send = function( message ){
        this.mediator.send( message , this );
}; 
/**
 * Template method para definição do Mediator
 * @param Mediator mediator
 */
AbstractColleague.prototype.setMediator = function( mediator ){
        if ( mediator instanceof Mediator ){
                this.mediator = mediator;
        } else {
                throw 'Opz, precisamos de um Mediator';
        }
}; 
/**
 * Template method para recuperação do Mediator
 * @return Mediator
 */
AbstractColleague.prototype.getMediator = function(){
        return this.mediator;
}; 
/**
 * Recebe a notificação do mediator caso um Colleague tenha seu estado modificado.
 * Precisa ser implementado nos Colleagues
 * @param String message
 */
AbstractColleague.prototype.notify = function( message ){};

InputColleague.js
/**
 * Implementação de do input
 * @param input Objeto do input onde o usuário irá digitar
 * @constructor
 */
function InputColleague( input ){
        var obj = this;
        var $_ = {
                onfocus : function(){
                        this.onkeydown = function( evt ){
                                if ( evt.keyCode >= 60 ){
                                        if ( this.timer ) clearInterval( this.timer );
                                        this.timer = setInterval( function( temp ){
                                                clearInterval( temp.timer );
                                                obj.getMediator().send( input.value , obj );
                                        } , 10 , this );
                                }
                        }
                },
                onblur : function(){
                        delete this.onkeydown;
                }
        };
       
        input.onfocus = $_.onfocus;
        input.onblur = $_.onblur;
       
        this.input = input;
}; 
InputColleague.prototype = new AbstractColleague(); 
/**
 * Recebe a notificação do mediator que seu Colleague teve seu estado modificado
 * @param String message
 */
InputColleague.prototype.notify = function( message ){
        this.input.value = message;
}; 

ListColleague.js
/**
 * Implementação da listagem de sugestões
 * @param input Objeto do UL que receberá as LIs
 * @constructor
 */
function ListColleague( list ){
        this.list = list;
}; 
ListColleague.prototype = new AbstractColleague(); 
/**
 * Recebe a notificação do mediator que seu Colleague teve seu estado modificado
 * @param String message
 */
ListColleague.prototype.notify = function( message ){
        this.list.innerHTML = message;
};

Com todos os Colleagues definidos, vamos definir agora o Mediator para um auto completar:



Mediator.js
/**
 * Mediator
 * @param String url A URL que será usada para recuperar as sugestões do banco de dados
 * @construct
 */
function Mediator( url ){
        this.ajax = new Ajax();
        this.colleagues = new Array();
        this.url = url;
}; 
/**
 * Adiciona um Colleague que será mediado pelo Mediator
 * @param Colleague colleague
 */
Mediator.prototype.add = function( colleague ){
        if ( colleague instanceof AbstractColleague ){
                this.colleagues.push( colleague );
        } else {
                throw 'Opz, precisamos de um Colleague';
        }
}; 
/**
 * Usado pelos Colleagues para notificar uns aos outros sobre suas mudanças de estado
 * @param String message
 * @param Colleague colleague
 */
Mediator.prototype.send = function( message , colleague ){
        if ( colleague instanceof AbstractColleague ){
                var $this = this;
               
                message = [ 'message' , escape( message ) ].join( '=' );
               
                this.ajax.open( 'POST' , this.url , true );
                this.ajax.send( message );
                this.ajax.onreadystatechange = function(){
                        if ( this.readyState == 4 ){
                                for ( var obj in $this.colleagues ){
                                        if ( $this.colleagues[ obj ] !== colleague ){
                                                $this.colleagues[ obj ].notify( this.responseText );
                                        }
                                }
                        }
                }
               
        } else {
                throw 'Opz, precisamos de um Colleague';
        }
};


Para a parte do servidor usaremos um PHP bem simples que apenas receberá a requisição e retornará um conjunto de <li> que representará as sugestões:




test.php
<?php
$headers = getallheaders(); 
if ( isset( $headers[ 'X-Requested-With' ] ) && ( $headers[ 'X-Requested-With' ] == 'XMLHttpRequest' ) ){
        if ( ( $_SERVER[ 'REQUEST_METHOD' ] == 'POST' ) && isset( $_POST[ 'message' ] ) ){
                $message = sprintf( '%s%%' , $_POST[ 'message' ] );
                $pdo = new PDO( 'mysql:host=127.0.0.1;dbname=test' , 'usuario' , 'senha' );
                $stm = $pdo->prepare( 'SELECT `u`.`usuariosNome` FROM `Usuarios` AS `u` WHERE `u`.`usuariosNome` LIKE :nome;' );
                $stm->bindParam( ':nome' , $message );
 
                if ( $stm->execute() ){
                        $i = 0;
 
                        foreach ( $stm->fetchAll( PDO::FETCH_OBJ ) as $row ){
                                printf( '<li><a href="#" onclick="muda(\'%s\');">%s</a></li>' , $row->usuariosNome , $row->usuariosNome );
                        }
                } else {
                        header( sprintf( '%s 500 Internal Server Error' , $_SERVER[ 'SERVER_PROTOCOL' ] ) );
                        var_dump( $stm->errorInfo() );
                }
        } else {
                header( sprintf( '%s 400 Bad Request' , $_SERVER[ 'SERVER_PROTOCOL' ] ) );
        }
} else {
        header( sprintf( '%s 400 Bad Request' , $_SERVER[ 'SERVER_PROTOCOL' ] ) );
}

Para esse exemplo foi utilizado um banco de dados MySQL com uma tabela de usuários com a seguinte estrutura:

CREATE SCHEMA IF NOT EXISTS `test`;
 CREATE TABLE IF NOT EXISTS `test`.`Usuarios` (
 `idUsuarios` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
 `usuariosNome` VARCHAR(45) NOT NULL,
 PRIMARY KEY(`idUsuarios`),
 INDEX `usuarios`(`usuariosNome` ASC)
) ENGINE = MyISAM DEFAULT CHARACTER SET = utf8 COLLATE = utf8_general_ci;

E os seguinte dados:
INSERT INTO `test`.`Usuarios` (`idUsuarios`, `usuariosNome`) VALUES (NULL, 'Joao');
INSERT INTO `test`.`Usuarios` (`idUsuarios`, `usuariosNome`) VALUES (NULL, 'Joao Batista');
INSERT INTO `test`.`Usuarios` (`idUsuarios`, `usuariosNome`) VALUES (NULL, 'Joao Batista Neto');
INSERT INTO `test`.`Usuarios` (`idUsuarios`, `usuariosNome`) VALUES (NULL, 'Joao Neto');
INSERT INTO `test`.`Usuarios` (`idUsuarios`, `usuariosNome`) VALUES (NULL, 'Jose');
INSERT INTO `test`.`Usuarios` (`idUsuarios`, `usuariosNome`) VALUES (NULL, 'Juliano');
INSERT INTO `test`.`Usuarios` (`idUsuarios`, `usuariosNome`) VALUES (NULL, 'Julio');


E para deixar a interface de usuário mais amigável a seguinte folha de estilos:

style.css
* {
        margin                  : 0;
        padding                 : 0;
        border                  : none;
        text-decoration : none;
}
 
body, html {
        margin                  : 10px;
        font-family             : Arial, Helvetica;
        font-size               : 14px;
}
 
form input#iNome {
        width                   : 160px;
        border                  : 1px solid #dadada;
        *margin                 : 0px 0px 0px 4px;
}
 
form label span {
        display                 : inline-block;
        width                   : 50px;
        text-align              : right;
}
 
ul#lTips {
        border                  : 1px solid #dadada;
        width                   : 160px;
        max-height              : 100px;
        overflow                : auto;
        margin                  : 0px 0px 0px 54px;
        *height                 : 0px;
}
 
ul#lTips li a {
        display                 : block;
        width                   : 100%;
        line-height             : 24px;
        height                  : 24px;
        color                   : #333333;
}
 
ul#lTips li a:hover {
        color                   : #FF0000;
}


Agora, sempre que o usuário digitar alguma coisa no input text o Mediator será avisado, enviará a requisição ao servidor e recuperará a lista de sugestões, essa lista é repassada ao outro Colleague responsável por montar a lista de sugestões.



;)