¿Es posible "agregar" dinámicamente a classpath en java?
urlclassloader (7)
java -classpath ../classes;../jar;. parserTester
¿Cómo puedo obtener la funcionalidad en el comando de arriba programáticamente? Al igual que, ¿es posible ejecutar como:
java parserTester
y obtener el mismo resultado? Intenté usar URLClassLoader pero modifica el classpath y no lo agrega.
Gracias!
Gracias por la respuesta Milhous. Pero eso es lo que estoy tratando de hacer ... ¿Cómo es posible poner el jar en el classpath primero? Traté de usar un cargador de clases personalizado también :(
Eso funciona ... Pero siento que necesito ejecutarlo solo como: java parserTester Me gustaría saber si tal cosa es posible ???
Necesita ser tan bcoz tengo parserTester.java y .class en una carpeta separada. Necesito retener la estructura del archivo. The parserTester hace uso de un jar en una carpeta jar separada.
¿Entendí bien? ¿La única razón por la que lo tiene es que desea iniciar su clase sin especificar el classpath y cargarlo en tiempo de ejecución? ...
java parserTester
en lugar de
java -classpath ../classes;../jar ;. parserTester
Probablemente no entendí tu razón. Pero si "eso es" lo que quieres puedes hacer lo siguiente (aunque no tiene mucho sentido para mí)
- Lanzar la clase
- Desde el método principal lauch otra clase y programáticamente establece el classpath allí.
- Fin de la historia
Algo así como el siguiente "java -pseudo code"
public static void main( String [] args ) {
String classpath = "classes;../jar";
Runtime.getRuntime().execute("java + classpath + " parserTester ");
}
Por favor, dime si lo hago bien. Si quieres hacer otra cosa, me gustaría ayudar.
Creo que lo que quiere es un "Contenedor de ejecución" o un "Iniciador" específico de la plataforma ... generalmente este componente se usa para detectar su sistema operativo y arquitectura y dependencias, y luego realiza ajustes antes de iniciar su aplicación. Es un patrón de diseño de la vieja escuela (hablando de los años 80 y anteriores) pero todavía se usa mucho hoy en día. La idea es que el programa puede ser independiente del sistema y del entorno y el iniciador hará los preparativos y le dirá al software todo lo que necesita saber. Muchos programas modernos de código abierto lo hacen con scripts de Shell y archivos por lotes, etc. Apache Tomcat, por ejemplo. También podría hacer el envoltorio en Java y lanzar el software con un ejecutable de línea de comando (asegúrese de agregar "&" al final de su comando exec en * NIX para que su envoltura pueda salir dejando solo el software en ejecución. .. también te permite cerrar la ventana del shell sin matar el proceso)
Excelente publicación buena, en mi caso hice esto para que funcionara bien (nota: específica de Windows):
set classpath=%classpath%;../lib/*
java -cp %classpath% com.test.MyClass
Podría implementar su propio cargador de clases , pero esa clase / jar debe estar en el classpath para que se ejecute.
tratar
java -cp *.jar:. myClass
o
export CLASSPATH=./lib/tool.jar:.
java myClass
o
java -jar file.jar
Puede escribir un archivo de proceso por lotes o un archivo de comandos de shell para exportar el classpath y ejecutar el programa java. En Windows,
establecer classpath =% classpath%; ../ classes; ../ jars / * java ParserTester
En Unix, export classpath =% classpath%: ../ classes: ../ jars / * java ParserTester
Si nombra el nombre del archivo como parser.bat o parser.sh, puede ejecutarlo llamando al analizador en el sistema operativo respectivo.
Desde java 1.6, puede incluir todos los archivos jar en un directorio en el classpath simplemente diciendo / *
Si intenta generar dinámicamente un archivo java, compilarlo y agregarlo a la vía de acceso de clases, establezca de antemano el directorio en el que se generará el archivo de clase en el classpath. Debería cargar la clase. Si está modificando la clase java ya generada, básicamente volviendo a compilar después de la modificación y si desea cargar la nueva clase, necesita usar su cargador de clases personalizado para evitar el almacenamiento en caché de la clase.
Puede usar java.net.URLClassLoader para cargar clases con cualquier lista definida por programa de URL que desee:
La clase pública URLClassLoader amplía SecureClassLoader
Este cargador de clases se utiliza para cargar clases y recursos desde una ruta de búsqueda de URL que hace referencia a ambos archivos y directorios JAR. Cualquier URL que termine con un ''/'' se supone que se refiere a un directorio. De lo contrario, se supone que la URL hace referencia a un archivo JAR que se abrirá según sea necesario.
El AccessControlContext del subproceso que creó la instancia de URLClassLoader se utilizará al cargar clases y recursos posteriormente.
Las clases que se cargan tienen permiso otorgado de forma predeterminada solo para acceder a las URL especificadas cuando se creó el URLClassLoader.
Desde: 1.2
Y un poco de trabajo de pies elegante puede ampliarlo para admitir el uso de nombres de rutas wildcarded para recoger directorios completos de JAR (este código tiene algunas referencias a los métodos de utilidad, pero su implementación debería ser obvia en el contexto):
/**
* Add classPath to this loader''s classpath.
* <p>
* The classpath may contain elements that include a generic file base name. A generic basename
* is a filename without the extension that may begin and/or end with an asterisk. Use of the
* asterisk denotes a partial match. Any files with an extension of ".jar" whose base name match
* the specified basename will be added to this class loaders classpath. The case of the filename is ignored.
* For example "/somedir/*abc" means all files in somedir that end with "abc.jar", "/somedir/abc*"
* means all files that start with "abc" and end with ".jar", and "/somedir/*abc*" means all files
* that contain "abc" and end with ".jar".
*
*/
public void addClassPath(String cp) {
String seps=File.pathSeparator; // separators
if(!File.pathSeparator.equals(";")) { seps+=";"; } // want to accept both system separator and '';''
for(StringTokenizer st=new StringTokenizer(cp,seps,false); st.hasMoreTokens(); ) {
String pe=st.nextToken();
File fe;
String bn=null;
if(pe.length()==0) { continue; }
fe=new File(pe);
if(fe.getName().indexOf(''*'')!=-1) {
bn=fe.getName();
fe=fe.getParentFile();
}
if(!fe.isAbsolute() && pe.charAt(0)!=''/'' && pe.charAt(0)!=''//') { fe=new File(rootPath,fe.getPath()); }
try { fe=fe.getCanonicalFile(); }
catch(IOException thr) {
log.diagln("Skipping non-existent classpath element ''"+fe+"'' ("+thr+").");
continue;
}
if(!GenUtil.isBlank(bn)) {
fe=new File(fe,bn);
}
if(classPathElements.contains(fe.getPath())) {
log.diagln("Skipping duplicate classpath element ''"+fe+"''.");
continue;
}
else {
classPathElements.add(fe.getPath());
}
if(!GenUtil.isBlank(bn)) {
addJars(fe.getParentFile(),bn);
}
else if(!fe.exists()) { // s/never be due getCanonicalFile() above
log.diagln("Could not find classpath element ''"+fe+"''");
}
else if(fe.isDirectory()) {
addURL(createUrl(fe));
}
else if(fe.getName().toLowerCase().endsWith(".zip") || fe.getName().toLowerCase().endsWith(".jar")) {
addURL(createUrl(fe));
}
else {
log.diagln("ClassPath element ''"+fe+"'' is not an existing directory and is not a file ending with ''.zip'' or ''.jar''");
}
}
log.diagln("Class loader is using classpath: /""+classPath+"/".");
}
/**
* Adds a set of JAR files using a generic base name to this loader''s classpath. See @link:addClassPath(String) for
* details of the generic base name.
*/
public void addJars(File dir, String nam) {
String[] jars; // matching jar files
if(nam.endsWith(".jar")) { nam=nam.substring(0,(nam.length()-4)); }
if(!dir.exists()) {
log.diagln("Could not find directory for Class Path element ''"+dir+File.separator+nam+".jar''");
return;
}
if(!dir.canRead()) {
log.error("Could not read directory for Class Path element ''"+dir+File.separator+nam+".jar''");
return;
}
FileSelector fs=new FileSelector(true).add("BaseName","EG",nam,true).add("Name","EW",".jar",true);
if((jars=dir.list(fs))==null) {
log.error("Error accessing directory for Class Path element ''"+dir+File.separator+nam+".jar''");
}
else if(jars.length==0) {
log.diagln("No JAR files match specification ''"+new File(dir,nam)+".jar''");
}
else {
log.diagln("Adding files matching specification ''"+dir+File.separator+nam+".jar''");
Arrays.sort(jars,String.CASE_INSENSITIVE_ORDER);
for(int xa=0; xa<jars.length; xa++) { addURL(createUrl(new File(dir,jars[xa]))); }
}
}
private URL createUrl(File fe) {
try {
URL url=fe.toURI().toURL();
log.diagln("Added URL: ''"+url.toString()+"''");
if(classPath.length()>0) { classPath+=File.pathSeparator; }
this.classPath+=fe.getPath();
return url;
}
catch(MalformedURLException thr) {
log.diagln("Classpath element ''"+fe+"'' could not be used to create a valid file system URL");
return null;
}
}
Tengo que estar de acuerdo con los otros dos carteles, parece que estás complicando demasiado una clase de prueba. No es tan inusual tener los archivos .java y .class en carpetas separadas, mientras que depende de los archivos jar en un tercero, sin cambiar programáticamente el classpath. Si lo hace porque no quiere tener que escribir classpath en la línea de comando cada vez, sugeriría un script de shell o un archivo por lotes. Mejor aún, un IDE. La pregunta que realmente tengo es ¿por qué estás tratando de administrar la ruta de clases en el código?