2015-09-22 15 views
6

Ho un client di servizio Web JaxWS che è stato eseguito con successo in Java 6 per anni. Ora, quando Java è stato aggiornato alla versione 8, stiamo ottenendo NullPointerException quando ottiene portoJaxWS client webservice per Java 6 non funziona in Java 8

java.lang.NullPointerException 
    at com.sun.xml.internal.ws.client.ClientContainer$1.getResource(Unknown Source) 
    at com.sun.xml.internal.ws.assembler.MetroConfigLoader.locateResource(Unknown Source) 
    at com.sun.xml.internal.ws.assembler.MetroConfigLoader.locateResource(Unknown Source) 
    at com.sun.xml.internal.ws.assembler.MetroConfigLoader.init(Unknown Source) 
    at com.sun.xml.internal.ws.assembler.MetroConfigLoader.<init>(Unknown Source) 
    at com.sun.xml.internal.ws.assembler.TubelineAssemblyController.getTubeCreators(Unknown Source) 
    at com.sun.xml.internal.ws.assembler.MetroTubelineAssembler.createClient(Unknown Source) 
    at com.sun.xml.internal.ws.client.Stub.createPipeline(Unknown Source) 
    at com.sun.xml.internal.ws.client.Stub.<init>(Unknown Source) 
    at com.sun.xml.internal.ws.client.Stub.<init>(Unknown Source) 
    at com.sun.xml.internal.ws.client.Stub.<init>(Unknown Source) 
    at com.sun.xml.internal.ws.client.sei.SEIStub.<init>(Unknown Source) 
    at com.sun.xml.internal.ws.client.WSServiceDelegate.getStubHandler(Unknown Source) 
    at com.sun.xml.internal.ws.client.WSServiceDelegate.createEndpointIFBaseProxy(Unknown Source) 
    at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(Unknown Source) 
    at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(Unknown Source) 
    at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(Unknown Source) 
    at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(Unknown Source) 
    at javax.xml.ws.Service.getPort(Unknown Source) 
    at myclient.stub.MyService.<init>(MyService.java:38) 

ho provato a fare funzionare con la versione Java 7 1.7.0_80 e là funziona anche, ma molto prima versione di Java 8 causa questo eccezione.

Ho battuto la testa con questo per un po 'di tempo ora, quindi se qualcuno potesse darmi qualche indizio su dove iniziare a sistemarlo sarebbe davvero grandioso.

Ecco il WSDL, l'ho modificato un po 'perché non è il mio servizio ma spero sia abbastanza?

