tipos - Reemplace una clase dentro de la biblioteca de clases Java con una versión personalizada
que es una clase en java (2)
La clase BasicLabelUI en javax / swing / plaf / basic se ve afectada por un error confirmado . En mi aplicación, necesito la funcionalidad proporcionada por la versión fija (archivada para v9) . Debido a razones tanto legales como técnicas, todavía estoy obligado a la versión JDK afectada.
Mi enfoque fue crear un paquete javax / swing / plaf / basic dentro de mi proyecto, que contiene la versión fija.
¿Cómo puedo forzar mi proyecto para que favorezca mi versión incluida de la clase sobre la clase defectuosa en el JDK instalado?
Esto tiene que ser algo portátil, ya que la clase fija también tiene que estar trabajando en el lado del cliente y la clase defectuosa en la instalación de JDK debe ignorarse. Por lo tanto, no quiero modificar el JDK, sino pasar por alto esta clase en particular.
¿Cómo puedo forzar mi proyecto para que favorezca mi versión incluida de la clase sobre la clase defectuosa en el JDK instalado?
Respuesta simple: no puedes. Al menos, no mientras estrictamente obedezca la restricción de que debe usar la versión de Javs afectada.
Suponiendo que pueda identificar una versión adecuada en los repositorios de origen de OpenJDK, es posible crear su propia versión de las bibliotecas de Java con un parche de errores. Sin embargo, eso no será Java real. Ciertamente, no calificará como "la versión de Java afectada" que está obligado a usar. (Y además, te estás comprometiendo a un ciclo interminable de volver a aplicar tu parche a cada nueva versión de parche de la versión actual de Java ...)
En teoría, también es posible colocar una versión modificada de alguna clase de biblioteca estándar de Java en un JAR y añadirla a la ruta de clases de arranque de la JVM utilizando la opción de línea de comando -Xbootclasspath
. Pero eso también equivale a cambiar "la versión de Java afectada".
Hacerlo usando un agente Java para usar una versión parcheada de la clase también está rompiendo las reglas. Y es más complicado.
Si usted y sus clientes deciden que ajustar la JVM es una solución aceptable, entonces hacerlo a través de la ruta de clases de arranque es probablemente el enfoque más simple y limpio. Y es DEFINITIVAMENTE legal 1 .
Sin embargo, te recomiendo que encuentres una solución al error hasta que se lance una versión de Java 9 con tu corrección.
1 - En realidad, incluso el enfoque de compilación a partir de una fuente modificada es legal, porque la licencia de Oracle Binary no se aplica a eso. La licencia binaria consiste en distribuir una versión modificada de un binario de Oracle. El otro problema posible es que puede estar violando los términos de uso de las marcas registradas de Java si distribuye una versión que es incompatible con Java "verdadera" y llama a su distro "Java". La solución a eso es ... ¡no lo llames "Java"!
Sin embargo, no solo sigas mi consejo. Pregúntele a un abogado. Mejor aún, no lo hagas en absoluto. Es innecesariamente complicado.
Como se mencionó en las otras respuestas, en teoría podría descomprimir el archivo rt.jar de su JVM y reemplazar el archivo con una versión de corrección de errores compatible.
Cualquier clase de la biblioteca de clases de Java, como las de Swing, se carga con el cargador de clases bootstrap que busca sus clases en este rt.jar . Por lo general, no puede añadir clases a esta ruta de clases sin agregarlas a este archivo. Hay una opción de VM (no estándar)
-Xbootclasspath/jarWithPatchedClass.jar:path
donde debería anteponer un archivo jar que incluye la versión parcheada, pero esto no necesariamente funciona en ninguna máquina virtual Java. Además, es ilegal implementar una aplicación que cambie este comportamiento. Como se indica en la documentación oficial :
No implemente aplicaciones que usen esta opción para anular una clase en rt.jar porque esto viola la licencia del código binario de Java Runtime Environment.
Sin embargo, si agregó una clase al cargador de clases de arranque (lo que es posible sin usar API no estándar mediante el uso de la API de instrumentación), el tiempo de ejecución aún cargará la clase original ya que el cargador de clases de arranque en este caso busca primero el archivo rt.jar . Por lo tanto, es imposible "sombrear" la clase rota sin modificar este archivo.
Finalmente, siempre es ilegal distribuir una máquina virtual con un archivo parcheado , es decir, ponerlo en un sistema de producción para un cliente. El acuerdo de licencia establece claramente que usted necesita
[...] distribuya el [tiempo de ejecución de Java] completo y sin modificar y solo incluido como parte de sus applets y aplicaciones
Por lo tanto, no se recomienda cambiar la VM que distribuye, ya que podría enfrentar consecuencias legales cuando esto se descubra.
Por supuesto, en teoría puede crear su propia versión de OpenJDK pero ya no puede llamar al Java binario cuando lo distribuye y asumo que su cliente no lo permitiría por lo que sugiere en su respuesta. Por experiencia, muchos entornos seguros computan hashes de binarios antes de la ejecución, lo que prohibiría ambos enfoques de ajustar la VM en ejecución.
La solución más fácil para usted probablemente sería la creación de un agente Java que usted agregue a su proceso de VM en el inicio. Al final, esto es muy similar a agregar una biblioteca como una dependencia de ruta de clase:
java -javaagent:bugFixAgent.jar -jar myApp.jar
Un agente Java es capaz de reemplazar la representación binaria de una clase cuando se inicia la aplicación y, por lo tanto, puede cambiar la implementación del método con errores.
En su caso, un agente tendría un aspecto similar al siguiente, donde debe incluir el archivo de clase parcheado como un recurso:
public static class BugFixAgent {
public static void premain(String args, Instrumentation inst) {
inst.addClassFileTransformer(new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) {
if (className.equals("javax/swing/plaf/basic/BasicLabelUI")) {
return patchedClassFile; // as found in the repository
// Consider removing the transformer for future class loading
} else {
return null; // skips instrumentation for other classes
}
}
});
}
}
El paquete javadoc java.lang.instrumentation
ofrece una descripción detallada de cómo construir e implementar un agente Java. Usando este enfoque, puede usar la versión fija de la clase en cuestión sin romper el acuerdo de licencia .
Por experiencia, los agentes de Java son una excelente manera de corregir errores temporales en bibliotecas de terceros y en la Biblioteca de clases de Java sin necesidad de implementar cambios en su código o incluso de implementar una nueva versión para un cliente. De hecho, este es un caso de uso típico para usar un agente de Java.