0
votes

I'm trying to run GetChanges method (sitedata.asmx) from a Java application. However I can't figure out the correct parameters I must pass. This is for SharePoint 2010.

By checking on the service protocol specification, I saw this are the required parameters:

objectType: The change tracking space to report about, either "ContentDatabase" or "SiteCollection". All other objectType values, as defined in section 2.2.5.3, MUST NOT be used. Note that "Site" in the context of this parameter actually means site collection.

contentDatabaseId: GUID of the content database, known in advance or obtained by GetContent request.

LastChangeId: A token specifying the starting point for the requested change report. Normally the protocol client obtains this value from the response to a previous GetContent or GetChanges operation.

CurrentChangeId: A token specifying the endpoint for the requested change report. If not empty, CurrentChangeId must be a valid token obtained from the response to a previous GetChanges operation. Normally, this element is empty; empty specifies that the protocol client requests all changes starting from the starting point up to the present time.

Timeout: A value that determines how many changes should be fetched in the current operation. This value MUST be greater than 0 and the protocol server MUST only fetch x% of total changes that are fetched by default, where x is (Timeout divided by 30000).

The protocol client MUST pass tokens that correspond to the change tracking space specified by the objectType and the target URL of the SOAP request.

The SOAP In message I'm sending is as follows:

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
    <soapenv:Body>
        <ns1:GetChanges xmlns:ns1="http://schemas.microsoft.com/sharepoint/soap/">
            <ns1:objectType>SiteCollection</ns1:objectType>
            <ns1:contentDatabaseId>E5C5E20A-5A9F-406C-B9F6-28923750CECD</ns1:contentDatabaseId>
            <ns1:startChangeId>1;0;E5C5E20A-5A9F-406C-B9F6-28923750CECD;634438121498470000;46852</ns1:startChangeId>
            <ns1:Timeout>0</ns1:Timeout>
        </ns1:GetChanges>
    </soapenv:Body>
</soapenv:Envelope>

However I get this response:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
        <soap:Fault>
            <soap:Code>
                <soap:Value>soap:Receiver</soap:Value>
            </soap:Code>
            <soap:Reason>
                <soap:Text xml:lang="en">Exception of type 'Microsoft.SharePoint.SoapServer.SoapServerException' was thrown.</soap:Text>
            </soap:Reason>
            <detail>
                <errorstring xmlns="http://schemas.microsoft.com/sharepoint/soap/">Object reference not set to an instance of an object.</errorstring>
            </detail>
        </soap:Fault>
    </soap:Body>
</soap:Envelope>

Checked the logs from SharePoint (located at Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\LOGS) and found the following exception:

SOAP exception: System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.SharePoint.SPChangeToken.ParseChangeToken(String strChangeToken)
at Microsoft.SharePoint.SPChangeToken..ctor(String strChangeToken)
at Microsoft.SharePoint.SoapServer.SiteDataImpl.GetChanges(ObjectType objectType, String contentDatabaseId, String& startChangeId, String& endChangeId, Int64 maxChangesToFetch, UInt32 maxSPRequests, Boolean getMetadata, Boolean ignoreSecurityIfInherit, Int32 schemaVersion, Boolean& moreChanges)
at Microsoft.SharePoint.SoapServer.SiteDataImpl.GetChanges(ObjectType objectType, String contentDatabaseId, String& startChangeId, String& endChangeId, Int32 Timeout, Boolean& moreChanges)
at Microsoft.SharePoint.SoapServer.SiteData.GetChanges(ObjectType objectType, String contentDatabaseId, String& LastChangeId, String& CurrentChangeId, Int32 Timeout, Boolean& moreChanges)

However, I'm not able to find any references to that error. I can't even found the method ParseChangeToken from SPChangeToken class (http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spchangetoken_methods.aspx), so this is confusing.

I already saw this question, however this doesn't solve my issue: Other question

Can anyone help me calling this web service correctly?

EDIT

Tried calling it from a C# application to determine that the issue is not with Java. This is the code:

SiteData.SiteDataSoapClient siteDataService = new SiteData.SiteDataSoapClient();
siteDataService.Endpoint.Address = new System.ServiceModel.EndpointAddress("URL/_vti_bin/sitedata.asmx");
siteDataService.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential("username", "password", "domain");
siteDataService.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

String startChangeId = "1;1;69d025ce-96a7-4131-adc0-7da1603e8d24;634439002539570000;46914";
String endChangeId = "";
bool hasMoreChanges = false;
String databaseID = E5C5E20A-5A9F-406C-B9F6-28923750CECD; //Got it by querying SharePoint database. Any idea how to get it programatically?
String result = siteDataService.GetChanges(SiteData.ObjectType.SiteCollection, databaseID, ref startChangeId, ref endChangeId, 0, out hasMoreChanges);
return result;

However, I got 'Microsoft.SharePoint.SoapServer.SoapServerException' and the detail of this exception is null. Used Fiddler to spy on the XML returned by the SharePoint server, and found the same 'Object reference not set to an instance of an object' exception.

So this certainly means there is something wrong with the parameters I'm passing, right?

Thanks!!

Edit

If someone is interested, I made this work too by setting StartChangeId to LastChangeId and EndChangeId to CurrentChangeId in the XML message.

1

1 Answers

0
votes

Solved it. By checking on the SharePoint logs, I noticed the following lines:

