Classloader: adicionando classe ao classpath em runtime

Boa tarde, pessoal do GUJ!

Estou fazendo aqui, por questões de praticidade (?!), uma pequena configuração no Windows para que eu possa rodar classes com 2 cliques.

Criei um arquivo bat com o seguinte:

cd C:\Java
java RunClass %1

Então, configurei o Windows para que, quando o usuário der 2 cliques sobre o arquivo *.class, o comando acima seja chamado, passando o path do arquivo para a minha classe RunClass.

Minha classe RunClass está assim:

import java.lang.reflect.Method;
import java.io.File;

public final class RunClass
  {
   public static void main(String[] args)
     {
      String className = (new File(args[0])).getName().replace(".class", "");
      (new RunClass()).runIt(className);
     }

   public void runIt(String className)
     {
      try
        {
         Class classPoint = Class.forName(className);
         Method method = classPoint.getMethod("main", new Class[] { String[].class });
         method.invoke(classPoint.newInstance(), new Object[] { new String[] { } });
        }
      catch(Exception e)
        { e.printStackTrace(); }
     }
  }

O fato de eu estar criando um objeto File e então pegando o nome é que, quando o Windows passa um nome de arquivo para uma aplicação, este nome vai formatado em formato DOS. Exemplo: C:\ARQUIV~1\FOLDER0~1\NOMEDA~1.CLASS
Para solucionar, usei o objeto File pra pegar o nome corretamente.

O Windows está passando o nome corretamente e a aplicação está chamando o método main perfeitamente. O problema (que eu não havia pensando quando tive a idéia de fazer a classe) é que o classpath não estará incluindo a pasta do arquivo *.class que estou tentando executar.

Eu li o tutorial sobre Classloader do GUJ mas não entendi muito bem como eu poderia usar nesse meu caso.

P.S.: é meio estranho que, para carregar uma classe, eu tenha de estar carregando outra primeiro, eu sei. Já pensei em fazer isso com o Runtime, mas preferi fazer assim para praticar um pouco de Reflection. Ah, também estou ciente de que essa minha classe só carregará classes que estejam no pacote-padrão.

Como eu adiciono uma pasta ao classpath em tempo de execução?

Tu não adicionas. Ao menos não no classpath usado pelo classloader que carrega a tua aplicação, esse é predefinido (com o uso da opção -classpath ou -cp ou da variável de ambiente CLASSPATH).

O que tu consegues fazer é a partir da classe que inicia a aplicação, carregar uma outra classe com um classloader customizado. Aí, para esse classloader, tu podes definir o classpath em tempo de execução.

Para criar um classloader com um classpath arbitrário tu podes usar o java.net.URLClassLoader já existente:

URL[] meuClassPath = (...);
ClassLoader customLoader = new URLClassLoader(meuClassPath);

[Dica: para obter URLs a partir de strings que decrevem paths podes usar new File(stringPath).toURL()]

Por default, um classloader tem o classloader da aplicação (systemClassloader) como pai. Assim, o classpath do classloader customizado é tudo o que estiver em ‘meuClassPath’ mais o que já existe no classloader da aplicação.

Para usar esse classloader ao invés do classloader padrão na hora de carregar a classe, usa a versão de Class#forName() que recebe o classloader a ser usado como argumento.

(...)
Class classPoint = Class.forName(className, true, customLoader);
(...)

Valew cara. Eu não entendi completamente o que terei de fazer, mas vou pesquisar mais por Classloaders e seguir as dicas que me destes.

Obrigado!

:shock:
Nossa, é tão complexo que eu tb fiquei sem entender, mas curti muito a ideia de poder configurar meu classpath sem ter que setar nenhuma variavel e sem reiniciar meu micro.

Valeu ! :stuck_out_tongue: