Talking to VSTS from Java

| 6 Comments

Update (Jan 2006): If you would like to use some of the features of VSTS from Eclipse then you might want to take a look at my new employer, Teamprise. They provide an eclipse plugin to access Team Foundation Source Control from Eclipse running on multiple platforms including Windows, Linux and Mac OS X.

Original Post: I have just managed to get a quick test java client talking to VSTS and retrieving a list of projects running on my Team Foundation Server. I know this is a small step towards the goals of the VSTSEclpse project, but I'm really happy I've got this working and wanted to tell everyone. Thanks to Davanum Srinivas, Dexter Wong, Evgeny Beskrovny and the rest of the folks over at the axis-user mailing list for all their help in getting NTLM authentication with a Windows Web Service working from a Java application. I've been trying to get NTLM authentication working for something I needed to do at work recently, so this came in doubly handy (another example of working on Open Source projects in your own time directly benefiting your employer).

The test client uses Axis 1.2.1, Jakarta Commons HTTPClient 3.0rc3, Jakarta Commons Codec 1.3 running in Java 1.4.2_08 on Windows XP SP2. The Commons stuff is needed to allow you to talk to the VSTS Web Services because they are protected with NTLM authentication. To get NTLM working from java, you need to do the following:-

Download a copy of the WDSL to a local directory. This is to allow you to run WSDL2Java over it to generate a starting point for your stubs. You can point WSDL2Java to a URL, but not if that is protected by NTLM ;-)

Run WDSL2Java pointing at your local WSDL file. For more information on WSDL2Java see the axis user documentation.

Edit the createCall method of your stub to include the credentials you would like to use in the call. Currently I have this hard-coded like:-

   protected org.apache.axis.client.Call createCall() throws java.rmi.RemoteException {
        try {
            org.apache.axis.client.Call _call = super._createCall();
            super.setUsername("MY_DOMAIN\\myuser");
            super.setPassword("mypassword");            
            ...


Obviously, this should be loaded from a configuration property somewhere.

Now, you need to configure Axis to use the Commons HTTPClient class for the HTTP transport. Luckily, the Axis folks have made it very configurable, so all you have to do is include a file called "client-config.wsdd" in the classpath. My client config looks as follows:-

<?xml version="1.0encoding="UTF-8"?>
<deployment name="defaultClientConfig"
 xmlns="http://xml.apache.org/axis/wsdd/"
 xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
  <globalConfiguration>
    <parameter name="disablePrettyXMLvalue="true"/>
  </globalConfiguration>
  <transport name="httppivot="java:org.apache.axis.transport.http.CommonsHTTPSender"/>
  <!-- transport name="httppivot="java:org.apache.axis.transport.http.HTTPSender"/ -->
  <transport name="localpivot="java:org.apache.axis.transport.local.LocalSender"/>
  <transport name="javapivot="java:org.apache.axis.transport.java.JavaSender"/>
</deployment>

The org.apache.axis.transport.http.CommonsHTTPSender class implements the BasicHandler handler interface and has all the code to make axis use the version of the Commons HTTPClient classes that you have in your classpath.

Now all you need to do is write a client class to use your stub to talk to VSTS. Below is my first class which returns a list of the projects available in the team foundation server by talking to the Common Structure Service running on Team Foundation in the bisserver application.

package org.vstseclipse.experimental.martin;

import java.rmi.RemoteException;

import javax.xml.rpc.ServiceException;

import com.microsoft.VisualStudio.ArrayOfProjectInfo;
import com.microsoft.VisualStudio.ProjectInfo;
import com.microsoft.VisualStudio.ServiceLocator;
import com.microsoft.VisualStudio.ServiceSoap;

/**
 * Test client to talk to VSTS from Java.
 * @author mwo38
 */

public class TestClient {

    public static void main(String[] args) {

        ServiceLocator commonStructureServiceLocator = new ServiceLocator();
        
        try {
            ServiceSoap commonStructureService = commonStructureServiceLocator.getServiceSoap();
            
            // Iterate over list of projects in team foundation.
            ArrayOfProjectInfo arrayOfProjectInfo = commonStructureService.listProjects();
            
            for(int i=0; i < arrayOfProjectInfo.getProjectInfo().length; i++)
            {
                ProjectInfo info = arrayOfProjectInfo.getProjectInfo(i);
                System.out.println("Project: " + info.getName());            
            }
            
        } catch (ServiceException e) {
            e.printStackTrace();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        
    }
}

Works a treat!! I have had a couple of weird errors when trying NTLM talking to a Windows 2000 workstation and the socket gets closed for no apparent reason, but works fine if I route the request over a proxy. When talking to a VSTS server (running on Windows Server 2003) from a machine that is in a domain trusted by the domain that the VSTS Team Foundation server is running on by a user that has permissions to the VSTS repository I have no problems what-so-ever.

Now the real work starts...

6 Comments

Thanks for posting this...I have been trying to write a simple web services client for the Work Item tracking service on VSTS using java and axis..and ran into trouble with authentication...this helps a lot

This absolutley fantastic site. Which gives us very useful information.
keep posting

Thank you for your article. It really save me a week of research. We are trying to integrate a .net application into our java software and the only authentication protocol they allow is ntlm.

I followed closely and I get the following:

run:
- ntlm authentication scheme selected
- Credentials cannot be used for NTLM authentication: org.apache.commons.httpclient.UsernamePasswordCredentials
org.apache.commons.httpclient.auth.InvalidCredentialsException: Credentials cannot be used for NTLM authentication: org.apache.commons.httpclient.UsernamePasswordCredentials
at org.apache.commons.httpclient.auth.NTLMScheme.authenticate(NTLMScheme.java:331)
at org.apache.commons.httpclient.HttpMethodDirector.authenticateHost(HttpMethodDirector.java:277)
at org.apache.commons.httpclient.HttpMethodDirector.authenticate(HttpMethodDirector.java:229)
at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:167)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:393)
at org.apache.axis.transport.http.CommonsHTTPSender.invoke(CommonsHTTPSender.java:196)
at org.apache.axis.strategies.InvocationStrategy.visit(InvocationStrategy.java:32)
at org.apache.axis.SimpleChain.doVisiting(SimpleChain.java:118)
at org.apache.axis.SimpleChain.invoke(SimpleChain.java:83)
at org.apache.axis.client.AxisClient.invoke(AxisClient.java:165)
at org.apache.axis.client.Call.invokeEngine(Call.java:2784)
at org.apache.axis.client.Call.invoke(Call.java:2767)
at org.apache.axis.client.Call.invoke(Call.java:2443)
at org.apache.axis.client.Call.invoke(Call.java:2366)
at
org.apache.axis.client.Call.invoke(Call.java:1812)
at org.tempuri.InergopolosSoapStub.storms(InergopolosSoapStub.java:855)
at dbclient.DbClient.doit(DbClient.java:28)
at dbclient.Main.main(Main.java:21)
- Failure authenticating with NTLM @inergopta:80
(401)Unauthorized

@bpawn

I had this problem when I passed in my username in the wrong format

domain\\username is right
username@region threw the exception.

Where can I get the jar file or whatnot to import the VisualStudio classes you imported?

Archives

Creative Commons License
This blog is licensed under a Creative Commons License.