Threads + JDialog - Dúvidas de como usar corretamente

31 respostas Resolvido
jframethreadsswingjava
B

Estou com algumas dúvidas relacionadas às Threads + JDialog, vou explicar algumas situações e gostaria de saber como aplicar para que funcione da maneira que eu preciso.

Atualmente tenho o seguinte:

Possuo um JFrame que contém dois JPanel, um deles é a barra de menu superior que possui alguns botões e o outro JPanel é o que eu chamo de desktop, pois é nele que vou atribuindo as telas específicas quando o usuário clica nos botões específicos, no primeiro JPanel que eu citei, que é a barra de menu superior e que está presente durante toda a aplicação e em toda e qualquer outra tela, possuo algumas Threads (Runnable) que fazem um “monitoramento” em saídas específicas de um CLP com o qual a minha aplicação se comunica, então dependendo do status de algumas dessas saídas que estão sendo monitoradas preciso abrir um JDialog com algumas informações, levando em consideração esse caso segue alguns pontos:

- Usando o JDialog com o parâmetro modal = true quando o mesmo ficar visível toda aplicação abaixo dele fica bloqueada, até mesmo o JFrame principal, então o usuário não pode fechar a aplicação sem que o JDialog seja "liberado".

 - Usando o JDialog com o parâmetro modal = false e o setAlwaysOnTop(true) quando o mesmo fica visível toda a aplicação abaixo dele fica liberada, sendo que o usuário pode acessar outras telas clicando nos botões da tela abaixo do JDialog sem problemas nenhum.

Resumindo a minha necessidade, eu gostaria de uma junção dos dois pontos que citei acima, mais ou menos assim:

- A thread chama o JDialog e este fica visível bloqueando o acesso ao JPanel (desktop) que citei acima, mas o JPanel de menu superior e o JFrame ficam disponíveis, principalmente o JFrame, para que o usuário possa fechar a aplicação se necessário, sem ter que liberar o JDialog, como posso fazer isso?

Então, agora além desta dúvida, ainda tenho outra, neste JDialog que é aberto, nele eu tenho outra thread (Runnable) que também fica “monitorando” saídas específicas de um CLP, para que esse JDialog possa ser liberado/fechado certas saídas que estão sendo monitoradas precisam estar acionadas, senão o JDialog não pode sumir, além disso tenho neste JDialog alguns labels com a descrição destas saídas e um icon para quando elas estiverem liberadas e outro para quando está bloqueadas, esses icons são setados pela thread de acordo com a leitura que a mesma vai realizando no CLP, o que acontece é que ás vezes parece que que thread trava ou “se perde”, pois o JDialog e seus componentes ficam se modificando sem sentido e até mesmo já aconteceu de travar e não permitir mais sair, gostaria de saber o que posso estar fazendo de errado e o que seria o ideal neste caso.

Se caso não for possível entender da maneira que expliquei me coloco a disposição para eclarecer o que for necessário.

Desde já agradeço.

Fico no aguardo.

Att.

31 Respostas

S

Quantos CLPs você tem e quantas Threads monitoram ele?

B

Um CLP e atualmente, no JPanel que citei, tem sete threads e nos JDialog (são quatro) que abrem em virtude do que acontecer no monitoramento dessas threads, tenho uma thread em cada, que fica monitorando para liberar ou não o “fechar” do JDialog.

S

Sugiro que tenha somente uma Thread para monitorar o CLP e crie uma estrutura de Listener para ser notificado sobre as mudanças no estado do CLP.

B

Já tinha pensado nesta questão de juntar e fazer somente uma thread, vou implementar isto, obrigada! Essa estrutura de Listener, poderias me explicar melhor?

E quanto aos JDialog, teria alguma ideia do que posso fazer?

S

Dê uma estudada no padrão de projeto Observer (também chamado de Listener).

Você já usou no seu dia-a-dia, apenas não percebeu.
Quando você quer tratar por exemplo o clique em um botão, você adiciona um ActionListener ao botão e o botão avisa esse ActionListener quando o botão é acionado, assim, toda classe que precisar ser notificada quando aquele botão é acionado, deve adicionar um ActionListener ao botão.

No seu caso você não vai adicionar listeners à um botão e sim à sua classe que monitora o CLP.
Sem ver o seu código, não tenho como te orientar da melhor forma, mas você pode por exemplo:

  • Criar uma classe CLPMonitor, que é quem vai monitorar o CLP (hoje você faz isso em várias Threads, agora vai fazer isso em uma só, encapsulada nessa classe);

  • Criar uma interface chamada CLPListener, nessa interface defina um método para cada evento que possa ser disparado pela classe CLPMonitor;

  • Crie uma classe CLPEvent, para encapsular as informações geradas à cada evento, esse CLPEvent você passa como parâmetro aos métodos da interface CLPListener;

Com essa estrutura acima, basta você adicionar CLPListeners ao CLPMonitor de forma que o CLPMonitor chame os métodos do CLPListener a medida que o monitor percebe mudanças no CLP.

B

Olá staroski,

Vou começar hoje implementar essa estrutura de Listener que me sugeriu, mas antes quero tirar umas dúvidas à respeito:

  1. A classe CLPMonitor irá implementar a interface CLPListener, então na classe além de implementar todos os métodos abstrator da interface eu terei ainda uma única thread que vai monitorar o CLP e dependendo da informação recebida irá disparar um desses métodos, isto?

  2. Quanto à classe CLPEvent, que você citou que deve ser passada como parâmetro aos métodos da interface, não entendi muito bem, seria por exemplo, a thread recebeu uma informação de erro do CLP então um dos métodos no CLPMonitor(método abstrato da interface) será disparado e neste método será usado o CLPEvent que seria “o evento que acontece” quando o tal erro do CLP foi identificado?

  3. Se não for pedir muito, poderia me exemplificar como ficaria a comunicação entre essas classes/interface, levando em consideração o exemplo que citei acima, de um erro qualquer recebido do CLP e que irá disparar um dos métodos para abrir uma janela sobre o erro, algo assim.

