Why does a JAX-WS client access the WSDL at run-time?
Asked Answered
S

2

8

After generating JAX-WS client using the wsimport

wsimport -keep WebService.wsdl

What reason does JAX-WS have to look for the wsdl location at run time?
Is this a bug?

I found this great post: JAX-WS client : what's the correct path to access the local WSDL?

but it doesn't say why do we need the wsdl at runtime

Swam answered 20/11, 2013 at 16:11 Comment(0)
D
7

Is this a bug?

No, this is not a bug, but reasonable from a conceptual point of view.

What reason does JAX-WS have to look for the wsdl location at run time?

At build-time, you generate classes from the WSDL which means you need to know what kind of operations the service supports and what structure the messages have (aka portTypes, types, messages).

At run-time a whole lot of different information comes into play. For instance, the actual address the service runs on might have changed. The bindings become relevant: Should messages be sent in SOAP 1.1 or 1.2 or are both formats ok? Futhermore, all sorts of policies (security, reliable messaging, etc.) might be attached to the service. All these things are dynamic and mostly irrelevant at build time. Ideally, you should be able to point your client at a different service that uses the same structure and it should work out of the box.

I'd like to answer another question I think you might have:

Isn't this a total overhead in cases where there is just one service that never changes?

Yes, it is. In cases where there is single service with a particular WSDL that never ever changes from its build-time state, reloading the WSDL at run-time is unnecessary and a waste of ressources. But JAX-WS would do a terrible job if it wouldn't allow for more complicated scenarios where information such as bindings or policies do change or where there is more than a single endpoint for the service.

Nonetheless, most JAX-WS implementations do allow for some mechanism to store the WSDL locally and not load it for invocations at runtime. In the RI, simply pointing the wsdlLocation in your @WebServiceClient to the file on the classpath should do the trick.

Debouch answered 22/11, 2013 at 14:58 Comment(0)
K
1

I think, maybe you need a code solution. The following solution requires you to store your WSDL file in the resources folder, where is a resources folder of maven kind project.

@Bean
public WeatherWebServiceServiceSoap weatherWebServiceServiceSoap() throws Exception{
    URL wslLocation = generatedWsdlLocation();
    if(log.isDebugEnabled()) {
        log.debug("WSDL Location: " + wslLocation.toString());
    }
    // cover wslLocation with the arg constructor
    WeatherWebServiceService weatherWebServiceService = new WeatherWebServiceService(wslLocation);
    weatherWebServiceService.setHandlerResolver(
        portInfo -> webServiceSOAPHandlerList.stream().map(s -> (Handler)s).collect(Collectors.toList()));

    WeatherWebServiceServiceSoap serviceSoap = weatherWebServiceService.getWeatherWebServiceServiceSoap();
    BindingProvider bindingProvider = ((BindingProvider)serviceSoap);

    bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
        wsdlProperties.getAddressLocation());

    return serviceSoap;
}

private URL generatedWsdlLocation() throws MalformedURLException {
    URL baseUrl = ClassUtil.getLocation(this.getClass());
    // the value of WebServiceConstants.WEATHER_WSDL_RESOURCES_LOCATION should be the relative path of your wsdl in resources(e.g. src/main/resources/wsdl, then the value should be wsdl/).
    return new URL(baseUrl, WebServiceConstants.WEATHER_WSDL_RESOURCES_LOCATION);
}

The ClassUtil allow you to get the classes path in jarFile where you store your wsdl file.

/**
 * ref: https://github.com/scijava/scijava-common/blob/scijava-common-2.62.1/src/main/java/org/scijava/util/ClassUtils.java#L296-L355
 */
@Slf4j
public class ClassUtil {

    /**
     * get the jar classes path where the <code>clazz</codee> belongs to.
     *
     * <p>
     * if in file system(e.g. /path/to/package/TheClass.class) return file directory (e.g. file:/path/to); if in jar (e.g.
     * /path/to/the-jar.jar!/the/package/TheClass), return path in jar( e.g. jar:file:/path/to/the-jar.jar!/BOOT-INF/classes!/)
     * return null when error occured.
     * </p>
     */
    public static URL getLocation(Class<?> clazz) {
        if (clazz == null) {
            // could not load the class
            return null;
        }
        try {
            URL codeSourceLocation = clazz.getProtectionDomain().getCodeSource().getLocation();
            if (codeSourceLocation != null) {
                return codeSourceLocation;
            }
        } catch (Exception e) {
            // SecurityException: Cannot access protection domain.
            // NullPointerException: Protection domain or code source is null.
        }
        final URL classResource = clazz.getResource(clazz.getSimpleName() + ".class");
        if (classResource == null) {
            // cannot find class resource
            return null;
        }
        String url = classResource.toString();
        // java.io.File -> java/io/File.class
        String suffix = clazz.getCanonicalName().replace('.', '/') + ".class";
        if (!url.endsWith(suffix)) {
            if (isDebugEnable()) {
                log.debug("Weired URL: {} should end with {}", url, suffix);
            }
            // weired URL
            return null;
        }

        String classesUrl = url.substring(0, url.length() - suffix.length());
        try {
            return new URL(classesUrl);
        } catch (MalformedURLException e) {
            if (isDebugEnable()) {
                log.debug(e.getMessage(), e);
            }
            return null;
        }
    }

    public static URL getFileLocation(Class<?> clazz) {
        URL url = getLocation(clazz);
        if(url == null) {
            return url;
        }
        String path = url.toString();
        if(path.startsWith("jar:")) {
            // remove "jar:" prefix and "!/" suffix
            path = path.substring(4, path.length() - 2);
        }
        try {
            return new URL(path);
        } catch (MalformedURLException e) {
            if (isDebugEnable()) {
                log.debug(e.getMessage(), e);
            }
            return null;
        }
    }

    private static boolean isDebugEnable() {
        return log.isDebugEnabled();
    }
}

some references:

Kiri answered 10/4, 2020 at 8:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.