Recursos como requisições http já trazem configurações de timeout definidas no código, mas as vezes este recurso não está disponível na API em uso, nesta postagem vamos descrever uma das possibilidades de implementação de timeout via código.
A abordagem que vamos utilizar é a mesma abordagem utilizada na postagem sobre Web Service com Cliente Assíncrono.
O método externo
// Esta classe é apenas um exemplo de código não controlado, seria a nossa API externa.
static class ExecucaoNaoControlada {
// O momento de retorno deste método não é conhecido, pode ocorrer em qualquer momento.
public String executar() throws Exception {
System.out.println("# Informe seu nome:");
return new BufferedReader(new InputStreamReader(System.in)).readLine();
}
}
Este trecho de código é apenas ilustrativo, este seria o método de uma API que estamos utilizando que não implementa timeout nativamente, mas este método pode demorar para concluir e nosso código não pode esperar um tempo indeterminado.Uma classe de conexão
// Esta classe apenas faz a conexão entre o nosso código e o código não controlado
static class ClasseDeConexao implements Callable<String> {
@Override
public String call() throws Exception {
return new ExecucaoNaoControlada().executar();
}
}
Estre trecho de código é uma classe de conexão. Esta classe fará a junção entre a API sendo utilizada e o nosso código. Esta classe adicional irá nos trazer a possibilidade de controle de tempo através da interface Callable
.Controlando a execução
Vamos conhecer os agentes responsáveis pelo proposto nesta postagem.java.util.concurrent.Executors
- Esta classe é responsável pela criação de um gestor de pool de threads, no nosso exemplo um gestor de thread única, outras opções podem ser avaliadas dependendo dos serviços que serão conectados.java.util.concurrent.ExecutorService
- Esta classe será o nosso gestor do pool de threads.java.util.concurrent.Future
- Esta classe será responsável por manter o controle sobre a nossa classe de conexão.Tudo junto
package testes;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class Timeout {
static ExecutorService tpes = Executors.newSingleThreadExecutor();
public static void main(String[] args) {
Future<String> handle = tpes.submit(new ClasseDeConexao());
try {
System.out.println("Iniciando chamada com timeout.");
String resposta = handle.get(10, TimeUnit.SECONDS);
System.out.println("O método retornou: " + resposta);
} catch(InterruptedException e) {
System.err.println("A thread principal foi interrompida.");
} catch(ExecutionException e) {
System.err.println("A execução do método lançou uma exceção.");
} catch(TimeoutException e) {
System.err.println("O método estourou o tempo limite (timeout).");
}
System.out.println("Encerrando aplicativo.");
System.exit(0);
}
// Esta classe apenas faz a conexão entre o nosso código e o código não controlado
static class ClasseDeConexao implements Callable<String> {
@Override
public String call() throws Exception {
return new ExecucaoNaoControlada().executar();
}
}
// Esta classe é apenas um exemplo de código não controlado, seria a nossa API externa.
static class ExecucaoNaoControlada {
// O momento de retorno deste método não é conhecido, pode ocorrer em qualquer momento.
public String executar() throws Exception {
System.out.println("# Informe seu nome:");
return new BufferedReader(new InputStreamReader(System.in)).readLine();
}
}
}
Através do nosso gestor de threads nós solicitamos a submissão de uma classe que implementa Callable
para execução através do pool de threads, neste momento a classe ainda não será enviada para execução, mas nos é retornado um handle do tipo Future
.Através do método
get
do nosso handle a classe é enviada para execução no pool de threads e será iniciada assim que possível. O método get
é um método bloqueante e a execução não continuará até que a classe chamada retorne um resultado, mas os dois parâmetros do método definem o tempo que nosso código poderá esperar para que um resultado seja entregue, caso este tempo seja atingido uma exceção do tipo TimeoutException
será lançada, mas caso o resultado seja entregue em tempo, o código irá continuar sua execução no caminho normal.Faça testes com o código de exemplo, informando um texto assim que solicitado, e também não informando nada e deixando o tempo esgotar.
Me ajudou muito teu código, Obrigado!
ResponderExcluirValeu!!!
ExcluirObrigado!! Me ajudou!
ResponderExcluirValeu!!!
ExcluirValeu, Claudio. Dizem que, não importa quanto tempo passe, a internet não perdoa. Mas, por outro lado, ajuda, mesmo muito tempo depois. Isto era exatamente o que eu procurava: como implementar uma JOptionPane.showInputDialog (ou JOptionPane.showConfirmDialog) com resposta default por timeout. Seu texto com exemplo simples, claro e muito bem explicado, está de parabéns.
ResponderExcluirValeu!!!
Excluir