web-dev-qa-db-fra.com

JAX-WS = Quand Apache CXF est installé, il "vole" l'implémentation JDK JAX-WS par défaut, comment résoudre?

J'ai un étrange problème.

  1. En utilisant wsimport, j'ai généré aussi du code JAX-WS à partir d'un WSDL (dans un Eclipse dédié Java). Cela fonctionne très bien dans JDK6 sans aucune dépendance externe (fonctionnant dans Eclipse)

  2. J'ai un deuxième projet où j'ai déjà utilisé Apache CXF. Si je copie le code décrit en 1.) dans ce projet, soudainement, le JDK n'exécute pas le JAX-WS (fichiers que j'ai générés), mais plutôt Apache CXF.

Comment puis-je empêcher Apache CXF de "faire tourner" le truc JAX-WS. (Le problème est que CXF ne réussit pas à exécuter le code ...). Je ne comprends pas non plus complètement comment Apache CXF découvre ces classes. Je ne les ai pas enregistrés quand même?

Merci beaucoup! Markus

33
Markus

Apache CXF (cxf-rt-frontend-jaxws-*.jar pour être précis) s'enregistre en tant que fournisseur JAX-WS dans la JVM. A l'intérieur du JAR susmentionné, il y a un fichier nommé: /META-INF/services/javax.xml.ws.spi.Provider avec le contenu suivant:

org.Apache.cxf.jaxws.spi.ProviderImpl

Si vous regardez maintenant javax.xml.ws.spi.FactoryFinder#find vous découvrirez que JDK recherche dans CLASSPATH la présence de javax.xml.ws.spi.Provider fichier et revient à l'implémentation Sun par défaut si non disponible. Vous avez donc deux options pour forcer le repli:

  • soit supprimer cxf-rt-frontend-jaxws-*.jar de CLASSPATH

  • ou remplacer javax.xml.ws.spi.Provider fichier fourni par CXF pour pointer vers l'emplacement de secours

La deuxième option est en fait un peu plus facile. Créez simplement:

/src/main/resources/META-INF/services/javax.xml.ws.spi.Provider

(en supposant que vous utilisez Maven) avec le contenu suivant:

org.Apache.cxf.jaxws.spi.ProviderImpl

Voilà, testé avec javax.xml.ws.Endpoint#publish.

59
Tomasz Nurkiewicz

Pour l'implémentation par défaut, mettez:

com.Sun.xml.internal.ws.spi.ProviderImpl

dans /src/main/resources/META-INF/services/javax.xml.ws.spi.Provider

16
Ken Larson