<?xml version="1.0" encoding="utf-8" standalone="no"?> 
<wsdl:definitions xmlns:ns1="http://www.dummyservice/sample/interface" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
     xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Sample" 
     targetNamespace="http://www.dummyservice/sample/interface"> 
    <wsdl:types> 
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" 
    targetNamespace="http://www.dummyservice/sample/interface" xmlns="http://www.dummyservice/sample/interface" 
    elementFormDefault="qualified" jaxb:version="2.0"> 
     <xs:element name="PersonQuery"> 
     <xs:complexType> 
      <xs:sequence> 
      <xs:element name="system" type="xs:string" /> 
      <xs:element name="user" type="xs:string" /> 
      </xs:sequence> 
     </xs:complexType> 
     </xs:element> 
     <xs:element name="PersonReply"> 
     <xs:complexType> 
      <xs:sequence> 
      <xs:element name="Header" type="HeaderType" /> 
      <xs:element name="person" type="PersonType" minOccurs="0" maxOccurs="1" /> 
      <xs:element name="address" type="AddressType" minOccurs="0" maxOccurs="unbounded" /> 
      </xs:sequence> 
     </xs:complexType> 
     </xs:element> 
     <xs:complexType name="HeaderType"> 
     <xs:sequence> 
      <xs:element name="tila" type="StatusType" /> 
     </xs:sequence> 
     </xs:complexType> 
     <xs:simpleType name="StatusType"> 
     <xs:annotation> 
      <xs:appinfo> 
      <jaxb:typesafeEnumClass> 
       <jaxb:typesafeEnumMember name="SUCCESS" value="0001" /> 
       <jaxb:typesafeEnumMember name="FAIL" value="0000" /> 
      </jaxb:typesafeEnumClass> 
      </xs:appinfo> 
     </xs:annotation> 
     <xs:restriction base="xs:string"> 
      <xs:enumeration value="0000" /> 
      <xs:enumeration value="0001" /> 
     </xs:restriction> 
     </xs:simpleType> 
     <xs:complexType name="PersonType"> 
     <xs:sequence> 
      <xs:element name="firstname" type="xs:string" minOccurs="0" /> 
      <xs:element name="lastname" type="xs:string" minOccurs="0" /> 
     </xs:sequence> 
     </xs:complexType> 
     <xs:complexType name="AddressType"> 
     <xs:sequence> 
      <xs:element name="addresstype" type="AddresstypeType" minOccurs="0" /> 
      <xs:element name="streetaddress" type="xs:string" minOccurs="0" /> 
      <xs:element name="city" type="xs:string" minOccurs="0" /> 
      <xs:element name="postalcode" type="xs:string" minOccurs="0" /> 
     </xs:sequence> 
     </xs:complexType> 
     <xs:simpleType name="AddresstypeType"> 
     <xs:annotation> 
      <xs:appinfo> 
      <jaxb:typesafeEnumClass> 
       <jaxb:typesafeEnumMember name="HOME" value="001" /> 
       <jaxb:typesafeEnumMember name="OFFICE" value="002" /> 
      </jaxb:typesafeEnumClass> 
      </xs:appinfo> 
     </xs:annotation> 
     <xs:restriction base="xs:string"> 
      <xs:enumeration value="001" /> 
      <xs:enumeration value="002" /> 
     </xs:restriction> 
     </xs:simpleType> 
    </xs:schema> 
    </wsdl:types> 
    <wsdl:message name="PersonQueryOperationRequest"> 
    <wsdl:part element="ns1:PersonQuery" name="parameters" /> 
    </wsdl:message> 
    <wsdl:message name="PersonQueryOperationResponse"> 
    <wsdl:part element="ns1:PersonReply" name="parameters" /> 
    </wsdl:message> 
    <wsdl:portType name="SamplePort"> 
    <wsdl:operation name="PersonQueryOperation"> 
     <wsdl:input message="ns1:PersonQueryOperationRequest" /> 
     <wsdl:output message="ns1:PersonQueryOperationResponse" /> 
    </wsdl:operation> 
    </wsdl:portType> 
    <wsdl:binding name="SampleSOAP" type="ns1:SamplePort"> 
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" /> 
    <wsdl:operation name="PersonQueryOperation"> 
     <soap:operation soapAction="http://www.dummyservice/sample/interface/SampleOperation" /> 
     <wsdl:input> 
     <soap:body use="literal" /> 
     </wsdl:input> 
     <wsdl:output> 
     <soap:body use="literal" /> 
     </wsdl:output> 
    </wsdl:operation> 
    </wsdl:binding> 
    <wsdl:service name="SampleService"> 
    <wsdl:port binding="ns1:SampleSOAP" name="Sample"> 
     <soap:address location="https://127.0.0.1/data/ws" /> 
    </wsdl:port> 
    </wsdl:service> 
</wsdl:definitions> 

Edit: Classe di carico sembra essere il problema, sia di classe contesto caricatore e di classe class loader sono nulli in quel ClientContainer.

private final ResourceLoader loader = new ResourceLoader() { 
public URL More ...getResource(String resource) throws MalformedURLException { 
     ClassLoader cl = Thread.currentThread().getContextClassLoader(); 
    if (cl == null) { 
     cl = this.getClass().getClassLoader(); 
    } 
    return cl.getResource("META-INF/"+resource); 
} 
}; 

