Problematização: Tenho uma Aplicação JAVASE e na minha tela principal tenho uma barra de menus composta por vários JMenuItem. Criei um método que faço a verificação de permissão de acordo o grupo de acesso vinculado no usuario que esta logando.
Ex: JMenuItem “Arquivo” - Liberado Acesso
JMenuItem “Configurações” - Acesso Bloqueado
JMenuItem “sobre” - Liberado Acesso.
e assim por diante.
A questão é que, eu tenho no meu banco de dados o nome do objeto JMenuItem salvo em uma coluna varchar.
O que estou almejando desenvolver:
um método que leia o banco de dados, recupere o nome do objeto JMenuItem, transforme em JMenuItem e atraves da validação de acesso liberado ou bloqueado eu vou habilitar ou desabilitar.
Objeto = recebe um nome obtido atraves do bd.
JMenuItem jmnu = (JMenuItem) Objeto = para transformar em um objeto do tipo JMenuItem
jmnu.setEnabled(true);
ou
jmnu.setEnabled(false);
o problema que quando executa esta logica, obtenho o seguinte erro:
run:
Exception in thread “AWT-EventQueue-0” java.lang.ClassCastException: java.lang.String cannot be cast to javax.swing.JMenuItem
at view.telaPrincipal.permissaoAcesso(telaPrincipal.java:206)
at view.telaPrincipal.(telaPrincipal.java:144)
at view.telaLogin.logar(telaLogin.java:326)
at view.telaLogin.jButtonEntrarActionPerformed(telaLogin.java:513)
at view.telaLogin.access$200(telaLogin.java:36)
at view.telaLogin$3.actionPerformed(telaLogin.java:401)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
at javax.swing.plaf.basic.BasicButtonListener$Actions.actionPerformed(BasicButtonListener.java:303)
at javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1663)
at javax.swing.JComponent.processKeyBinding(JComponent.java:2882)
at javax.swing.JComponent.processKeyBindings(JComponent.java:2929)
at javax.swing.JComponent.processKeyEvent(JComponent.java:2845)
at java.awt.Component.processEvent(Component.java:6310)
at java.awt.Container.processEvent(Container.java:2236)
at java.awt.Component.dispatchEventImpl(Component.java:4889)
at java.awt.Container.dispatchEventImpl(Container.java:2294)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1954)
at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:806)
at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:1074)
at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:945)
at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:771)
at java.awt.Component.dispatchEventImpl(Component.java:4760)
at java.awt.Container.dispatchEventImpl(Container.java:2294)
at java.awt.Window.dispatchEventImpl(Window.java:2746)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:90)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.awt.EventQueue$4.run(EventQueue.java:729)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
CONSTRUÍDO COM SUCESSO (tempo total: 31 segundos)
Espero ter sido claro com a minha exemplificação, caso nao tenha sido, entre em contato para que possa melhor detalhar.
Então, no seu banco de dados você está salvando um objeto String que contém apenas o texto do JMenuItem.
Um objeto String nunca vai ter uma referência para um objeto do tipo JMenuItem, por isso não faz nem sentido tentar fazer o cast.
Você precisa instanciar um novo JMenuItem e setar o texto dele com o texto que você leu do banco.
Mas como você vai tratar a seleção desse JMenuItem?
Você tem algum mapeamento de opções de menu e respectivas ações e/ou classes a serem chamadas?
Eles são criados na inicialização do Form e cada JMenuItem possui o seu ActionPerformed para abrir os respectivos JInternalFrame.
Eu até tive o mesmo raciocinio que voce, mas se eu instanciar um novo objeto do tipo JMenuItem, eu ficarei com 2 objetos para fazer a mesma coisa (01 que foi criado na inicialização e outro que acabara de ser criado e atribuido o texto recuperado do banco de dados).
Para resolver este problema entendo que precisaria modificar a questão dos JMenuItem que são criados na inicialização, NÃO criar e carregar os JMenuItem e sim instanciar e vincular o actionPerformed no caso NESTE método em questão.
O motivo de ter aberto este tópico foi para verificar a possibilidade de alguem compartilhar uma ideia que pudesse talvez contornar esta situação sem a necessidade de fazer essa referida manutenção na inicialização do form, embora eu ainda sim acredite que no fim das contas a saida será somente esta.
vc serializou o jmenuitem para o banco? se sim como vc fez isso?
Eu usaria um aproach diferente.
Faça um map com os menu itens e no banco coloque a lista com esses índices do map sendo as permissões.
Caso contenha o índice no banco vc pega do map o menu correspondente.
Não, não serializei, na vdd este projeto é antigo e estou acertando para que fique otimizado. Basicamente tenho uma tela de cadastro de grupo de acessos e nessa tela eu cadastro o nome de todos os JMenuItem e salvo em banco. Quando o usuario vai logar, eu pego o grupo de acesso que esta vinculado com o usuario e verifico suas permissões 1 a 1, passo por todos os JMenuItem fazendo esta validação, entao vc já deve imaginar o codigo medonho e repetitivo, por isso que quero transformar em objeto essa validação.
Você pode sim montar suas telas dinamicamente.
Pra isso você terá de utilizar reflection.
Sugiro que além de guardar o texto do JMenuItem, você também guarde o nome da classe do JInternalFrame a ser instanciado.
Abaixo um exemplo hipotético:
Primeiro uma classe pra representar os registros da sua tabela que contém o nome do menu e a classe da tela a ser aberta:
public class OpcaoMenu {
public final String nome;
public final String classe;
public OpcaoMenu(String nome, String classe) {
this.nome = nome;
this.classe = classe;
}
}
Agora um programinha hipotético que monta o menu a partir da leitura de uma tabela que possui o texto do JMenuItem e a classe do JInternalFrame a ser apresentado:
import java.awt.Dimension;
import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.UIManager;
public class TelaPrincipal extends JFrame {
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
TelaPrincipal telaPrincipal = new TelaPrincipal();
telaPrincipal.setDefaultCloseOperation(EXIT_ON_CLOSE);
telaPrincipal.setLocationRelativeTo(null);
telaPrincipal.setVisible(true);
} catch (Throwable t) {
t.printStackTrace();
}
}
private JMenu menu;
private JDesktopPane desktopPane;
TelaPrincipal() {
super("Tela Principal");
setMinimumSize(new Dimension(640, 480));
menu = new JMenu("Meu Menu");
desktopPane = new JDesktopPane();
setContentPane(desktopPane);
JMenuBar menuBar = new JMenuBar();
menuBar.add(menu);
setJMenuBar(menuBar);
List<OpcaoMenu> opcoesMenu = lerMenusDoBanco();
montarMenus(opcoesMenu);
}
private void montarMenus(List<OpcaoMenu> opcoes) {
for (OpcaoMenu opcao : opcoes) {
JMenuItem item = new JMenuItem(opcao.nome);
item.addActionListener(event -> exibeTela(opcao.classe));
menu.add(item);
}
}
private void exibeTela(String nomeClasse) {
try {
Class<JInternalFrame> classe = (Class<JInternalFrame>) Class.forName(nomeClasse);
Constructor<JInternalFrame> construtor = classe.getDeclaredConstructor();
JInternalFrame internalFrame = construtor.newInstance();
internalFrame.setVisible(true);
desktopPane.add(internalFrame);
internalFrame.setSelected(true);
} catch (Exception e) {
e.printStackTrace();
}
}
private List<OpcaoMenu> lerMenusDoBanco() {
List<OpcaoMenu> opcoes = new ArrayList<>();
try {
Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement("select nome, classe from TabelaMenu");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
String nome = rs.getString(0);
String classe = rs.getString(1);
OpcaoMenu menu = new OpcaoMenu(nome, classe);
opcoes.add(menu);
}
} catch (SQLException e) {
e.printStackTrace();
}
return opcoes;
}
private Connection getConnection() {
Connection conn = null;
// Aqui você retorna sua conexão com o banco de dados
return conn;
}
}
Realmente eu não conhecia “reflection”, vou buscar conhecimento sobre, adaptar o codigo exemplo que voce disponibilizou e em breve retorno aqui para postar os resultados.
Desde já agradeço a todos pela disponibilidade e atenção.
Reflexão computacional não é um assunto trivial.
Mas é bastante utilizado em automação. Quando você tem frameworks que fazem um monte de coisa automagicamente através de arquivos de configuração ou anotações, por baixo dos panos eles utilizam reflexão computacional. Por exemplo quando você define Servlets em um arquivo web.xml.
Antigamente eu trabalhei no desenvolvimento de um sistema onde toda sua interface de usuário era customizada para cada cliente através de arquivos XML. O sistema lia esse XML e daí sabia quais classes deveria instanciar para montar as telas. Dá um trabalho no início, mas fica bastante dinâmico.
Recentemente fui protagonista em um desenvolvimento desta natureza, o sistema simplesmente ia “se construindo” conforme ia executando e de fato a estrutura inicial deu muito trabalho pro desenvolvedor fazer, no entanto, quando esta tudo Ok, a facilidade e praticidade eram surpreendentes
Vamos lá, o que você quer dizer com “nome da referência”?
Referências não têm nome, classes e atributos possuem identificadores, que acabamos no dia a dia chamando de nomes.
Se você quer instanciar classes a partir de String, sim, é possível, é isso que é feito no exemplo que postei pro nosso colega.
É possível obter a referência de um atributo de um objeto através de uma String, sim, é possível, tudo isso faz parte do conceito de reflexão.
A dúvida que me bateu era algo mais ou menos assim:
Posso, por exemplo, criar uma variável cujo nome será inputado pelo usuário? Ou seja, deixar um usuário ou alguém escolher o nome/identificador da variável?
Sei que isso não tem uso prático, é apenas questão de conhecer as possibilidades.
Em que momento seria importante o usuário informar o nome de uma variável, sendo que ele nem enxerga o código fonte?
Compilar o código fonte e executar o bytecode gerado são momentos diferentes.
O identificador da variável é utilizado pelo compilador em tempo de compilação para gerar o bytecode.
Depois de gerado, o bytecode permanece estático, não é alterado pelo usuário.