J'ai essayé l'autre et je ne pouvais tout simplement pas le faire fonctionner, donc pour définir CXF s'il n'était pas défini sur CXF, je remplace simplement le délégué à l'intérieur du service.

 try {
        loc = this.getClass().getResource(wsdlResource); 
        QName qName = new QName( wsTargetNamespace, wsName );
        service = new YourWS(loc, qName);
        Field delegateField = Service.class.getDeclaredField("delegate"); //ALLOW CXF SPECIFIC SERVICE DELEGATE ONLY!
        delegateField.setAccessible(true);
        ServiceDelegate previousDelegate = (ServiceDelegate) delegateField.get(service);
        if (!previousDelegate.getClass().getName().contains("cxf")) {
            ServiceDelegate serviceDelegate = ((Provider) Class.forName("org.Apache.cxf.jaxws.spi.ProviderImpl").newInstance())
                .createServiceDelegate(loc, qName, service.getClass());
            log.info("The " + getClass().getSimpleName() + " delegate is changed from " + "[" + previousDelegate + "] to [" +
                serviceDelegate +
                "]");
            delegateField.set(service, serviceDelegate);
        }
        port = service.getYourWSSoap();
7
EpicPandaForce

Les mécanismes de recherche standard ne semblent pas bien fonctionner dans OSGi (*).

Il y a deux façons de travailler pour forcer le service à reprendre l'implémentation CXF de javax.xml.ws.spi.Provider:

  • l'approche de la mise en delegate par réflexion donnée dans la réponse d'EpicPandaForce à cette question ( https://stackoverflow.com/a/31892305/109079 )

  • appeler le niveau inférieur JaxWsProxyFactoryBean; cela semble éviter tous les appels au javax.xml.ws.spi.FactoryFinder inclus avec Java qui est la racine du problème

Voici un exemple de ce dernier, pour les codeurs moins intrépides qui préfèrent ne pas changer les champs privés de manière réfléchie:

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.getClientFactoryBean().getServiceFactory().setWsdlURL(WinRmService.WSDL_LOCATION);
factory.setServiceName(WinRmService.SERVICE);
factory.setEndpointName(WinRmService.WinRmPort);
// factory.setFeatures(...);  // if required

Service winrm = factory.create(WinRm.class);        

Client client = ClientProxy.getClient(winrm);

Quelques notes:

  • Passer un URL comme ci-dessus, plutôt que la simple factory.setWsdlURL(String) peut être nécessaire si le WSDL est une ressource sur le chemin de classe (évitez les URL bundle://... Non résolubles pour les éléments de chemin de classe)

  • Vous pouvez avoir besoin de bundles supplémentaires pour les fonctionnalités (telles que l'adressage)


(*) Quant à savoir pourquoi les mécanismes de recherche ne fonctionnent pas dans la plupart des conteneurs OSGi, consultez ce petit peu désagréable dans le FactoryFinder d'Oracle Java:

private static final String OSGI_SERVICE_LOADER_CLASS_NAME = "com.Sun.org.glassfish.hk2.osgiresourcelocator.ServiceLoader";

private static boolean isOsgi() {
    try {
        Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME);
        return true;
    } catch (ClassNotFoundException ignored) {
    }
    return false;
}

OSGi = Glassfish? Fishy en effet!

3
Partly Cloudy

J'avais un problème similaire. Dans mon cas, j'ai dû utiliser org.Apache.cxf.jaxws.spi.ProviderImpl pour les éléments JAX-WS (création de points de terminaison de service Web, etc.) et com.Sun.xml.internal.ws.spi.ProviderImpl pour publier des points de terminaison sur com.Sun.net.httpserver.HttpsServer.

J'ai réussi à résoudre ce problème en créant mon propre fournisseur qui étend javax.xml.ws.spi.Provider et l'utiliser au lieu de celui par défaut.

package provider;

import Java.net.URL;
import Java.util.List;

import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.ws.Endpoint;
import javax.xml.ws.EndpointReference;
import javax.xml.ws.WebServiceFeature;
import javax.xml.ws.spi.Provider;
import javax.xml.ws.spi.ServiceDelegate;
import javax.xml.ws.wsaddressing.W3CEndpointReference;

import org.w3c.dom.Element;

public class MyProvider extends Provider
{

@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public ServiceDelegate createServiceDelegate(URL wsdlDocumentLocation, QName serviceName, Class serviceClass)
{
    try {
        return ((Provider) Class.forName("org.Apache.cxf.jaxws.spi.ProviderImpl").newInstance()).createServiceDelegate(wsdlDocumentLocation, serviceName, serviceClass.getClass());
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

@Override
public Endpoint createEndpoint(String bindingId, Object implementor)
{
    try {
        return ((Provider) Class.forName("com.Sun.xml.internal.ws.spi.ProviderImpl").newInstance()).createEndpoint(bindingId, implementor);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

@Override
public Endpoint createAndPublishEndpoint(String address, Object implementor)
{
    try {
        return ((Provider) Class.forName("com.Sun.xml.internal.ws.spi.ProviderImpl").newInstance()).createAndPublishEndpoint(address, implementor);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

@Override
public EndpointReference readEndpointReference(Source eprInfoset)
{
    try {
        return ((Provider) Class.forName("org.Apache.cxf.jaxws.spi.ProviderImpl").newInstance()).readEndpointReference(eprInfoset);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

@Override
public <T> T getPort(EndpointReference endpointReference, Class<T> serviceEndpointInterface, WebServiceFeature... features)
{
    try {
        return ((Provider) Class.forName("org.Apache.cxf.jaxws.spi.ProviderImpl").newInstance()).getPort(endpointReference, serviceEndpointInterface, features);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

@Override
public W3CEndpointReference createW3CEndpointReference(String address, QName serviceName, QName portName, List<Element> metadata, String wsdlDocumentLocation, List<Element> referenceParameters)
{
    try {
        return ((Provider) Class.forName("org.Apache.cxf.jaxws.spi.ProviderImpl").newInstance()).createW3CEndpointReference(address, serviceName, portName, metadata, wsdlDocumentLocation,
                referenceParameters);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

}

Créez ensuite simplement:

/src/main/resources/META-INF/services/javax.xml.ws.spi.Provider

(en supposant que vous utilisez Maven) avec le contenu suivant:

package.MyProvider
1
Monika Bozhinova