Telnet via WebService

Em vários projetos, é necessário integração com legado (sistema antigo que o cliente não quer migrar). E em alguns casos específicos essa integração pode ser um pouco mais complicada do que leitura de arquivos textos, troca de informações em base e afins. Nesse post eu irei explicar como criar um Web Service que faz a autenticação em um servidor Telnet e executa comandos nesse servidor. Essa é mais uma maneira (não tão convencional) de se realizar integração com legado.

Como client de Telnet, iremos utilizar a biblioteca Commons Net da Apache. Faça o download nesse site, descompacte-a e localize o jar da biblioteca. Crie um projeto e adicione as duas libs (jars) no classpath (build path no Eclipse e libraries no JDeveloper e Netbeans). Crie um WebService (JAX-WS) conforme o exemplo abaixo:

package br.com.thiagovespa.ws.telnet;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.SocketException;

import javax.jws.*;

import org.apache.commons.net.telnet.TelnetClient;

@WebService
public class TelnetService {

	private final static String HOST = "192.168.1.100";
	private final static String VALID_CHARS = " :-.,!?+=_'\"|\\/;@#$%&*()[]{}";
	private final static int TIMEOUT = 150000;

	@WebMethod
	public String executeCommand(String user, String password,
			String delimiter, String command, boolean showLogin)
			throws SocketException, IOException {

		if (hasSpecialChar(delimiter)) {
			return "Não utilize caracteres especiais como delimitador. Caracteres válidos: letras, espaço e números e "
					+ VALID_CHARS;
		}

		StringBuilder retorno = new StringBuilder();
		TelnetClient telnet = new TelnetClient();
		telnet.setDefaultTimeout(TIMEOUT);

		telnet.connect(HOST);
		if (!telnet.isConnected()) {
			return "ERRO: Não foi possível conectar na telnet";
		}

		telnet.setSoTimeout(TIMEOUT);

		InputStream in = telnet.getInputStream();
		PrintStream out = new PrintStream(telnet.getOutputStream());

		doLogin(user, password, (showLogin ? retorno : null), in, out);

		write(command, out);
		readValues(delimiter, in, retorno);
		return retorno.toString();

	}

	private void doLogin(String user, String password, StringBuilder retorno,
			InputStream in, PrintStream out) {
		readValues("login: ", in, retorno);
		if (retorno != null) {
			retorno.append("\n");
		}
		write(user, out);
		readValues("Password:", in, retorno);
		if (retorno != null) {
			retorno.append("\n");
		}
		write(password, out);
		readValues(">", in, retorno);
	}

	private void readValues(String endString, InputStream in,
			StringBuilder retorno) {
		try {
			char lastChar = endString.charAt(endString.length() - 1);
			StringBuilder sb = new StringBuilder();
			char ch = (char) in.read();
			while (true) {
				sb.append(ch);
				if (ch == lastChar) {
					if (sb.toString().endsWith(endString)) {
						if (retorno != null) {
							retorno.append(removeSpecialChars(sb.toString()));
						}
						return;
					}
				}
				ch = (char) in.read();
			}
		} catch (Exception e) {
			e.printStackTrace();
			if (retorno != null) {
				retorno.append("\nERRO: ").append(e.getMessage()).toString();
			}
		}

	}

	private void write(String value, PrintStream out) {
		try {
			out.println(value + "\n");
			out.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private boolean isSpecialChar(char ch) {
		if (Character.isLetterOrDigit(ch) || ch == '\n'
				|| VALID_CHARS.indexOf(ch) >= 0)
			return false;
		return true;
	}

	private boolean hasSpecialChar(String str) {
		for (char ch : str.toCharArray()) {
			if (isSpecialChar(ch))
				return true;
		}
		return false;
	}

	private String removeSpecialChars(String str) {
		StringBuilder sb = new StringBuilder();
		for (char ch : str.toCharArray()) {
			if (!isSpecialChar(ch)) {
				sb.append(ch);
			}
		}
		return sb.toString();
	}
}

O serviço recebe como parâmetro cinco informações: o usuário, senha, um delimitador de comando, o comando a ser executado e um booleano para informar se é pra retornar todos os dados ou somente as informações após fazer o login. A conexão com Telnet é feita na linha 22 e 24. Após à conexão efetuada, informamos o usuário e a senha utilizando o método doLogin(). Na linha 48 utilizamos o caracter ">" como delimitador de fim do comando de login. Você pode substituí-lo pelo correspondente no seu server de Telnet.

Na linha 61 e 17 evitamos o uso de caracteres especiais para não termos problemas com o nosso cliente de WebService. Você pode utilizar outro tratamento, por exemplo: utilizar o CDATA ou realizar escape dos caracteres.

Agora é só executar o serviço passando os parâmetros adequados. O importante é passar corretamente o delimitador. Esse campo indica quando terminou a execução de um comando e quando o serviço pode retornar a resposta. Você pode inclusive executar os comandos com parâmetros e utilizar o operador && para executar mais de um comando. Abaixo temos o exemplo de uma requisição:





         usuario
         senha
         FIM
         <![CDATA[ ps && comandoQueImprimiFim ]]>
         false



Com essa execução, a resposta seria:




         ps && comandoQueImprimiFim
   PID  CLS PRI TTY      TIME COMD
  6626   TS  59 pts134   0:00 ps
  5867   TS  70 pts134   0:00 bash
FIM



Aí é só utilizar essa resposta em qualquer outro sistema que posso se comunicar via WebService SOAP.

Sobre: Thiago Galbiatti Vespa

Thiago Galbiatti Vespa é mestre em Ciências da Computação e Matemática Computacional pela USP e bacharel em Ciências da Computação pela UNESP. Coordenador de projetos do JavaNoroeste, membro do JCP (Java Community Process), consultor Oracle, arquiteto de software de empresas de médio e grande porte, palestrante de vários eventos e colaborador de projetos open source. Possui as certificações: Oracle Certified Master, Java EE 5 Enterprise Architect – Step 1, 2 and 3; Oracle WebCenter Portal 11g Certified Implementation Specialist; Oracle Service Oriented Architecture Infrastructure Implementation Certified Expert; Oracle Certified Professional, Java EE 5 Web Services Developer; Oracle Certified Expert, NetBeans Integrated Development Environment 6.1 Programmer; Oracle Certified Professional, Java Programmer; Oracle Certified Associate, Java SE 5/SE 6