Exceção esta tirando o ActionListener de um JComboBox

Bom dia galera,

Estou com um problema em uma tela Swing que estou fazendo. Não tive muitas oportunidades de desenvolver telas Swing mais complexas como as que estou fazendo agora … é provável que este meu erro seja algo simples, porém se puderem me dar algumas dicas ficarei agradecido.

Tenho uma tela onde possuem alguns combos aninhados.

[list]No primeiro combo o usuário irá escolher uma conexão dentre uma lista fornecida;[/list]
[list]Ao ser escolhida a conexão, a aplicação irá se conectar ao servidor para baixar as informações para carregar o segundo combo;[/list]

O meu problema esta sendo o seguinte:

Em algumas situações, não será possível se conectar com o servidor escolhido por indisponibilidade … neste caso, eu fiz o tratamento de exceções dentro do método que iria carregar os combos conforme pode ser visto logo abaixo.

O mais estranho é que eu faço o tratamento da exceção logando a mensagem com Log4J e mostrando uma mensagem amigável para o usuário através de um popup. Não existe nenhuma exceção sem este tipo de tratamento.

Porém, após a exceção, o combo de conexão fica bobo … é como se ele perdesse o ActionListener que inicialmente foi adicionado a ele.

Alguém teria alguma dica?!?

[]'s

CombosAninhados.java


import java.awt.Dimension;

public class CombosAninhados extends JFrame {
	
	private static final long serialVersionUID = 1L;
	
	private JLabel labelConnection;
	private JLabel labelCategory;
	private JComboBox comboConnection;
	private JComboBox comboCategory;
	private JPanel panelGrid;
	private JLabel labelProduct;
	private JComboBox comboProduct;
	
	private List<String> connections;
	
	private static final String COMBO_CONNECTION_LABEL_SELECT = "Select";
	
	public CombosAninhados() {
		super("Combos aninhados");
		setPreferredSize(new Dimension(400, 300));
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(ScreenUtils.getBounds(400, 300, true));
		
		getContentPane().setLayout(null);
		
		this.connections = new ArrayList<String>();
		this.connections.add("192.168.101.23:9810");
		this.connections.add("192.168.102.23:9810");
		this.connections.add("192.168.103.23:9810");
		this.connections.add("192.168.104.23:9810");
		
		JPanel panel = new JPanel();
		panel.setBorder(new TitledBorder(null, "Combos Aninhados", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		panel.setBounds(10, 10, 359, 238);
		getContentPane().add(panel);
		panel.setLayout(null);
		
		labelConnection = new JLabel("Conex\u00E3o");
		labelConnection.setBounds(15, 30, 100, 14);
		panel.add(labelConnection);
		
		comboConnection = new JComboBox();
		comboConnection.setBounds(115, 27, 230, 20);
		panel.add(comboConnection);
		
		labelCategory = new JLabel("Categoria");
		labelCategory.setBounds(15, 58, 100, 14);
		panel.add(labelCategory);
		
		comboCategory = new JComboBox();
		comboCategory.setBounds(115, 55, 230, 20);
		comboCategory.setEnabled(false);
		panel.add(comboCategory);
		
		labelProduct = new JLabel("Produdo");
		labelProduct.setBounds(15, 86, 100, 14);
		panel.add(labelProduct);
		
		comboProduct = new JComboBox();
		comboProduct.setBounds(115, 83, 230, 20);
		comboProduct.setEnabled(false);
		panel.add(comboProduct);
		
		panelGrid = new JPanel();
		panelGrid.setBounds(15, 111, 330, 110);
		panel.add(panelGrid);
		
		comboConnectionInitialize();
		
		setResizable(false);
		setVisible(true);
	}
	
	private void comboConnectionInitialize() {
		comboConnection.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				comboConnectionActionListener(e);
			}
		});
		comboConnection.addItem(COMBO_CONNECTION_LABEL_SELECT);
		for( String conn : this.connections ) {
			comboConnection.addItem(conn);
		}
	}
	
	private void comboCategoryInitialize(List<String> categories) {
		comboCategory.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				comboCategoryActionListener(e);
			}
		});
		comboCategory.addItem(COMBO_CONNECTION_LABEL_SELECT);
		for( String category : categories ) {
			comboConnection.addItem(category);
		}
		comboCategory.setEnabled(true);
	}
	
	private void comboProductInitialize(List<String> products) {
		comboProduct.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				comboCategoryActionListener(e);
			}
		});
		comboProduct.addItem(COMBO_CONNECTION_LABEL_SELECT);
		for( String product : products ) {
			comboProduct.addItem(product);
		}
		comboProduct.setEnabled(true);
	}
	
	private void comboConnectionActionListener(ActionEvent evt) {
		try {
			JComboBox combo = (JComboBox) evt.getSource();
			String  selected = (String) combo.getSelectedItem();
			
			if( COMBO_CONNECTION_LABEL_SELECT.equals(selected) ) { 
				// do nothing
			} else {
				ConnectionManager cm = ConnectionManager.getInstance();
				cm.connect(selected);
				List<String> categories = cm.getCategories();
				comboCategoryInitialize(categories);
			}
		} catch (ConnectionRefusedException cre) {
			throw new AppException("application.messages.connection.refused");
		} catch (Exception e) {
			throw new AppException();
		}		
	}
	
	private void comboCategoryActionListener(ActionEvent evt) {
		try {
			JComboBox combo = (JComboBox) evt.getSource();
			String  selected = (String) combo.getSelectedItem();
			
			if( COMBO_CONNECTION_LABEL_SELECT.equals(selected) ) { 
				// do nothing
			} else {
				ConnectionManager cm = ConnectionManager.getInstance();
				List<String> products = cm.getProducts(selected);
				comboProductInitialize(products);
			}
		} catch (ConnectionRefusedException cre) {
			throw new AppException("application.messages.connection.refused");
		} catch (Exception e) {
			throw new AppException();
		}
	}
	
	public static void main(String[] args) {
		new CombosAninhados();
	}
}