Quando abbiamo deciso esplicitamente la classificazione per sistemi loader di classe del contesto caricatore prima di richiamare ws chiamano iniziato a lavorare. Ma è una buona soluzione per questo? Mi chiedo perché questo ha smesso di funzionare in Java8 e questo potrebbe essere un problema nella loro implementazione ws?

saluti,

Janne

+0

È possibile modificare la domanda per includere il WSDL, in modo che possiamo provare a ricreare il problema? – VGR

+0

Ho incluso WSDL, ho dovuto modificarlo per distinguerlo dall'originale, ma si spera che sia ok. –

+0

Sembra l'unico modo [quel metodo] (http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/com/sun/xml/internal/ws/client/ClientContainer.java # ClientContainer.0loader) può lanciare NullPointerException quando la classe viene caricata dal sistema ClassLoader (poiché [ Java può restituire null per indicare il sistema ClassLoader] (http://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getClassLoader--)). Su quale sistema operativo stai funzionando? Hai provato a utilizzare l'ultima versione di Java? – VGR

risposta

1

provare a generare gli stub per il cliente webservice nuovo in Java8.

Generalmente generiamo un jar di stub e lo inseriamo nel classpath. Dato che Java è compatibile con le versioni precedenti, salta questo passaggio di rigenerazione del jar durante l'aggiornamento a una versione superiore.

Ma dal momento che per Java 6 & 7 funziona e solo in mancanza di Java 8, consiglierei di generare nuovamente gli Stub solo per escludere qualsiasi problema nella compatibilità con le versioni precedenti di Java 8.

+0

Li ho generati con Java 8 ma questo non ha alcun effetto. –

4

Abbiamo riscontrato questo problema (stessa traccia di stack e tutto) dopo l'aggiornamento da Java 6 a Java 8. Pubblicherò prima la nostra soluzione, seguita da ulteriori spiegazioni.

Per noi, le principali condizioni in cui si verifica questo problema è:

  1. Java 8 in esecuzione come JVM incorporato tramite jvm.dll dall'interno di un programma di Windows. Ho provato a riprodurre il problema in esecuzione in una Java JVM standalone e non ho potuto farlo accadere. Esecuzione standalone è stato molto utile per diagnosticare il problema, tuttavia.
  2. Utilizzo del metodo proxy di inizializzazione della connessione a un servizio Web JAX-WS, che crea dinamicamente lo stub sul lato client utilizzando il WSDL ospitato sul lato server.

Soluzione:

Quando si richiama la chiamata a Service.getPort(Class<T>) esso deve essere fatto nel proprio thread, separato dal filo hai avuto la chiamata in esecuzione in precedenza. Ciò che ti consente è un'occasione per impostare ClassLoader su quel thread su ClassLoader che non è il classloader bootstrap, che è il punto cruciale del problema in ClientContainer.java (ulteriori spiegazioni su quello sotto).Ecco alcuni esempi di codice che funzionano per noi. Dovrai modificarlo in base alle tue esigenze.

public class YourClass { 
    private YourWebService yourWebService; 