Enquanto isso vou implemantando em um geral a classe CLPMonitor e a interface.

Grata.

S

Não, a classe CLPMonitor terá um método addCLPListener para você poder adicionar listeners à ela.

O que você monitora no CLP? A ideia é essa classe encapsular os dados lidos do CLP.

Fica complicado eu fazer o exemplo sendo que você não postou o código que você tem.
Não sei o que você monitora nos CLPs.

B

Eu monitoro variáveis/bits específicos, por exemplo, uma variável é 4099 e o bit é 9 essa variável e bit em questão retornam se o equipamento está em falha, tem diversas outras variáveis com seus próprios bits, retornando true e false dependendo da situação. Então através de uma biblioteca modbus externa faço leitura e escrita, exemplo:

if(modbus.readBoolean(4099, 9) {
     // máquina em falha
     // abre tela informativa
}

Uso neste sentido. Então atualmente eu tenho threads que ficam rodando e dentro delas tenho o verificador if monitorando as variáveis/bits conforme exemplo acima, e quando entra no if realiza determinada coisa, na maioria somente faz reset em algumas variáveis/bits (writeBoolean(variável, bit, false)) e em outras abre um JDialog confome citei.

  • Como seria mais ou menos esse método addCLPListener?

  • O CLPEvent vai conter o que for lido no CLP?

S
public class CLPMonitor {

    private List<CLPListener> listeners = new LinkedList<>();

    public void addCLPListener(CLPListener listener) {
        listeners.add(listener);
    }

    public void iniciar() {
        // aqui você dispara a Thread pra monitorar o CLP
    }
}

No código que monitora o CLP você fará algo mais ou menos assim:

if (modbus.readBoolean(4099, 9) {
    for (CLPListener listener : listeners) {
        listener.maquinaEmFalha(new CLPEvent(4099, 9));
    }
}
B

Vou te mandar o que tenho até o momento, para que você verifique se estou fazendo corretamente:

Interface CLPListener:

public interface CLPListener {
   // (4099, 9)
   public void generalEmergency(CLPEvent event);
}

Classe CLPMonitor:

public class CLPMonitor {

    private Protocol protocol;
    private List<CLPListener> listeners = new LinkedList<CLPListener>();

    public CLPMonitor() {
        protocol = ActionMachineBuilder.getConnection();
        clpMonitor();
    }

    public void addCLPListener(CLPListener listener) {
        listeners.add(listener);
    }

    public void clpMonitor() {
        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    protocol.connect();

                    while (true) {
                        if (protocol.readBoolean(4099, 9))  {
                            for (CLPListener listener : listeners) {
                                listener.generalEmergency(new CLPEvent(4099, 9));
                            }
                        }
                    }
                } catch (Exception e) {
                    // ignored
                    protocol.disconnect();
                }
            }
        });
        thread.start();
    }
}

Classe CLPEvent:

public class CLPEvent {
    
    private int variable;
    private int bit;
   
    public CLPEvent(int variable, int bit) {
        this.variable = variable;
        this.bit = bit;
    }
}

Até aqui tudo certo?

Agora quanto ao “evento” que deve ser disparado, por exemplo, a abertura de um JDialog informando a situação de máquina em falha, deve ser feito na classe que irá instanciar o CLPMonitor? E essa classe irá implementar os métodos da interface CLPListener, ou essa implementação já é na classe CLPEvent?

No meu caso eu instanciaria a classe CLPMonitor no meu JPanel fixo de menu, para que fique monitorando durante toda a execução da aplicação.

S

Em princípio sim, eu renomearia o método public void clpMonitor() para public void start(), fica mais legível.

Não precisa exatamente ser a classe que instanciou o CLPMonitor, não sei como está sua implementação.
O método addCLPMonitorListener permite que qualquer implementação de CLPMonitorListener se registre nele para ser avisado quando algo acontece.

O CLPEvent só encapsula os dados obtidos pelo CLP, o CLPEvent não implementa a interface CLPListener.

OK, então no seu JPanel após inicializar o CLPMonitor (ou receber uma instância dele por parâmetro, não sei como está implementado) você fará isso:

meuObjetoClpMonitor.addCLPListener(new CLPListener() {

    public void generalEmergency(CLPEvent event) {
        // aqui você chama algum método pra fazer algo quando a mensagem do CLP chegar
    }
});

Percebe como o uso de listener simplifica a implementação?

Antes você tinha meia dúzia de threads perguntando o status do CLP o tempo todo.

Agora você tem somente uma thread que faz isso e que avisa todos os objetos interessados.

Quem quer receber avisos do CLPMonitor, só precisa registrar um CLPListener.

B

Ah sim, muito obrigada, agora ficou claro como vai ser o funcionamento, não há uma implementação da interface, eu ignorei completamente o conceito do Listener em si, desculpe.

Tranquilo, agora uma dúvida quanto ao monitoramento, por exemplo, no meu JPanel que comentei que roda em toda a aplicação eu vou ter o seguinte:

CLPMonitor clpMonitor = new CLPMonitor();
clpMonitor.start();
clpMonitor.addCLPListener(new CLPListener() {
     @Override
     public void generalEmergency(CLPEvent event) {
         // método à ser chamado com o que deve ser feito   
     }
});

Esse código irá startar a thread de monitoramento na classe CLPMonitor e o Listener fica escutando para então disparar o trecho de código necessário em cada situação que o CLP fornecer. Agora, por exemplo, se eu tiver um outra classe e nela eu preciso também monitorar algo no CLP, mas é somente uma ou duas situações específicas para aquele processo que tem naquela classe, se eu fizer nela o mesmo trecho de código acima eu estarei startando a thread novamente e vou ter ela rodando duas vezes, estou certa?
Então se eu somente fizer:

