Consuming SharePoint web services with a Java client

Using CAML for CRUD methods on SharePoint Copy and Lists services

I used to think that a Camel was something to smoke or ride in the desert, but that was before I opened my mouth at work one day and said, "Sure I can automatically save these documents up to SharePoint." SharePoint has been around for a long time, so I assumed that there must be a Java API or possibly some exposed web services that I could consume. Well, as it turns out I did get the job done, and as with many things it ended better than it started. But there were quite a few hurdles along the way, which I'd like to help other JavaWorld readers avoid.

In this I'll show you how to perform basic CRUD operations on a SharePoint document folder from a Java client. The demonstration will focus on some of the more popular methods that any SharePoint web service consumer is likely to invoke, which are found in Microsoft's Copy and Lists services. For the CRUD operations we'll use CAML (Collaborative Application Markup Language), an XML-based language utilized in many of the methods exposed by Copy and Lists. You'll learn how to construct valid CAML structures that are passed as method parameters or assigned to object properties, which are in turn passed as parameters to these services.

Hopefully, this tip will convince you that there are CAMLs that you can use to get business documents from point A to point B, without relying on four legs and a hump.

Demonstration code

My demonstration code is very simple: I've used no open source code other than for logging and my implementation has no dependencies on Java EE technology, so you can run the source code directly from Eclipse in a standard Java application.

Background

I work in a group that performs information management for large volumes of data that eventually is housed in various data-marts (service, export, report, and so on). Consumers, both internal and external to the company, consume the data in order to make business and personal decisions. Various kinds of monitoring take place in this environment, including automated audits and reports that are run against the data housed in data-marts. Audits ensure that data is in a consistent state, both across marts and within the mart where it resides. Audit reports are emailed to various people and then manually saved on SharePoint.

Because the engines used for monitoring have a pluggable output writer concept, it was natural to consider setting up a writer for SharePoint. We already were writing to the database, SMTP servers, and a file system, so this seemed like a logical next step and a way to avoid the manual process.

The trick, of course, was making it all work.

Getting started: Communicating with SharePoint

The sample application for this article demonstrates how to communicate with SharePoint from a Java client. I wrote the application using Eclipse 3.6.2 and Java 1.6.0_32. Figure 1 shows the two main packages contained within the sample application.

Figure 1. Package diagram (click to enlarge)

The first package, com.jw.sharepoint.examples, contains all the custom code for the solution. It uses the code contained in the com.microsoft.sharepoint.webservices package, which was code-generated.

Before diving into how the custom code is structured I'll explain how to generate the Microsoft package. First, recall that we'll be using two web services to invoke service calls: Copy and Lists. You can access these services on the SharePoint site that you are trying to communicate with in the following locations:

  • https://server/site/_vti_bin/Lists.asmx
  • https://server/site/_vti_bin/Copy.asmx

Generating the web services package

To generate the code for the web services package we'll use wsimport, which is located in the bin directory of your Java installation, assuming that you're using Java 1.6 or higher. If your SharePoint site is running over HTTPS you might have a problem running wsimport when pointing it directly to your server via the above URLs, in which case you would receive an error like this one:

[ERROR] sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPath
BuilderException: unable to find valid certification path to requested target

The problem in this case is that your cacerts file doesn't have the certificate from the site. An easy way to get around this is to use your browser to download the WSDL files locally. For the example listed below I have done just that and saved the WSDLs in c:\temp\. Listing 1 and Listing 2 show the code snippets I used to generate the source code, along with the output. You can ignore the warning for each service.

Listing 1. Copy service code generation

C:\temp>"%JAVA_HOME%\bin\wsimport" -d . -p com.microsoft.schemas.sharepoint.soap -keep -extension -Xnocompile Copy.wsdl
parsing WSDL...

[WARNING] SOAP port "CopySoap12": uses a non-standard SOAP 1.2 binding.
  line 229 of file:/C:/temp/Copy.wsdl

generating code...

Listing 2. Lists service code generation

C:\temp>"%JAVA_HOME%\bin\wsimport" -d . -p com.microsoft.schemas.sharepoint.soap -keep -extension -Xnocompile list.wsdl
parsing WSDL...

[WARNING] SOAP port "ListsSoap12": uses a non-standard SOAP 1.2 binding.
  line 1511 of file:/C:/temp/list.wsdl

generating code...

Once you've generated the code it's ready to be incorporated into the solution and used. You can remove the –Xnocompile option from the wsimport command. This option would cause the class files to be generated along with the source, but for this exercise we'll just copy the generated source files into the solution and let Eclipse compile them as if we had authored the source code.

A note about security

In order to successfully execute the SharePoint services I had to deviate from my normal method of web service consumption, which most always involves the use of Axis2. I quickly found that Axis2 has issues with the NTML authorization. It's possible to overcome these errors by using JCIFS in conjunction with Axis2 (see Resources) but that seemed like overkill for something relatively easy. With the approach I'm demonstrating there are no security hurdles to overcome. If your SharePoint site is using HTTPS you will need to ensure that the cacerts file is updated with the site’s certificate (see Resources for details).

Because the examples are meant to be executed as console applications in Eclipse, I pass the following VM argument in the run configuration:

-Djavax.net.ssl.trustStore=path to your updated cacerts file

Custom code