    // You may want to synchronize on this method depending on your use case 
    public YourWebService getYourWebService() { 
    if (this.yourWebService == null) { 
     // We create a thread so that we can set the ClassLoader 
     Thread t = new Thread() { 
     public void run() { 
      synchronized(this) { 
      // Get the string of your webservice WSDL from somewhere 
      String url = "http://YOURHOST:YOURPORT/your-web-service/YourWebService?wsdl"; 
      URL srvUrl = null; 
      try { 
       srvUrl = new URL(url); 
      } catch (MalformedURLException ex) { 
       throw new RuntimeException(String.format("Malformed URL: %s", url), ex); 
      } 

      QName qName = new QName("your-webservice-namespace", "YourWebServiceName"); 
      Service service = Service.create(srvUrl, qName); 
      this.yourWebService = service.getPort(YourWebService.class); 
      notify(); 
      } 
     } 
     }; 

     // Thread.currentThread().getContextClassloader() 
     // returns null in com.sun.xml.internal.ws.client.ClientContainer. 
     // (See http://hg.openjdk.java.net/jdk8/jdk8/jaxws/file/d03dd22762db/src/share/jaxws_classes/com/sun/xml/internal/ws/client/ClientContainer.java, lines 39-47) 
     // To work around that, I force setting of the ContextClassLoader 
     // on this thread (in which the Service.getPort() method will run) so 
     // that when ClientContainer calls Thread.currentThread().getContextClassLoader(), it doesn't get a null 
     // (i.e., the bootstrap classloader). 
     // 
     t.setContextClassLoader(YourClass.class.getClassLoader()); 
     t.start(); 
     // Wait until above thread completes in order to return yourWebService 
     synchronized(t) { 
     try { 
      t.wait(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     } 
    } 

    return this.yourWebService; 
    } 
} 

Sfondo Ulteriori e Dettagli:

La difficoltà nella diagnosi di questo per noi era che il problema si è verificato solo all'interno di un prodotto Windows, che lancia una JVM incorporato. Senza remote debugging su quella JVM, ci sarebbe voluto molto più tempo per arrivare alla fine del problema. Una volta ho visto che 1) quando ho eseguito lo stesso codice che richiama la chiamata a Service.getPort(Class<T>) all'interno di una JVM autonoma (al di fuori del prodotto Windows) e, 2) che la classe ClientContainer è stata in grado di ottenere il ClassLoader del thread corrente e, 3) che il ClassLoader restituito non era il bootloader ClassLoader (ovvero non null), mi ha fatto capire che dovevo trovare un modo per assicurarmi che il thread su cui era in esecuzione il ClientContainer non potesse ottenere il bootload ClassLoader. L'obiettivo quindi è stato vedere se riuscivo a trovare un modo per modificare ClassLoader risolto dal codice ClientContainer.

ClientContainer fonte: http://hg.openjdk.java.net/jdk8/jdk8/jaxws/file/d03dd22762db/src/share/jaxws_classes/com/sun/xml/internal/ws/client/ClientContainer.java

Avviso nel ClientContainer fonte che ci sono due tentativi di risolvere un classloader. Il problema è che se entrambi questi tentativi restituire il classloader di bootstrap, una NullPointerException provocherà sulla linea 45 dal cl sarà nullo:

cl.getResource("META-INF/"+resource); 

Questa soluzione garantisce che il classloader risolto dal codice ClientContainer sarà classloader che hai messo sul tuo filo.

ho depositato un biglietto per il team di JAX-WS per indagare il problema qui: https://java.net/jira/browse/JAX_WS-1178

1

mi sono imbattuto lo stesso problema dopo l'aggiornamento a Java 8. @ risposta di 2AGuy aiuta molto. Tuttavia, il problema si è verificato sia per Mac che per Linux. E potrebbe esserci una versione più semplice per risolvere il problema. Il codice può essere eseguito sotto la linea prima di chiamare getPort():

Thread.currentThread().setContextClassLoader(XXX.class.getClassLoader());

Questo sostituirà il filo contesto classe Loader da Bootstrap loader delle classi a chi lo si imposta ad essere, senza il sovraccarico di creare un nuovo thread solo per imposta il classLoader.

La causa principale di questo è ancora un mistero per me. Il mio sospetto è che il thread per creare un proxy client del servizio SOAP (un proxy di endpoint del servizio nel contesto della documentazione) sia il thread principale o creato da esso, il cui classLoader è il caricatore della classe bootstrap, caricato nella fase iniziale all'avvio di JVM. Basato su Java doc per Class#getClassLoader(), viene visualizzato un NullPointer.

Alcune implementazioni possono utilizzare null per rappresentare il caricatore di classe bootstrap. Questo metodo restituirà null in tali implementazioni se questa classe è stata caricata dal loader della classe bootstrap.

+1

Dai a quell'uomo un biscotto! –