CLPMonitor clpMonitor = new CLPMonitor();
clpMonitor.addCLPListener(new CLPListener() {
     @Override
     public void generalEmergency(CLPEvent event) {
         // método à ser chamado com o que deve ser feito   
     }
});

Já vai funcionar né? Não preciso chamar o método clpMonitor.start()? E aí neste caso eu só implemento o método que eu precisar no Listener?

S

Nas outras classes você não vai chamar o método start(), você vai somente passar o mesmo objeto clpMonitor e adicionar mais um CLPListener ao objeto.

B

Conforme eu fiz acima então? Crio um novo objeto CLPMonitor e adiciono o Listener e aí implemento os métodos que necessito e pronto?

A única diferença é que não vou evocar o método start(), este só será evocado uma vez, isto?

Grata.

S

Não, ninguém falou em criar um novo objeto CLPMonitor, você vai instanciar o CLPMonitor uma única vez.

O mais simples é você passar o objeto CLPMonitor que você instanciou para elas e aí essas classes fazem o addCLPMonitorListener.

B

Ok então, irei fazer isto e ver como vai funcionar.

B

Olá,

Bom, estive implementando o que tratamos aqui no tópico e acabou me surgindo uma dúvida, provavelmente pode ser levada como “boba”, mas asism, conforme citei acima, o que eu uso para monitorar as variáveis/bits na thread do CLPMonitor é o seguinte:

if (protocol.readBoolean(4099, 9))  {
     for (CLPListener listener : listeners) {
         listener.generalEmergency(new CLPEvent(4099, 9));
     }
}

Então, fazendo isto, quando o CLP retornar true na variável 4099 bit 9 o listener vai disparar o evento específico, mas se eu também quiser ficar sabendo quando a variável/bit em questão estiver retornando false? É somente fazer um else nesse if acima e adicionar um listener para disparar um evento específico para quando for false?

S

Exatamente, para cada situação esperada você cria um método diferente na sua interface CLPListener.

B

Olá staroski,

Faz tempo que não mexo aqui no tópico porque não havia tido a oportunidade de testar na prática esta implementação, esta semana estou fazendo isto, mas estive me deparando com a seguinte exceção:

Exception in thread "Thread-6" java.util.ConcurrentModificationException
	at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:966)
	at java.util.LinkedList$ListItr.next(LinkedList.java:888)
	at com.cutplanning.cvl.monitor.CLPMonitor$1.run(CLPMonitor.java:111)
	at java.lang.Thread.run(Thread.java:748)

Dei uma pesquisada, mas não consegui definir uma solução para isto, saberia me dizer o que eu faço para resolver e também o por que desta exceção?

A única coisa que entendi é que o problema é no acesso à LinkedList a qual adiciono os eventos disparados pelo CLP para então poder tratá-los.

Desde já agradeço.

S

A exceção acontece porque você está alterando uma coleção (adicionando ou removendo itens) enquanto itera usando um enhanced for ou um Iterator.

Posta o código completo da classe que fica fácil te mostrar como resolver o problema.

B

Estou usando uma LinkedList e via thread de monitoramento vou adicionando os eventos disparados.

public class CLPMonitor {

    private Protocol protocol;
    private List<CLPListener> listeners = new LinkedList<CLPListener>();
    private static CLPMonitor eagerInstance = null;

    public static synchronized CLPMonitor getEagerInstance() {
        if (eagerInstance == null) {
           eagerInstance = new CLPMonitor();
        }
        return eagerInstance;
    }

    private CLPMonitor() {

    }

    public void start() {
        clpMonitor();
    }

    public void addCLPListener(CLPListener listener) {
        listeners.add(listener);
    }

    public void clpMonitor() {
        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    while (true) {
                        if (protocol.readBoolean(4099, 2)) {
                            for (CLPListener listener : listeners) {
                                listener.machineReference(new CLPEvent(4099, 2));
                            }
                        } else {
                            for (CLPListener listener : listeners) {
                                listener.machineReferenceOff(new CLPEvent(4099, 2));
                            }
                        }

                        if (protocol.readBoolean(4099, 9)) {
                            for (CLPListener listener : listeners) {
                                listener.generalEmergency(new CLPEvent(4099, 9));
                            }
                        } else {
                            for (CLPListener listener : listeners) {
                                listener.generalEmergencyLiberate(new CLPEvent(4099, 9));
                            }
                        }

                        if (protocol.readBoolean(4140, 6)) {
                            for (CLPListener listener : listeners) {
                                listener.engineOverload(new CLPEvent(4140, 6));
                            }
                        }

                        if (protocol.readBoolean(4099, 8)) {
                            for (CLPListener listener : listeners) {
                                listener.failedMachine(new CLPEvent(4099, 8));
                            }
                        }

                        if (!protocol.readBoolean(4100, 2)) {
                            for (CLPListener listener : listeners) {
                               listener.tablePositioning(new CLPEvent(4100, 2));
                            }
                        }

                        if (protocol.readBoolean(4100, 7)) {
                             for (CLPListener listener : listeners) {
                                 listener.positionTable(new CLPEvent(4100, 7));
                             }
                        }

                        if (protocol.readBoolean(4100, 4) && !protocol.readBoolean(4099, 9)) {
                             for (CLPListener listener : listeners) {
                                 listener.securityCurtain(new CLPEvent(4100, 4));
                             }
                        } else {
                             for (CLPListener listener : listeners) {
                                 listener.securityCurtainLiberate(new CLPEvent(4100, 4));
                             }
                        }
                            }
                        }

                        if (protocol.readBoolean(4099, 6)) {
                            for (CLPListener listener : listeners) {
                                listener.feedPositionOK(new CLPEvent(4099, 6));
                            }
                        }

