J'ai un étrange problème.
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)
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
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
.
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
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();
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!
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