AppException.java

import org.apache.log4j.Logger;

public class AppException extends RuntimeException {

	public AppException() {
		String message = MessageUtils.getString("application.messages.erro.generico");
		Logger.getLogger(this.getClass()).error(message);
		MessageUtils.showMessage(null, message, MessageUtils.ERROR_MESSAGE_TYPE);
	}

	public AppException(String message, Throwable cause) {
		String msg = MessageUtils.getString(message);
		Logger.getLogger(this.getClass()).error(msg, cause);
		MessageUtils.showMessage(null, msg, MessageUtils.ERROR_MESSAGE_TYPE);
	}

	public AppException(String message) {
		String msg = MessageUtils.getString(message);
		Logger.getLogger(this.getClass()).error(msg);
		MessageUtils.showMessage(null, msg, MessageUtils.ERROR_MESSAGE_TYPE);
	}

	public AppException(Throwable cause) {
		Logger.getLogger(this.getClass()).error(cause);
		MessageUtils.showMessage(null, cause.getMessage(), MessageUtils.ERROR_MESSAGE_TYPE);
	}

}

ConnectionRefusedException.java


public class ConnectionRefusedException extends RuntimeException {

	private static final long serialVersionUID = 1L;

	private String message;
	private Throwable cause;
	
	public ConnectionRefusedException() { }

	public ConnectionRefusedException(String message) {
		this.message = message;
	}

	public ConnectionRefusedException(Throwable cause) {
		this.cause = cause;
	}

	public ConnectionRefusedException(String message, Throwable cause) {
		this.message = message;
		this.cause = cause;
	}

}

ConnectionManager.java

import java.util.ArrayList;
import java.util.List;

public class ConnectionManager {

	private static ConnectionManager instance;
	
	private ConnectionManager() { }
	
	public static ConnectionManager getInstance() {
		if ( instance == null ) instance = new ConnectionManager();
		return instance;
	}
	
	public void connect(String server) {
		try {
			
			// try to connect
			
		} catch( Exception e ) {
			throw new ConnectionRefusedException(e);
		}
	}
	
	public List<String> getCategories() {
		return new ArrayList<String>();
	}
	
	public List<String> getProducts(String category) {
		return new ArrayList<String>();
	}
}

Tudo que estiver no construtor so sera chamado quando voce instancia a classe, logo, se voce a instancia 1 vez ela so vai executar o metodo comboConnectionInitialize na primeira vez, apos isso se perde.

Minha sugestao é voce adicionar o actionListener no construtor, exemplo:

[code]
comboConnection = new JComboBox();
comboConnection.setBounds(115, 27, 230, 20);

// Preenche o combo
comboConnection.addItem(COMBO_CONNECTION_LABEL_SELECT);
for( String conn : this.connections ) {
comboConnection.addItem(conn);
}

comboCategory.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
comboCategoryActionListener(e);
}
});
panel.add(comboConnection); [/code]

Entendeu ? montei o combo na instanciacao, ja que os valores do comboConnection sao fixos.
Depois adicionei o actionListener.
Agora voce apaga o metodo comboConnectionInitialize do construtor.

Acredito que so isso ja resolva seu problema. Da uma testada.

Não resolve não … eu já tentei isso. Pensei algo parecido e modifiquei para testar, porém o erro persistiu.

O que mais me intriga é que eu não estou deixando nenhuma exceção sem tratamento, e quando o problema ocorre, a exceção vai para o console, como se tivesse um e.printstacktrace() no código. E não tem … curioso não?!?

Faz um teste, tira o try-catch entao, tb acho estranho, massssss…