06/20/2011 08:24:03.80  w3wp.exe (0x1C2C)                           0x0CAC  SharePoint Foundation           General                         fbs6    Medium      <?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><GetChanges xmlns="http://schemas.microsoft.com/sharepoint/soap/"><objectType>SiteCollection</objectType><contentDatabaseId>{E5C5E20X-5A9F-406C-B9F6-28923750CECD}</contentDatabaseId><startChangeId></startChangeId><endChangeId>1;1;69c025ce-96a7-4131-adc0-7da1603e8d24;634439772069030000;47449</endChangeId><Timeout>0</Timeout></GetChanges></S:Body></S:Envelope>  bafe1d43-e41c-47e9-bff2-5dc35a15298d
06/20/2011 08:24:03.80  w3wp.exe (0x1C2C)                           0x0CAC  SharePoint Foundation           General                         9ka5    Verbose     GetChanges: objectType=SiteCollection, contentDbId={E5C5E20X-5A9F-406C-B9F6-28923750CECD}, startChange=, endChange=; MaxChanges=0, MaxSPRequests=50 bafe1d43-e41c-47e9-bff2-3dc35a15298d

Notice on the second line, that the content database Id is enclosed by "{}" characters. Also, see that "contentDbId" is parsed correctly from the incoming XML, while "endChange" is empty. This second observation, is probably what leads to the "Object reference not set to an instance of an object" exception. So, what is wrong with that changeId? No idea, probably there is something wrong with the XML encoding that prevents SharePoint from parsing the changeId correctly.

By further looking on the same log, I found this lines:

06/20/2011 08:42:54.35  w3wp.exe (0x1C2C)                           0x2BC4  SharePoint Foundation           General                         fbs6    Medium      <?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"><soapenv:Body><ns1:GetChangesEx xmlns:ns1="http://schemas.microsoft.com/sharepoint/soap/"><ns1:version>1</ns1:version><ns1:xmlInput>&lt;GetChanges>&lt;ObjectType>1&lt;/ObjectType>&lt;ContentDatabaseId>{x4284f47-f050-4fe9-b7e9-caf8f4b882b0}&lt;/ContentDatabaseId>&lt;StartChangeId>1;0;x4284f47-f050-4fe9-b7e9-caf8f4b882b0;634441572386370000;72973&lt;/StartChangeId>&lt;EndChangeId />&lt;RequestLoad>100&lt;/RequestLoad>&lt;GetMetadata>False&lt;/GetMetadata>&lt;IgnoreSecurityIfInherit>True&lt;/IgnoreSecurityIfInherit>&lt;/GetChanges></ns1:xmlInput></ns1:GetChangesEx></soapenv:Body></soapenv:Envelope>   fa5ab5a7-2e27-4e78-aa1f-b027ca3b120f
06/20/2011 08:42:54.35  w3wp.exe (0x1C2C)                           0x2BC4  SharePoint Foundation           General                         9ka5    Verbose     GetChanges: objectType=ContentDatabase, contentDbId={x4284f47-f050-4fe9-b7e9-caf8f4b882b0}, startChange=1;0;x4284f47-f050-4fe9-b7e9-caf8f4b882b0;634441572386370000;72973, endChange=; MaxChanges=500, MaxSPRequests=50 fa5ab5b7-2e27-4e78-aa1f-b027ca3b120f

Here, the changeId is correctly parsed from the incoming XML. So, I changed from GetChanges() method to GetChangesEx(), passed the exact same parameters I was using on the former call, and it worked correctly!! My guess is that because the parameters are encoded inside an element of the SOAP In request, the Web Service is able to parse them correctly.

Here is the final SOAP In message (formatted):

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
    <soapenv:Body>
        <ns1:GetChangesEx xmlns:ns1="http://schemas.microsoft.com/sharepoint/soap/">
            <ns1:version>1</ns1:version>
            <ns1:xmlInput>&lt;GetChanges>&lt;ObjectType>7&lt;/ObjectType>&lt;ContentDatabaseId>{X5C5E20A-5A9F-406C-B9F6-28923750CECD}&lt;/ContentDatabaseId>&lt;StartChangeId>1;1;69f025ce-96a7-4131-adc0-7da1603e8d24;634439727021700000;47404&lt;/StartChangeId>&lt;EndChangeId>1;1;69d025ce-96a7-4131-adc0-7da1603e8b24;634441802456970000;47472&lt;/EndChangeId>&lt;RequestLoad>100&lt;/RequestLoad>&lt;GetMetadata>False&lt;/GetMetadata>&lt;IgnoreSecurityIfInherit>True&lt;/IgnoreSecurityIfInherit>&lt;/GetChanges></ns1:xmlInput>
        </ns1:GetChangesEx>
    </soapenv:Body>
</soapenv:Envelope>

Edit

C# code example:

SiteData.SiteDataSoapClient siteDataService = new SiteData.SiteDataSoapClient();
siteDataService.Endpoint.Address = new System.ServiceModel.EndpointAddress("URL/_vti_bin/sitedata.asmx");
siteDataService.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential("username", "password", "domain");
siteDataService.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

String xmlInput = "<GetChanges>" + 
                  "<ObjectType>7</ObjectType>" + 
                  "<ContentDatabaseId>{X5C5E20A-5A9F-406C-B9F6-28923750CECD}</ContentDatabaseId>" + 
                  "<StartChangeId>1;1;69b025ce-96a7-4131-adc0-7da1603e8d24;634439727021700000;47404</StartChangeId>" + 
                  "<EndChangeId>1;1;69b025ce-96a7-4131-adc0-7da1603e8d24;634441802456970000;47472</EndChangeId>" + 
                  "<RequestLoad>100</RequestLoad>" + 
                  "<GetMetadata>False</GetMetadata>" + 
                  "<IgnoreSecurityIfInherit>True</IgnoreSecurityIfInherit>" + 
                  "</GetChanges>";
String result = siteDataService.GetChangesEx(1, xmlInput);