Módulos Plugáveis

7 respostas
tebosoftware

Bom dia pessoal

Eu já vi algumas coisas no fórum, mas não deu certo então vamos à pergunta:

Eu quero desenvolver uma ferramenta onde eu possa adicionar os módulos que o cliente precisa (contas a receber, contas a pagar, etc) e que o mesmo seja automaticamente adicionado ao menu do sistema, ou seja, o módulo menu irá centralizar tudo e a medida que o jar do módulo adicional estiver na pasta, o mesmo seja adicionado ao menu.
Eu pensei em usar o bloco static dentro da classe, mas como ela não é carregada, não é executado.
Tentei também criar um classloader (código abaixo) mas mesmo assim não executou o processo.

Alguém tem alguma ideia?

MenuController.java

package br.com.tebosoftware.gui;

import br.com.tebosoftware.util.Util;
import java.io.File;
import java.io.FileFilter;
import java.io.FileWriter;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import javax.swing.JMenu;
import javax.swing.JMenuItem;

/**
 *
 * @author Shubacca
 * @data 03/09/2012
 */
public class MenuController {

    private static final List<JMenu> menus = Util.createList();

    static {
        JarClassLoader cl = new JarClassLoader();
        File diretorio = new File(Util.getUserDir() + "/lib");
        try {
            File[] arquivos = diretorio.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    return pathname.isFile() && pathname.getName().endsWith(".jar");
                }
            });
            FileWriter writer = new FileWriter(new File("c:/temp/teste.txt"));
            for (File f : arquivos) {
                writer.write(f.getName() + "\n");
                JarFile jar = new JarFile(f.getAbsoluteFile());
                Enumeration entries = jar.entries();
                while (entries.hasMoreElements()) {
                    ZipEntry entry = (ZipEntry) entries.nextElement();
                    String name = entry.getName();
                    if (!entry.isDirectory() && name.endsWith(".class")) {
                        writer.write("  " + name + "\n");
                        if (name.equals("br/com/tebosoftware/Inicializar.class")) {
                            writer.write("Carregando...\n");
                            Class c = cl.loadClass(jar, name);
                            Class.forName(c.getName());
                            writer.write("Carregou...\n");
                        }
                    }
                }
            }
            writer.close();
        } catch (Exception ex) {
            Logger.getLogger(MenuController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void adicionarMenu(JMenu menu) {
        if (!menus.contains(menu)) {
            menus.add(menu);
        }
    }

    public static void adicionarMenu(String menuTree, JMenuItem menu) {
        JMenu _menu = findJMenu(menuTree);
        if (_menu == null) {
            String[] captions = menuTree.split("\\|");
            for (String caption : captions) {
                JMenu _menu2 = new JMenu(caption);
                if (_menu != null) {
                    _menu.add(_menu2);
                } else {
                    adicionarMenu(_menu2);
                }
                _menu = _menu2;
            }
        }
        _menu.add(menu);
    }

    private static JMenu findJMenu(String menuTree) {
        return findJMenu(menuTree.split("\\|"));
    }

    private static JMenu findJMenu(String[] captions) {
        for (JMenu menu : getMenus()) {
            for (String c : captions) {
                if (menu.getText().equals(c)) {
                    if (captions.length > 1) {
                        return findJMenu(menu, Arrays.copyOfRange(captions, 1, captions.length));
                    } else {
                        return menu;
                    }
                }
            }
        }
        return null;
    }

    private static JMenu findJMenu(JMenu menu, String[] captions) {
        for (int i = 0; i < menu.getMenuComponentCount(); i++) {
            if (menu.getMenuComponent(i) instanceof JMenu) {
                JMenu menu2 = (JMenu) menu.getMenuComponent(i);
                for (String c : captions) {
                    if (menu2.getText().equals(c)) {
                        if (captions.length > 1) {
                            return findJMenu(menu2, Arrays.copyOfRange(captions, 1, captions.length));
                        } else {
                            return menu2;
                        }
                    }
                }
            }
        }
        return null;
    }

    public static void removerMenu(JMenu menu) {
        if (menus.contains(menu)) {
            menus.remove(menu);
        }
    }

    public static List<JMenu> getMenus() {
        return menus;
    }
}

7 Respostas

Ataxexe

Recomendo que você use algo como OSGi. O Eclipse usa isso, o JBoss e mais uma pancada de outros.

Uma alternativa mais simples seria criar uma interface comum para os plugins e escanear as classes no classpath pra pegar todas as que implementem a interface. Não é uma solução tão parruda, mas ajuda um bocado. Eu já fiz isso algumas vezes para outros fins e o resultado foi muito bom.

Da forma que você fez não fica dinâmico, você está dependendo de um diretório hard coded, de classes hard coded e você irá ter alguns problemas por conta do classloader customizado.

tebosoftware

No caso eu tinha pensando em criar uma pasta com os módulos, e no caso ai eu estava fazendo um teste com uma classe para verificar se eu conseguia fazer funcionar. Eu esqueci de colocar o código da classe Inicializar

package br.com.tebosoftware;

import br.com.tebosoftware.gui.MenuController;
import javax.swing.JMenuItem;

/**
 *
 * @author Shubacca
 * @data 04/09/2012
 */
public class Inicializar {

    static {
        MenuController.adicionarMenu("Sistema", new JMenuItem("Parametro"));
    }
}

Mas na ideia do interface, como eu faria para pegar as classes que implementam tal interface?

Ataxexe

Aí é uma gambiarra muito louca porque a linguagem não provê nenhum meio pra você escanear classes, cada framework usa uma abordagem diferente pra isso. Você pode dar uma olhada no fonte do JBoss Seam e do VRaptor pra ver como eles fazem isso ou implementar você mesmo um scanner.

Eu mantenho um framework há um bom tempo que tem uma funcionalidade de class scan, mas é bem limitada por se basear em pacotes. Exemplo:

Set<Class> classes = findClasses().assignableTo(InterfacePlugin.class).in("meu.pacote");

Ou você pode usar uma Annotation:

Set<Class> classes = findClasses().annotatedWith(Plugin.class).in("meu.pacote");
tebosoftware

Certo, mas qual é o framework?

Ataxexe

Tá na minha assinatura.

O fonte dele é hospedado agora no github.

tebosoftware

Ok, vou dar uma olhada.

sergiotaborda

O problema é que vc está usando static. Use classes e objetos como deve ser e vc não terá problemas.

Criado 5 de setembro de 2012
Ultima resposta 5 de set. de 2012
Respostas 7
Participantes 3