                        if (protocol.readBoolean(4099, 15)) {
                            for (CLPListener listener : listeners) {
                                listener.machineStopped(new CLPEvent(4099, 15));
                            }
                        } else {
                            for (CLPListener listener : listeners) {
                                listener.machineInMotion(new CLPEvent(4099, 15));
                            }
                        }

                        if (protocol.readBoolean(4100, 3)) {
                            for (CLPListener listener : listeners) {
                                listener.resetFailed(new CLPEvent(4100, 3));
                            }
                        } else {
                            for (CLPListener listener : listeners) {
                                listener.resetFailedOff(new CLPEvent(4100, 3));
                            }
                        }

                        if (protocol.readBoolean(4099, 14)) {
                            for (CLPListener listener : listeners) {
                                listener.machineOK(new CLPEvent(4099, 14));
                            }
                        } else {
                            for (CLPListener listener : listeners) {
                                listener.machineOFF(new CLPEvent(4099, 14));
                            }
                        }
                    }

                } catch (Exception e) {
                    
                }
            }
        });
        thread.start();
    }
}
S

Alguma classe está chamando o método addCLPListener enquanto sua Thread está processando um dos inúmeros for (CLPListener listener : listeners).

Quando eu implemento classes que podem receber vários listeners, eu não utilizo coleções, eu implemento o padrão de projeto Composite com a interface do listener.

O próprio Swing e AWT utilizam essa estratégia através da classe AWTEventMulticaster.
O benefício é que a implementação fica mais limpa, pois acaba com a necessidade de iterar listas de listeners.

No seu caso, vamos criar a classe CLPEventMulticaster, conforme abaixo:

final class CLPEventMulticaster implements CLPListener {

    protected static CLPListener add(CLPListener atual, CLPListener proximo) {
        if (atual == null) {
            return proximo;
        }
        if (proximo == null) {
            return atual;
        }
        return new CLPEventMulticaster(atual, proximo);
    }

    protected static CLPListener remove(CLPListener atual, CLPListener anterior) {
        if (atual == anterior || atual == null) {
            return null;
        }
        if (atual instanceof CLPEventMulticaster) {
            return ((CLPEventMulticaster) atual).remove(anterior);
        }
        return atual;
    }

    private final CLPListener a;
    private final CLPListener b;

    private CLPEventMulticaster(CLPListener a, CLPListener b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public void engineOverload(CLPEvent clpEvent) {
        a.engineOverload(clpEvent);
        b.engineOverload(clpEvent);
    }

    @Override
    public void failedMachine(CLPEvent clpEvent) {
        a.failedMachine(clpEvent);
        b.failedMachine(clpEvent);
    }

    @Override
    public void feedPositionOK(CLPEvent clpEvent) {
        a.feedPositionOK(clpEvent);
        b.feedPositionOK(clpEvent);
    }

    @Override
    public void generalEmergency(CLPEvent clpEvent) {
        a.generalEmergency(clpEvent);
        b.generalEmergency(clpEvent);
    }

    @Override
    public void generalEmergencyLiberate(CLPEvent clpEvent) {
        a.generalEmergencyLiberate(clpEvent);
        b.generalEmergencyLiberate(clpEvent);
    }

    @Override
    public void machineInMotion(CLPEvent clpEvent) {
        a.machineInMotion(clpEvent);
        b.machineInMotion(clpEvent);
    }

    @Override
    public void machineOFF(CLPEvent clpEvent) {
        a.machineOFF(clpEvent);
        b.machineOFF(clpEvent);
    }

    @Override
    public void machineOK(CLPEvent clpEvent) {
        a.machineOK(clpEvent);
        b.machineOK(clpEvent);
    }

    @Override
    public void machineReference(CLPEvent clpEvent) {
        a.machineReference(clpEvent);
        b.machineReference(clpEvent);
    }

    @Override
    public void machineReferenceOff(CLPEvent clpEvent) {
        a.machineReferenceOff(clpEvent);
        b.machineReferenceOff(clpEvent);
    }

    @Override
    public void machineStopped(CLPEvent clpEvent) {
        a.machineStopped(clpEvent);
        b.machineStopped(clpEvent);
    }

    @Override
    public void positionTable(CLPEvent clpEvent) {
        a.positionTable(clpEvent);
        b.positionTable(clpEvent);
    }

    @Override
    public void resetFailed(CLPEvent clpEvent) {
        a.resetFailed(clpEvent);
        b.resetFailed(clpEvent);
    }

    @Override
    public void resetFailedOff(CLPEvent clpEvent) {
        a.resetFailedOff(clpEvent);
        b.resetFailedOff(clpEvent);
    }

    @Override
    public void securityCurtain(CLPEvent clpEvent) {
        a.securityCurtain(clpEvent);
        b.securityCurtain(clpEvent);
    }

    @Override
    public void securityCurtainLiberate(CLPEvent clpEvent) {
        a.securityCurtainLiberate(clpEvent);
        b.securityCurtainLiberate(clpEvent);
    }

    @Override
    public void tablePositioning(CLPEvent clpEvent) {
        a.tablePositioning(clpEvent);
        b.tablePositioning(clpEvent);
    }

    private CLPListener remove(CLPListener listener) {
        if (listener == a) {
            return b;
        }
        if (listener == b) {
            return a;
        }
        CLPListener primeiro = remove(a, listener);
        CLPListener segundo = remove(b, listener);
        if (primeiro == a && segundo == b) {
            return this;
        }
        return add(primeiro, segundo);
    }
}

E agora vamos modificar a classe CLPMonitor para ter somente um atributo do tipo CLPListener:

public class CLPMonitor {

    private Protocol protocol;
    
    // mesmo quando houver mais de um listener
    // para essa classe é como se fosse um só objeto
    // é a magia do padrão Composite
    private CLPListener listener;
    
    private static CLPMonitor eagerInstance;

    public static synchronized CLPMonitor getEagerInstance() {
        if (eagerInstance == null) {
            eagerInstance = new CLPMonitor();
        }
        return eagerInstance;
    }

