2015-11-10 22 views
8

La classe BasicLabelUI in javax/swing/plaf/base è influenzata da un confirmed bug. Nella mia domanda I ho bisogno della funzionalità fornita dallo fixed version (filed for v9). A causa di motivi sia legali che tecnici, sono ancora legato alla versione JDK interessata.Sostituire una classe all'interno della libreria di classi Java con una versione personalizzata

Il mio approccio era quello di creare un pacchetto javax/swing/plaf/basic all'interno del mio progetto, contenente la versione fissa.

Come posso forzare il mio progetto a favorire la versione inclusa della classe sulla classe difettosa nel JDK installato?

Questo deve essere un po 'portatile in quanto la classe fissa deve anche funzionare sul lato cliente e la classe difettosa nell'installazione JDK deve essere ignorata. Pertanto, non voglio modificare il JDK, ma piuttosto ignorare questa particolare classe.

+0

Stranamente, non riesco a riprodurre questo errore in Java 1.7.0_75 o 1.8.0_65, in Windows 7, utilizzando il codice dall'invio del bug. Ho provato a modificarlo per usare il look-and-feel di default; Ho provato ad aggiungere l'uso di EventQueue.invokeLater al metodo principale. (Speravo di sperimentare una soluzione alternativa basata su InputMap.) – VGR

+0

Posso riprodurre il comportamento errato utilizzando il codice da http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7172652. Sto usando 1.8.0_45 – bogus

risposta

11

Come accennato da parte delle altre risposte, si potrebbe in teoria, naturalmente, decomprimere rt.jar file del JVM e sostituire il file con una versione bugfixed compatibili.

Eventuali classi della libreria Java di classe come quelli di Swing vengono caricati dalla classe bootstrap loader che guarda le sue classi da questo rt.jar. Generalmente non è possibile aggiungere le classi a questo percorso di classe senza aggiungerle a questo file. C'è un'opzione (non standard) VM

-Xbootclasspath/jarWithPatchedClass.jar:path 

dove si sarebbe anteporre un file jar che include la versione patchata, ma questo non significa necessariamente lavorare su qualsiasi macchina virtuale Java. Inoltre, è illegale distribuire un'applicazione che modifica questo comportamento! Come si afferma nella official documentation:

Non distribuire le applicazioni che utilizzano questa opzione per sostituire una classe in rt.jar perché questo viola il codice binario licenza Java Runtime Environment.

Se invece annesse una classe alla classe bootstrap loader (ciò che è possibile senza utilizzare API non standard utilizzando l'API strumentazione), il runtime sarebbe ancora caricare la classe originale come classe bootstrap loader in questo caso cerca prima il rt.jar. È quindi impossibile "oscurare" la classe rotta senza modificare questo file.

Infine, è sempre illegal to distribute a VM with a patched file, ovvero lo inserisce in un sistema di produzione per un cliente. Il contratto di licenza afferma chiaramente che è necessario

[...] distribuire il [runtime Java] completo e non modificato in pacchi solo come una parte del tuo applet e applicazioni

Modifica della VM che si distribuire è quindi sconsigliato in quanto si potrebbero affrontare conseguenze legali quando questo è mai scoperto.

Naturalmente, è possibile, in teoria, costruire la propria versione della OpenJDK, ma non si può chiamare l'binario Java più quando si distribuisce e presumo che il vostro cliente non avrebbe permesso a questo da quello che lei suggerisce nella vostra risposta. Per esperienza, molti ambienti sicuri calcolano gli hash dei binari prima dell'esecuzione, cosa che proibirebbe entrambi gli approcci di modificare la VM in esecuzione.

La soluzione più semplice per voi sarebbe probabilmente la creazione di un Java agent che si aggiunge al processo VM all'avvio. Alla fine, questo è molto simile all'aggiunta di una libreria come dipendenza percorso classe:

java -javaagent:bugFixAgent.jar -jar myApp.jar 

offerente A Java è in grado di sostituire rappresentazione binaria di una classe quando si avvia l'applicazione e può quindi modificare l'implementazione del passeggino metodo.

Nel tuo caso, un agente sarebbe simile a quanto segue dove è necessario includere il file di classe patch come ressource:

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 
     } 
     } 
    }); 
    } 
} 

