THE PLM STATE

Setting a List Value through Agile PLM Web Services from Microsoft .Net

Agile Web Services & Complex TypesIf you are having trouble figuring out how to use complex data types with Agile Web services from Microsoft .Net this blog may be for you.

The web services for Agile PLM are a great way of providing a means of platform independent integration. What I have come to learn however is that there are a few gotchas that can really cost a significant amount of time to overcome.

One such obstacle is the ability to set a list value on an object. In this article I will:

  • Generate a C# class to access the Agile PLM BusinessObject services
  • Write code to get an existing item from Agile
    • Look at the AgileListEntryType element
  • Write code to update an item in Agile by setting a list value
  • Show that the same response element attributes fail in the request
    • Compare the XML element with the documentation
    • Modify the list element so that the value gets stored

How to generate a C# class for the Agile PLM BusinessObject service

There are a number of ways to reference the web service calls in Visual Studio. Until recently, I added a web reference and created a factory class to return the correct service delegate. It works without issue, but there's an easier method.

  • Locate the Agile PLM service you wish to talk to
    • My development machine’s hostname is dev-agile9311c and runs on port 7001, so I can see the service listings at http://dev-agile9311c:7001/CoreService/services
    • Pick the service you wish to create a class for, such as the BusinessObject
    • Copy the wsdl link location for the Business Object. Against my Agile PLM environment the URL would be http://dev-agile9311c:7001/CoreService/services/BusinessObject?wsdl
  • Open a Visual Studio command prompt. On my installation of Visual Studio, I can get to it from Start | All Programs | 1. 1. Microsoft Visual Studio 2010 | Visual Studio Tools | Visual Studio x64 Win64 Command Prompt (2010)
  • Change directory to your C# project source code directory
  • Generate the service object using the wsdl.exe command
    • Execute: wsdl /out:BusinessObjectService.cs http://dev-agile9311c:7001/CoreService/services/BusinessObject?wsdl
    • It will give some schema validation errors, but will still write the C# class
  • Add the BusinessObjectService.cs to your .Net project

Getting an existing Agile object

For the purposes of the blog, I have simply created a console C# project and added the BusinessObjectService.cs generated above. The next step is for me to create a getObject() method that will return a single document. Here is the code:

{codecitation style="brush: c#;auto-links: false"}
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace AgileWSTest
{
class Program
{
static void Main(string[] args)
{
Program app = new AgileWSTest.Program();
app.getObject();
}

public BusinessObjectService getService()
{
System.Net.ServicePointManager.Expect100Continue = false;
System.Net.ICredentials credentials = new System.Net.NetworkCredential("rmccabe", "agile");
BusinessObjectService service = new BusinessObjectService();

service.Url = "http://dev-agile9311c:7001/CoreService/services/BusinessObject";
service.Credentials = credentials;
return service;
}

public void getObject()
{
BusinessObjectService service = getService();

GetObjectRequestType request = new GetObjectRequestType();
AgileGetObjectRequest[] agileGetObjectRequest = new AgileGetObjectRequest[1];
agileGetObjectRequest[0] = new AgileGetObjectRequest();
agileGetObjectRequest[0].classIdentifier = "Document";
agileGetObjectRequest[0].objectNumber = "Zws-Test-1";

request.requests = agileGetObjectRequest;

GetObjectResponseType response = service.getObject(request);
}
}
}
{/codecitation}

Executing the code and watching the TCP traffic with Wire Shark gives me better insight as to what Agile is returning as compared to the debugger.

Monitoring traffic for getObject()

Notice the highlighted XML itemCategory element. This is our list of interest.

{codecitation style="brush: xml;"}

275514
OTHER
Other

{/codecitation}

We can use this response information to update the element right? Well, not exactly … read on.

Setting the list value in Agile

Let’s add a method to our test utility to update the same item:

{codecitation style="brush: c#;"}
public void updateItem()
{
BusinessObjectService service = getService();

UpdateObjectRequestType request = new UpdateObjectRequestType();
AgileUpdateObjectRequest[] requests = new AgileUpdateObjectRequest[1];

requests[0] = new AgileUpdateObjectRequest();
requests[0].classIdentifier = "Document";
requests[0].objectNumber = "Zws-Test-1";
AgileRowType rows = new AgileRowType();

XmlElement[] messages = new XmlElement[1];
messages[0] = buildListAttribute("itemCategory", "Other");

rows.Any = messages;
requests[0].data = rows;
request.requests = requests;

UpdateObjectResponseType response = service.updateObject(request);
String status = response.statusCode.ToString().ToUpper();
}
{/codecitation}

Notice the buildListAttribute() call. We will use the details in the web service response so that our update request looks similar. The only difference is that our element will only pass in the value of the selection, not the id and apiName. In addition, since our element is the apiName of the list, the attributeId attribute is not needed:

{codecitation style="brush: c#;"}
public XmlElement buildListAttribute(String apiName, String value)
{
XmlDocument doc = new XmlDocument();
XmlAttribute attribute;
XmlElement element;
element = doc.CreateElement(apiName);

attribute = doc.CreateAttribute("xsi", "type", "http://www.w3.org/2001/XMLSchema-instance");
attribute.Value = "AgileListEntryType";
element.Attributes.Append(attribute);

attribute = doc.CreateAttribute("xmlns:common");
attribute.Value = "http://xmlns.oracle.com/AgileObjects/Core/Common/V1";
element.Attributes.Append(attribute);

element.InnerXml = "" + value + "";
return element;
}
{/codecitation}

Running the application again to call the updateItem() method and watching in Wire Shark, we can see the request:

Calling updateItem()

The element we are sending in our update request is:

{codecitation style="brush: xml;"}
Other
{/codecitation}

With the exception of the missing attributeId, you can see that the itemCategory attributes are identical to the ones returned from Agile in our get request. BUT THEY DON’T WORK! In fact, if we look at the application server log, Weblogic Server in my case, you can see an exception being thrown in stderr.log: “java.lang.Exception: No deserializer for {http://www.w3.org/2001/XMLSchema-instance}AgileListEntryType”. If we inspect the results returned, we won’t find much except “60086” which is meaningless.

Let’s head to the documentation you might be saying … RTFM right? The documentation can be found at http://docs.oracle.com/cd/E18601_08/otn/pdf/user/E18640_02.pdf. Here is a sample call on page 138:

{codecitation style="brush: xml;"}

1
ACTIVE
Active

{/codecitation}

The fix is rather simple thought not intuitivley obvious – we are going to switch the URLs making our buildListAttribute() method look like the following:

{codecitation style="brush: c#;"}
public XmlElement buildListAttribute(String apiName, String value)
{
XmlDocument doc = new XmlDocument();
XmlAttribute attribute;
XmlElement element;
element = doc.CreateElement(apiName);

attribute = doc.CreateAttribute("xsi", "type", "http://xmlns.oracle.com/AgileObjects/Core/Common/V1");
attribute.Value = "AgileListEntryType";
element.Attributes.Append(attribute);

element.InnerXml = "" + value + "";
return element;
}
{/codecitation}

This call works, but let’s look at the request so we can see how different it is:

Fixed updateItem() call

New header:
{codecitation style="brush: xml;"}
{/codecitation}

Original Response header:
{codecitation style="brush: xml;"}

{/codecitation}

Summary

List values and complexTypes are not as difficult as they may first appear; you just need to make sure to provide the proper instructions so that Agile can understand how to instantiate objects in your request. Hopefully this allows you to move forward with your .Net integration to Agile PLM.

Subscribe to the ZWS Blog

Recent Posts