The custom code for this solution is located in the com.jw.sharepoint.examples package in the article source code. It contains a custom class for each of the SharePoint functions we'll be testing:

  1. SharePointUploadDocumentExample demonstrates how to upload a document to SharePoint.
  2. SharePointDeleteListItemExample demonstrates how to delete a document from SharePoint using CAML to query a list and delete a list item.
  3. SharePointListExample demonstrates how to query a folder on SharePoint using CAML and then interpret the results.

Note that I won't explicitly discuss the final class, SharePointListExample. The SharePointDeleteListItemExample class contains functionality for querying, so SharePointListExample is presented for you to study on your own.

About the custom classes

As shown in Figure 2, each of the custom classes follows the same pattern and extends the SharePointBaseExample class, which provides basic SharePoint functionality, as well as utility functions for dealing with CAML and XML. The custom classes also use specific properties files that they load via an initialize() function that is called in main. The properties files have all the properties needed for communicating with SharePoint and any other data that is required at runtime for the class in question.

Figure 2. Class diagram for the custom code (click to enlarge)

Properties files

Each of the properties files located in the demonstration code's Configuration directory has the name of the class it supports with a .properties extension. Most of the properties contained in these files should be self-explanatory. Table 1 briefly describes the additional properties contained in SharePointDeleteListItemExample.properties.

Table 1. Additional properties of the SharePointDeleteListItemExample class

Property Description Example value
username The username for authentication to the SharePoint site. This should be fully domain qualified if running on Linux or using a different ID than used for Windows authentication. Domain\bigbird
password The password to the SharePoint site sesame
wsdl URL to the Lists.asmx WSDL https://abc.xyz.com/project/epms9615/_vti_bin/Lists.asmx?wsdl
Endpoint URL to the Lists.asmx https://abc.xyz.com/project/epms9615/_vti_bin/Lists.asmx
Folder The name of the base folder to use. Prod Support Folder
copy.wsdl URL to the Copy.asmx WSDL https://abc.xyz.com/team/eds/_vti_bin/Copy.asmx?wsdl
copy.endpoint URL to the Copy.asmx https://abc.xyz.com/team/eds/_vti_bin/Copy.asmx
copy.location The location to place files for upload https://abc.xyz.com/project/epms9615/Prod%20Support%20Folder/
Daily%20Monitoring%20Status/AuditDeleteTesting/
copy.sourceFile The local file to use for uploading Configuration/SharePointDeleteListItemExample.properties
delete.FileRef.base The base URL to the SharePoint site, used in delete file requests. https://abc.xyz.com/

Additional configuration files

Some additional configuration files are located in the Configuration directory. These are simple XML snippets written in CAML. We'll use these files, described in Table 2, throughout the solution.

Table 2. Additional configuration files

CAML file Description
Query.xml A CAML file containing the query that we'll use to list files from the SharePoint server. This file shows an example of a query that uses three fields with two different data types (Text and DateTime), as well as two different operators (Contains and Eq).
QueryOptions.xml A static file that we'll use throughout the examples to tell a SharePoint service that we want it to search all subfolders of the current folder.
Delete.xml A CAML file that we'll use to delete SharePoint files. Strings are substituted at runtime.
DeleteListItemQuery.xml A CAML file that we'll use to perform a query of candidate files available for removal from SharePoint

First demo: Uploading a file to SharePoint

Our first exercise will be uploading a file to SharePoint via the CopySoap web service. For this we'll use some of the classes that we generated in Listing 1 and Listing 2 by executing wsimport on the Copy.asmx.

In the SharePointBaseExample class you'll notice a method named getCopySoap(). We'll use this method to return a generated CopySoap instance, which we'll then use to upload a file by calling the method uploadDocument(CopySoap port, String sourceUrl).

The getCopySoap() method is shown in Listing 3.

Listing 3. getCopySoap()

protected CopySoap getCopySoap() throws Exception {
    logger.info("Creating a CopySoap instance...");
    Copy service = new Copy(new URL(getProperties().getProperty("copy.wsdl")),
        new QName("http://schemas.microsoft.com/sharepoint/soap/", "Copy"));
    CopySoap copySoap = service.getCopySoap();
    BindingProvider bp = (BindingProvider) copySoap;
    bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, getProperties().getProperty("username"));
    bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, getProperties().getProperty("password"));
    bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, getProperties().getProperty("copy.endpoint"));
    return copySoap;
}

A Copy class is instantiated using a two-argument constructor, which takes the copy service's WSDL location along with the QName instance to use. We get the CopySoap instance we need from the Copy instance. Once this is done we can cast it to a BindingProvider, which performs the protocol binding and contains the associated context objects for request-and-response message processing. From the BindingProvider we can then set the username, password, and endpoint information on the request-context Map.

Listing 4 shows the main method of class SharePointUploadDocumentExample. This method is very simple; it uses getCopySoap() and uploadDocument(CopySoap port, String sourceUrl) to upload a document to SharePoint. The source file to be copied to SharePoint is defined in the SharePointUploadDocumentExample associated properties file, which it passes to the uploadDocument(…) method via the copy.sourceFile property value.

Listing 4. Upload document main method

public static void main(String[] args) {
    logger.debug("main...");        
    try {       
        SharePointUploadDocumentExample example = new SharePointUploadDocumentExample();
        example.initialize();
        CopySoap p = example.getCopySoap();
        example.uploadDocument(p, properties.getProperty("copy.sourceFile"));
    } catch (Exception ex) {
        logger.error("Error caught in main: ",ex);
    }
}

uploadDocument()

Next we'll call the uploadDocument() method. There are a few things to know about this method:

1 2 Page 1
Page 1 of 2
How to choose a low-code development platform