Il pacchetto Javadoc java.lang.instrumentation offre una descrizione dettagliata di come costruire e implementare un agente Java. Utilizzando questo approccio, è possibile utilizzare la versione fissa della classe in questione senza interrompere il contratto di licenza.

Per esperienza, gli agenti Java sono un ottimo modo per correggere bug temporanei nelle librerie di terze parti e nella libreria di classi Java senza la necessità di distribuire le modifiche nel codice o di richiedere la distribuzione di una nuova versione per un cliente. Di fatto, questo è un tipico caso d'uso per l'utilizzo di un agente Java.

+0

Se si intende modificare il comportamento della JVM, esiste un modo più semplice. Ma penso che questo stia infrangendo le regole del cliente (credo). –

+0

Non penso che un cliente abbia un problema nello strumentare le classi di un processo che esegue un'applicazione Swing (vale a dire molto probabilmente una VM dedicata). Alla fine, la maggior parte dei clienti vuole una versione priva di bug. Quello che suggerisci è comunque una violazione legale del contratto di licenza Java. –

+0

Non hai letto completamente la mia risposta. Vedi il primo paragrafo dopo la linea. –

0

Come posso forzare il mio progetto a favorire la mia versione inclusa della classe sulla classe difettosa nel JDK installato?

Risposta semplice: non è possibile. Almeno, non mentre obbedisci rigorosamente al vincolo che dovresti usare la versione di Javs interessata.

Supponendo che sia possibile identificare una versione appropriata nei repository di origine OpenJDK, è possibile creare il proprio gusto delle librerie Java con un bug corretto. Tuttavia, quello non sarà Java reale. Certamente, non si qualificherà come "la versione di Java interessata" che si è costretti a utilizzare. (E inoltre, ti stai impegnando in un ciclo infinito per riapplicare la patch a ogni nuova patch release dell'attuale versione di Java ...)

In teoria è anche possibile inserire una versione modificata di alcuni standard Java classe di libreria in un JAR e la antepone al percorso di classe bootstrap della JVM utilizzando l'opzione della riga di comando . Ma ciò equivale a cambiare "anche la versione di Java interessata".

L'utilizzo di un agente Java per utilizzare una versione patch della classe sta infrangendo anche le regole. Ed è più complicato.


Se tu ei tuoi clienti decide che tweaking la JVM è una soluzione accettabile, allora farlo attraverso il percorso di classe di bootstrap è probabilmente l'approccio più semplice e pulito. Ed è SICURAMENTE legale .

Tuttavia, è consigliabile trovare una soluzione alternativa per il bug fino a quando non verrà rilasciata una versione di Java 9 con la correzione.


1 - In realtà, anche l'approccio di build-da-modificato-source è legale, perché la licenza Oracle Binary non si applica a questo. La licenza Binary riguarda la distribuzione di una versione modificata di un binario Oracle. L'altro possibile problema è che si potrebbero violare i termini per l'utilizzo del marchio o dei marchi Java se si distribuisce una versione che è incompatibile con Java "vero" e si chiama la distro "Java". La soluzione è ... non chiamarlo "Java"!

Tuttavia, non seguire semplicemente il mio consiglio.Chiedi a un avvocato. Meglio ancora, non farlo affatto. È inutilmente complicato.

+0

Tu dici che * [d] oinglo usando un agente Java per usare una versione patchata della classe sta infrangendo anche le regole * cosa non è vero. Puoi strumentare qualsiasi classe di bootstrap tu voglia. –

+0

Questa non è strumentazione. Questo sta deliberatamente modificando il comportamento della JVM. A meno che il cliente non sia d'accordo, stai violando * le loro * regole; cioè quello che dice che la versione X di Java deve essere supportata. Perché è cattivo? Supponiamo che mescoleranno il tuo codice con un altro codice che richiede la stessa classe/metodo di comportarsi nel modo standard? –

+0

Una domanda equivalente alla mia è: Se ci sono due classi con nomi identici, stesse interfacce e lo stesso nome di pacchetto - come è possibile impostare quale viene riconosciuto e quale viene ignorato? – bogus