Acontece a mesma coisa … o combo esta perdendo o action listener =/

Resolvi o problema, mas não gostei da solução que tive que fazer.

Aparentemente, o problema é causado sempre que aparece um throw new ApplicationException dentro de um ActionListener.

Para resolver o problema, eu simplesmente substitui o throw new ApplicationException pelo tratamento que era feito dentro da própria exceção:

Era assim:

} catch (ConnectionRefusedException ce) { throw new ApplicationException("application.messages.connection.refused"); } catch (Exception ec) { throw new ApplicationException(); } Agora esta assim:

	} catch (ConnectionRefusedException ce) {
		String msg = MessageUtils.getString("application.messages.connection.refused");
		Logger.getLogger(this.getClass()).error(msg);
		MessageUtils.showMessage(null, msg, MessageUtils.ERROR_MESSAGE_TYPE);
	} catch (Exception ec) {
		String message = MessageUtils.getString("application.messages.error.generic");
		Logger.getLogger(this.getClass()).error(message);
		MessageUtils.showMessage(null, message, MessageUtils.ERROR_MESSAGE_TYPE);
	}

Alguém tem alguma explicação lógica para isso?!?!?!

[]'s

Tem sim:

http://www.java-samples.com/showtutorial.php?tutorialid=294

Cuidado com excesso de throw =D

Então, isso não responde minha questão.

Tanto é, que a única coisa que eu fiz foi remover o último throw que seria o que apresentaria as mensagens da exceção.

Estranho isso … rs

Se nao responde sinto muito, mas ate onde eu sei isso tb lança um stackTrace ao dar um shutdown no programa. Quando voce lanca throw ***Exception, voce diz para o programa lançar uma exceção, o que para tratar, apos um lançamento, deve ter um catch, no caso, sua logica esta possibilitando a finalizacao do mesmo, pois voce lança uma excecao dentro do catch e nao o trata e uma excessao nao tratada finaliza o programa ou encerra o seu percurso. Se voce da um catch numa excessao, voce nao precisa lançar ela novamente, logo seu throw new esta lancando a mesma execessao 2 vezes, e pelo meu pouco conhecimento é que toda excessao deve ser tratada, logo, throw new nao é tratada e tb é redundante no seu codigo. Experimente usar throw AppException ao inves de throw new. Espero ter ajudado.

Segue um link de como trabalhar com lançamento de excessoes: http://docs.oracle.com/javase/tutorial/essential/exceptions/throwing.html

Vamos tirar bons frutos desta discussão … rs

No meu código, tem vários try/catch e normalmente eu sempre dou o throw dentro do catch para identificar que tipo de exceção esta sendo lançada.

Por exemplo, em um método que eu tento fazer a conexão com o servidor, independente da exceção lançada eu dou throw em uma ConnectionRefusedException que herda da RuntimeException (unchecked exception) porém ela não tem o super() nos seus construtores … simplesmente encapsula a mensagem ou throwable lançado.

Ai então, na camada de visualização da app (jpanel ou jform) tenho um try/catch com um catch para cada possibilidade de exceção … neste ponto então, eu dou throws new ApplicationException, que por sua vez herda RuntimeException e loga a exceção e mostra uma mensagem amigável ao usuário.

Sempre tratei exceções desta forma … em aplicações com várias camadas, os métodos tem throw UncheckedException e na camada de visualização eu trato com um try/catch gigante, verificando cada possibilidade de exceção a ser lançada.

Acho que nesta situação em especifico, o problema foi dar um throw de dentro de um action listener … por algum motivo este action listener se torna inválido para a aplicação.

Tanto que quando eu tirei o throw de dentro do try/catch e mostrei a mensagem de erro no popup, parou de acontecer …

Vivendo e aprendendo … ou não … rs

é velho, por isso so que nem o John Connor, nao confio nas maquinas =D.

Cara,

Nesta documentação eu vi que o mais aconselhado seria ao invés de ter um try/catch para cada método, independente da camada da aplicação, teria somente na minha camada de visualização, onde ali seria tratada todos as possíveis exceções.

Isso na verdade é bastante controverso … rs, até porque muitas vezes quando estamos reutilizando componentes (que é o meu caso) não temos o knowhow de todas as possibilidades de exceção que podem ser lançadas em um método.

Exemplificando melhor, em um método que faz uma conexão RMI/CORBA através de um EJB, pode ser lançada uma série de exceções diferentes, porém o que é importante para o meu negócio é simplesmente saber que não foi possível se conectar.

Como eu disse, é meio controverso …

Porém, valeu a discussão =)

Obrigado :wink:

[quote]
Isso na verdade é bastante controverso … rs, até porque muitas vezes quando estamos reutilizando componentes (que é o meu caso) não temos o knowhow de todas as possibilidades de exceção que podem ser lançadas em um método. [/quote]

Com certeza. Flw xD