    private CLPMonitor() {}

    public void start() {
        clpMonitor();
    }

    public void addCLPListener(CLPListener listener) {
        // aqui acontece a mágica ao adicionar listeners
        this.listener = CLPEventMulticaster.add(this.listener, listener);
    }

    public void removeCLPListener(CLPListener listener) {
        // se quiser remover um listener, também pode
        this.listener = CLPEventMulticaster.remove(this.listener, listener);
    }

    public void clpMonitor() {
        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    while (true) {
                        if (listener == null) {
                            // se não há listeners
                            // dá uma chance pras outras Threads
                            // e volta pro início 
                            Thread.yield();
                            continue;
                        }
                        // Veja que maravilha: não há mais loops!!!
                        if (protocol.readBoolean(4099, 2)) {
                            listener.machineReference(new CLPEvent(4099, 2));
                        } else {
                            listener.machineReferenceOff(new CLPEvent(4099, 2));
                        }

                        if (protocol.readBoolean(4099, 9)) {
                            listener.generalEmergency(new CLPEvent(4099, 9));
                        } else {
                            listener.generalEmergencyLiberate(new CLPEvent(4099, 9));
                        }

                        if (protocol.readBoolean(4140, 6)) {
                            listener.engineOverload(new CLPEvent(4140, 6));
                        }

                        if (protocol.readBoolean(4099, 8)) {
                            listener.failedMachine(new CLPEvent(4099, 8));
                        }

                        if (!protocol.readBoolean(4100, 2)) {
                            listener.tablePositioning(new CLPEvent(4100, 2));
                        }

                        if (protocol.readBoolean(4100, 7)) {
                            listener.positionTable(new CLPEvent(4100, 7));
                        }

                        if (protocol.readBoolean(4100, 4) && !protocol.readBoolean(4099, 9)) {
                            listener.securityCurtain(new CLPEvent(4100, 4));
                        } else {
                            listener.securityCurtainLiberate(new CLPEvent(4100, 4));
                        }

                        if (protocol.readBoolean(4099, 6)) {
                            listener.feedPositionOK(new CLPEvent(4099, 6));
                        }

                        if (protocol.readBoolean(4099, 15)) {
                            listener.machineStopped(new CLPEvent(4099, 15));
                        } else {
                            listener.machineInMotion(new CLPEvent(4099, 15));
                        }

                        if (protocol.readBoolean(4100, 3)) {
                            listener.resetFailed(new CLPEvent(4100, 3));
                        } else {
                            listener.resetFailedOff(new CLPEvent(4100, 3));
                        }

                        if (protocol.readBoolean(4099, 14)) {
                            listener.machineOK(new CLPEvent(4099, 14));
                        } else {
                            listener.machineOFF(new CLPEvent(4099, 14));
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }
}

Perceba que agora não há mais aqueles for (CLPListener listener : listeners).
Mesmo que você adicione mais de um CLPListener, a classe CLPMonitor manipula apenas um objeto do tipo CLPListener.
O CLPEventMulticaster é quem liga um CLPListener com outro para atuar como uma lista, assim você não terá mais as java.util.ConcurrentModificationException.

B

Fiz estas alterações que você sugeriu e realmente não recebi mais a exceção, obrigada!

Quanto a ideia de remover um listener, em que casos eu deveria fazer isto?

Eu tenho uma instância única do CLPMonitor, à qual faço o addCLPListener em diversas telas do software, esse addCLPListener eu faço no setVisible() de cada tela, pois cada uma tem suas particularidades à executar em cada evento, entende? Gostaria de saber se esta é uma implementação correta ou se teria outra forma de fazer que seria mais indicada?

S

Quando você quiser, só pus o método para manter a ortogonalidade.
Não faz muito sentido você permitir adicionar algo à uma coleção e não permitir que remova.

Então, nesse caso, faz todo sentido você fazer o addCLPListener quando passar true para o setVisible e chamar o removeCLPListener quando passar false para o setVisible.

B

Eu até tentei isto, mas a princípio parece que ele remove “todos” e aí os eventos específicos não são mais disparados.

Outra coisa que notei é que, por exemplo, estou em uma tela e aí um dos eventos é disparado faz o que precisa, tranquilo, aí saio desta dela e entro em outra e o evento é disparado novamente e tals, mas aí pelo que parece ele ainda acessa o comportamento para aquele evento lá na outra tela que estava antes, o que será que pode ser?

S

Sim, porque você saiu da tela, mas o listener daquela tela continua registrado no CLPMonitor, por isso tem que remover ao sair da tela.

Não deveria, dá uma depurada no método remove do CLPEventMulticaster.

Você copiou a classe conforme eu postei?

Posta os seguintes códigos:

CLPEventMulticaster
CLPMonitor
1 ou 2 telas que adicionam um CLPListener
B
Fiz a depuração no método e pelo que notei ele sempre cai no:

if (atual == anterior || atual == null) {

return null;

}

Segue a classe CLPEventMulticaster:

public final class CLPEventMulticaster implements CLPListener {
    
    protected static CLPListener add(CLPListener atual, CLPListener proximo) {
        if (atual == null) {
            return proximo;
        }
        if (proximo == null) {
            return atual;
        }
        return new CLPEventMulticaster(atual, proximo);
    }

    protected static CLPListener remove(CLPListener atual, CLPListener anterior) {
        if (atual == anterior || atual == null) {
            return null;
        }
        if (atual instanceof CLPEventMulticaster) {
            return ((CLPEventMulticaster) atual).remove(anterior);
        }
        return atual;
    }

    private final CLPListener a;
    private final CLPListener b;

    private CLPEventMulticaster(CLPListener a, CLPListener b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public void generalEmergency(CLPEvent event) {
        a.generalEmergency(event);
        b.generalEmergency(event);
    }

    @Override
    public void generalEmergencyApproved(CLPEvent event) {
        a.generalEmergencyApproved(event);
        b.generalEmergencyApproved(event);
    }

    @Override
    public void securityCurtain(CLPEvent event) {
        a.securityCurtain(event);
        b.securityCurtain(event);
    }

    @Override
    public void securityCurtainApproved(CLPEvent event) {
        a.securityCurtainApproved(event);
        b.securityCurtainApproved(event);
    }

    @Override
    public void engineOverload(CLPEvent event) {
        a.engineOverload(event);
        b.engineOverload(event);
    }

    @Override
    public void failedMachine(CLPEvent event) {
        a.failedMachine(event);
        b.failedMachine(event);
    }

    @Override
    public void feedPositionOK(CLPEvent event) {
        a.feedPositionOK(event);
        b.feedPositionOK(event);
    }

    @Override
    public void tablePositioning(CLPEvent event) {
        a.tablePositioning(event);
        b.tablePositioning(event);
    }

    @Override
    public void positionTable(CLPEvent event) {
        a.positionTable(event);
        b.positionTable(event);
    }

    @Override
    public void machineStopped(CLPEvent event) {
        a.machineStopped(event);
        b.machineStopped(event);
    }

    @Override
    public void machineInMotion(CLPEvent event) {
        a.machineInMotion(event);
        b.machineInMotion(event);
    }

    @Override
    public void resetFailed(CLPEvent event) {
        a.resetFailed(event);
        b.resetFailed(event);
    }

    @Override
    public void resetFailedOff(CLPEvent event) {
        a.resetFailedOff(event);
        b.resetFailedOff(event);
    }

    @Override
    public void machineOK(CLPEvent event) {
        a.machineOK(event);
        b.machineOK(event);
    }

    @Override
    public void machineOFF(CLPEvent event) {
        a.machineOFF(event);
        b.machineOFF(event);
    }

    @Override
    public void machineReference(CLPEvent event) {
        a.machineReference(event);
        b.machineReference(event);
    }

    @Override
    public void machineReferenceOff(CLPEvent event) {
        a.machineReferenceOff(event);
        b.machineReferenceOff(event);
    }

    private CLPListener remove(CLPListener listener) {
        if (listener == a) {
            return b;
        }
        if (listener == b) {
            return a;
        }
        CLPListener primeiro = remove(a, listener);
        CLPListener segundo = remove(b, listener);
        if (primeiro == a && segundo == b) {
            return this;
        }
        return add(primeiro, segundo);
    }
}

Segue a classe CLPMonitor:

public class CLPMonitor {

    private Protocol protocol;
    private CLPListener listener;
    private static CLPMonitor eagerInstance = null;

    public static synchronized CLPMonitor getEagerInstance() {
        if (eagerInstance == null) {
            eagerInstance = new CLPMonitor();
        }
        return eagerInstance;
    }

    private CLPMonitor() {
        
    }

    public void start() {
        protocol = ActionMachineBuilder.getConnection();
        clpMonitor();
    }

    public void addCLPListener(CLPListener listener) {
        this.listener = CLPEventMulticaster.add(this.listener, listener);
    }

    public void removeCLPListener() {
        this.listener = CLPEventMulticaster.remove(this.listener, listener);
    }

    public void clpMonitor() {
        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                try {

                    if (ProtocolConstants.isdemonstrationmode) {
                        sleep(100);
                        return;
                    }

                    protocol.connect();

                    while (true) {
                        if (protocol.readBoolean(4099, 2)) {
                            listener.machineReference(new CLPEvent(4099, 2));
                        } else {
                            listener.machineReferenceOff(new CLPEvent(4099, 2));
                        }

                        if (protocol.readBoolean(4099, 9)) {
                            listener.generalEmergency(new CLPEvent(4099, 9));
                        } else {
                            listener.generalEmergencyApproved(new CLPEvent(4099, 9));
                        }

                        if (protocol.readBoolean(4140, 6)) {
                            listener.engineOverload(new CLPEvent(4140, 6));
                        }

                        if (protocol.readBoolean(4099, 8)) {
                            listener.failedMachine(new CLPEvent(4099, 8));
                        }

                        if (!protocol.readBoolean(4100, 2)) {
                            listener.tablePositioning(new CLPEvent(4100, 2));
                        }

                        if (protocol.readBoolean(4100, 7)) {
                            listener.positionTable(new CLPEvent(4100, 7));
                        }

                        if (protocol.readBoolean(4100, 4) && !protocol.readBoolean(4099, 9)) {
                            listener.securityCurtain(new CLPEvent(4100, 4));
                        } else {
                            listener.securityCurtainApproved(new CLPEvent(4100, 4));
                        }

                        if (protocol.readBoolean(4100, 3) && !protocol.readBoolean(4100, 4)) {
                            listener.resetFailed(new CLPEvent(4100, 3));
                        } else {
                            listener.resetFailedOff(new CLPEvent(4100, 3));
                        }

                        if (protocol.readBoolean(4099, 6)) {
                            listener.feedPositionOK(new CLPEvent(4099, 6));
                        }

                        if (protocol.readBoolean(4099, 15)) {
                            listener.machineStopped(new CLPEvent(4099, 15));
                        } else {
                            listener.machineInMotion(new CLPEvent(4099, 15));
                        }

                        if (protocol.readBoolean(4099, 14)) {
                            listener.machineOK(new CLPEvent(4099, 14));
                        } else {
                            listener.machineOFF(new CLPEvent(4099, 14));
                        }
                    }

                } catch (IOException e) {
                    // ignored
                    protocol.disconnect();
                }
            }
        });
        thread.start();
    }

Segue uma tela (AppMenuPane - tela principal do sistema):

public class AppMenuPane extends JPanel {

