I want to create a client in Java that connects to a web service which requires Digest authentication. Since I am not familiar with java and java stack, I've made a research and came across jax-ws, axis2, xcf, and metro. I have learned JAX-WS is an API and there is a reference implementation in the JDK but it lacks the digest authorization support.

My first attempt was to use axis2 since there is a built-in support for it in the Eclipse IDE. The following code seems to follow digest authentication workflow but somehow it still fails the authorization in the end.

Service1Stub stub = new Service1Stub();

HttpTransportProperties.Authenticator authenticator = new Authenticator();
List<String> authSchemes = new ArrayList<String>();
authSchemes.add(Authenticator.DIGEST);
authenticator.setAuthSchemes(authSchemes);
authenticator.setUsername("doman user");
authenticator.setPassword("domain password");
authenticator.setPreemptiveAuthentication(true);
Options options = stub._getServiceClient().getOptions();
options.setProperty(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, authenticator);
options.setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, org.apache.axis2.Constants.VALUE_FALSE);

GetData getData = new GetData();
getData.setValue(25);
GetDataResponse data = stub.getData(getData);
System.out.println(data.getGetDataResult());

My second attempt was to use metro framework but I get some errors related to JAXB versions.

java.lang.LinkageError: JAXB 2.1 API is being loaded from the bootstrap classloader, but this RI needs 2.2 API.

I have to use JDK 1.6.0_03 so I guess this is happening because of a JDK version mismatch, but I also don't want to use the suggested "endorsed directory mechanism" because it might cause lots of troubles during deployment.

I am totally lost and I am looking for the simplest, quickest and up-to-date way of consuming a web service that requires Digest authentication in Java? Preferably with as little as dependencies possible.

有帮助吗?

解决方案 2

Metro framework was way too complicated to configure and documents that I have found were incomplete. So I've done it with using Apache Axis2.

Steps to follow:

  • Download and extract Apache Axis2 binaries.
  • Reference to all jar files
  • Go to /bin folder and use wsdl2java to generate client code.

wsdl2java -S src -uri "wsdl_file_location"

Copy everything under the src folder to your java application and connect to the service as follows:

//Fictious is the name of the web service
FictiousStub stub = new FictiousStub("servicelocation/fictiousService.php");

HttpTransportProperties.Authenticator authenticator = new Authenticator();
List<String> authSchemes = new ArrayList<String>();
authSchemes.add(Authenticator.DIGEST);
authenticator.setAuthSchemes(authSchemes);
authenticator.setUsername("admin");
authenticator.setPassword("12345");
authenticator.setPreemptiveAuthentication(true);
Options options = stub._getServiceClient().getOptions();
options.setProperty(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, authenticator);
options.setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, org.apache.axis2.Constants.VALUE_FALSE);

其他提示

Java classloading is a mess, sorry. The root cause is that there are no strong names like there are in the .NET world, and therefore the runtime linker takes whatever match comes first on the classpath, regardless of whether that's the library version that the code was compiled against. The OSGi system solves this problem, but it never gained mainstream adoption.

The error message you cited:

java.lang.LinkageError: JAXB 2.1 API is being loaded from the bootstrap classloader, but this RI needs 2.2 API.

is uncharacteristically useful and specific, most of the time what happens instead is that you're left staring at a NoSuchMethodError or something of the sort. Over time, you learn to recognise these as library version mismatches. In this case, the library author(s) have written code to recognize a common error case and print a better error message (bless 'em).

Rant over, here's some info that I hope will set you on the right track:

  • Java classloaders are hierarchical and resolution is bottom-up
  • but there's a blessed class loader at the root of the hierarchy that's responsible for loading the core runtime library
  • vendor shenanigans have led to a lot of stuff getting included in the core runtime library that really shouldn't have been in there, as it's a shortcut to becoming dominant in an otherwise Darwinistic selection process. JAXB is one of these things, as you've just found out. JAXB2 is actually pretty decent, but it evolves independent of the core runtime and, well, here we are.
  • the JDK and JRE installation has a folder called lib\endorsed where you can add JAR files that need to be loaded by the root loader, bypassing even what's in rt.jar.

In summary, if you manually add the 2.2 version of the JAXB library to %JAVA_HOME%\lib\endorsed, then it should override the included 2.1 version and your web service client will deploy. This will have to happen on every system that will run the web service client, until the JDK is updated to a 7.x version that does included JAXB 2.2. If the same JVM is running other JAXB based applications, these may or may not break as a result.

Yes, this is painful. A tangent that you could investigate is to deliberately use an older version of Metro that's built for JAXB 2.1. So long as you're bound to deploying on 1.6.0_03, this may well be the better option, despite losing some of the recent improvements in Metro.

Updated: here's a blog post on this topic. It contains some links to further information.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top