    private JPanel bottomMenu;
    private Protocol modbus;

    public AppMenuPane() {
        setLayout(new BorderLayout());
        
        modbus = ActionMachineBuilder.getConnection();
        modbus.disconnect();

        bottomMenu = new MenuPanePane(new FormLayout("0dlu,pref,0dlu,pref,0dlu,pref,0dlu,pref,0dlu,pref,0dlu,pref,0dlu,pref,0dlu,pref,0dlu,pref,0dlu", "0dlu,pref,0dlu"));
        add(bottomMenu, BorderLayout.SOUTH);
    
        // neste bottomMenu tem os botões que acessam as outras telas do sistema
    }

    @Override
    public void setVisible(boolean bln) {
        super.setVisible(bln);
        if (bln) {
            enabledButtons();

            try {
                modbus.connect();
            } catch (IOException ex) {
                //
            }

            CLPMonitor.getEagerInstance().addCLPListener(new CLPListener() {
                @Override
                public void generalEmergency(CLPEvent event) {
                    disableButtons();
                }

                @Override
                public void generalEmergencyApproved(CLPEvent event) {
                    enabledButtons();
                }

                @Override
                public void securityCurtain(CLPEvent event) {
                    disableButtons();
                }

                @Override
                public void securityCurtainApproved(CLPEvent event) {
                    try {
                        modbus.writeBoolean(4097, 9, false);
                        sleep(200);
                    } catch (IOException ex) {
                        //Logger.getLogger(AppMenuPane.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    enabledButtons();
                }

                @Override
                public void engineOverload(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void failedMachine(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void feedPositionOK(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void tablePositioning(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void positionTable(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void machineStopped(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void machineInMotion(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void resetFailed(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void resetFailedOff(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void machineOK(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void machineOFF(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void machineReference(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void machineReferenceOff(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }
            });
        } else {
            modbus.disconnect();
        }
    }

}

Segue outra tela (IOPane):

public class IOPane extends JPanel {

    private JButton backButton;
    private MenuPanePane bottomMenu;
    private Protocol modbus;

    public IOPane() {
        setLayout(new BorderLayout());
        
        modbus = ActionMachineBuilder.getConnection();
        modbus.disconnect();

       // aqui tem umas tabelas

        bottomMenu = new MenuPanePane(new FormLayout("0dlu,pref,0dlu,pref,0dlu,fill:pref:grow,0dlu,pref,0dlu", "0dlu,pref,0dlu"));
        add(bottomMenu, BorderLayout.SOUTH);

        backButton = ButtonUtility.makeButton();
        backButton.setText("Voltar");
        backButton.setPreferredSize(new Dimension(bw, bh));

        backButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                ModuleManager.getInstance().backToLastModule();
            }
        });
        bottomMenu.add(backButton, new CellConstraints(8, 2));
    }

    @Override
    public void setVisible(boolean bln) {
        super.setVisible(bln);

        if (bln) {

            try {
                modbus.connect();
            } catch (IOException ex) {
                //Logger.getLogger(IOPane.class.getName()).log(Level.SEVERE, null, ex);
            }

            CLPMonitor.getEagerInstance().addCLPListener(new CLPListener() {
                @Override
                public void generalEmergency(CLPEvent event) {
                    backButton.setEnabled(false);
                }

                @Override
                public void generalEmergencyApproved(CLPEvent event) {
                    backButton.setEnabled(true);
                }

                @Override
                public void securityCurtain(CLPEvent event) {
                    backButton.setEnabled(false);
                }

                @Override
                public void securityCurtainApproved(CLPEvent event) {
                    try {
                        modbus.writeBoolean(4097, 9, false);
                        sleep(200);
                    } catch (IOException ex) {
                        //Logger.getLogger(IOPane.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    backButton.setEnabled(true);
                }

                @Override
                public void engineOverload(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void failedMachine(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void feedPositionOK(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void tablePositioning(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void positionTable(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void machineStopped(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void machineInMotion(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void resetFailed(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void resetFailedOff(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void machineOK(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void machineOFF(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void machineReference(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public void machineReferenceOff(CLPEvent event) {
                    //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }
            });
        } else {
            modbus.disconnect();
        }
    }
}

Bom, no geral as telas seguem sempre essa mesma estrutura, algumas delas ao clicar no botão de voltar irão retornar sempre para a tela principal (AppMenuPane), outras delas irão voltar para a tela aberta anteriormente (sistema de histórico de telas).

Outra coisa, tenho uma “tela” que nada verdade é um um painel de menu, que fica visível durante todo o tempo, nele eu também tenho um addCLPListener, será que isso não pode ser um problema?

S

É que na classe CLPMonitor você implementou errado o método removeCLPListener, veja:

public void removeCLPListener() { // está faltando o parâmetro listener
    this.listener = CLPEventMulticaster.remove(this.listener, listener); // você está removendo o this.listener
}

O correto é assim:

public void removeCLPListener(CLPListener listenerParaRemover) {
    this.listener = CLPEventMulticaster.remove(this.listener, listenerParaRemover);
}

Eu vi que as implementações dos seus CLPListeners tem muitos métodos vazios, faz sentido pois em cada tela você só quer tratar alguns eventos específicos, mas fica muito feio você toda vez implementar todos os métodos vazios.
O ideal nesse caso, é ter uma classe abstrata que implementa o CLPListener sem fazer nada, aí ao invés de implementar diretamente o CLPListener, você estende essa classe e só sobrescreve os métodos dos eventos do seu interesse.

Vamos criar a classe CLPAdapter, com métodos que não fazem nada:

public abstract class CLPAdapter implements CLPListener {

    @Override
    public void machineReference(CLPEvent clpEvent) {}

    @Override
    public void machineReferenceOff(CLPEvent clpEvent) {}

    @Override
    public void generalEmergency(CLPEvent clpEvent) {}

    @Override
    public void generalEmergencyLiberate(CLPEvent clpEvent) {}

    @Override
    public void engineOverload(CLPEvent clpEvent) {}

    @Override
    public void failedMachine(CLPEvent clpEvent) {}

    @Override
    public void tablePositioning(CLPEvent clpEvent) {}

    @Override
    public void positionTable(CLPEvent clpEvent) {}

    @Override
    public void securityCurtain(CLPEvent clpEvent) {}

    @Override
    public void securityCurtainLiberate(CLPEvent clpEvent) {}

    @Override
    public void feedPositionOK(CLPEvent clpEvent) {}

    @Override
    public void machineStopped(CLPEvent clpEvent) {}

    @Override
    public void machineInMotion(CLPEvent clpEvent) {}

    @Override
    public void resetFailed(CLPEvent clpEvent) {}

    @Override
    public void resetFailedOff(CLPEvent clpEvent) {}

    @Override
    public void machineOK(CLPEvent clpEvent) {}

    @Override
    public void machineOFF(CLPEvent clpEvent) {}

}

Agora veja como suas telas ficam com um código mais limpo:

Tela AppMenuPane:

public class AppMenuPane extends JPanel {

    // com o uso do CLPAdapter, basta sobrescrever apenas os métodos do eventos de interesse desta tela
    private final CLPListener listener = new CLPAdapter() {

        @Override
        public void generalEmergency(CLPEvent event) {
            disableButtons();
        }

        @Override
        public void generalEmergencyApproved(CLPEvent event) {
            enabledButtons();
        }

        @Override
        public void securityCurtain(CLPEvent event) {
            disableButtons();
        }

        @Override
        public void securityCurtainApproved(CLPEvent event) {
            try {
                modbus.writeBoolean(4097, 9, false);
                sleep(200);
            } catch (IOException ex) {
                // Logger.getLogger(AppMenuPane.class.getName()).log(Level.SEVERE, null, ex);
            }
            enabledButtons();
        }
    };

    private JPanel bottomMenu;
    private Protocol modbus;

    public AppMenuPane() {
        setLayout(new BorderLayout());

        modbus = ActionMachineBuilder.getConnection();
        modbus.disconnect();

        bottomMenu = new MenuPanePane(new FormLayout("0dlu,pref,0dlu,pref,0dlu,pref,0dlu,pref,0dlu,pref,0dlu,pref,0dlu,pref,0dlu,pref,0dlu,pref,0dlu","0dlu,pref,0dlu"));
        add(bottomMenu, BorderLayout.SOUTH);

        // neste bottomMenu tem os botões que acessam as outras telas do sistema
    }

    @Override
    public void setVisible(boolean visible) {
        super.setVisible(visible);
        try {
            CLPMonitor clpMonitor = CLPMonitor.getEagerInstance();
            if (visible) {
                enabledButtons();
                modbus.connect();
                clpMonitor.addCLPListener(listener);
            } else {
                modbus.disconnect();
                clpMonitor.removeCLPListener(listener);
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

}

Tela IOPane:

public class IOPane extends JPanel {

    // com o uso do CLPAdapter, basta sobrescrever apenas os métodos do eventos de interesse desta tela
    private final CLPListener listener = new CLPAdapter() {
        @Override
        public void generalEmergency(CLPEvent event) {
            backButton.setEnabled(false);
        }

        @Override
        public void generalEmergencyApproved(CLPEvent event) {
            backButton.setEnabled(true);
        }

        @Override
        public void securityCurtain(CLPEvent event) {
            backButton.setEnabled(false);
        }

        @Override
        public void securityCurtainApproved(CLPEvent event) {
            try {
                modbus.writeBoolean(4097, 9, false);
                sleep(200);
            } catch (IOException ex) {
                // Logger.getLogger(IOPane.class.getName()).log(Level.SEVERE, null, ex);
            }
            backButton.setEnabled(true);
        }
    };

    private JButton backButton;
    private MenuPanePane bottomMenu;
    private Protocol modbus;

    public IOPane() {
        setLayout(new BorderLayout());

        modbus = ActionMachineBuilder.getConnection();
        modbus.disconnect();

        // aqui tem umas tabelas

        bottomMenu = new MenuPanePane(new FormLayout("0dlu,pref,0dlu,pref,0dlu,fill:pref:grow,0dlu,pref,0dlu", "0dlu,pref,0dlu"));
        add(bottomMenu, BorderLayout.SOUTH);

        backButton = ButtonUtility.makeButton();
        backButton.setText("Voltar");
        backButton.setPreferredSize(new Dimension(bw, bh));

        backButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent ae) {
                ModuleManager.getInstance().backToLastModule();
            }
        });
        bottomMenu.add(backButton, new CellConstraints(8, 2));
    }

    @Override
    public void setVisible(boolean visible) {
        super.setVisible(visible);
        try {
            CLPMonitor clpMonitor = CLPMonitor.getEagerInstance();
            if (visible) {
                modbus.connect();
                clpMonitor.addCLPListener(listener);
            } else {
                modbus.disconnect();
                clpMonitor.removeCLPListener(listener);
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}
B

Olá staroski,

Muito obrigada, implementei de acordo com suas orientações e está funcionando perfeitamente bem. :wink:

Agora estou com um outro “probleminha”, talvez até fuja do que vínhamos falando, mas enfim, em um deste eventos disparados eu abro um JDialog e com a liberação deste eu abro outro JDialog que ficará aguardando se o usuário quer prosseguir ou não, mas está acontecendo que quando abre a primeira vez (somente na primeira) ele não mostra os JLabel somente os JButton, aí depois nas outras vezes mostra certo, sabe me dizer o que seria? E olha que eu tenho mais dois JDialogs que também abro no disparar de algum destes eventos, inclusive o que abre antes deste que dá o problema, mas em nenhum acontece isto …

S
Solucao aceita

Cria um novo tópico, explicando esse novo problema e posta os fontes.
:slight_smile:

B

Obrigada staroski. :call_me_hand:

Criado 7 de maio de 2018
Ultima resposta 15 de ago. de 2018
Respostas 31
Participantes 2