It's all about the answers!

Ask a question

How to retrieve the set of allowed custom SCM attributes via Plain Java Client API?


Richard Pohl (2134) | asked Mar 09 '16, 9:08 a.m.
edited Sep 04 '18, 11:01 a.m. by David Lafreniere (4.8k7)

After reading Ralph's post on https://rsjazz.wordpress.com/2016/02/04/setting-custom-attributes-for-scm-versionables/ I played with the Plain Java Client API to set custom attributes of components.

I wrote the following piece of code that works very well.

IScmService scmService = (IScmService) ((TeamRepository) this.teamRepo).getServiceInterface(IScmService.class);
IWorkspaceManager manager = (IWorkspaceManager) this.teamRepo.getClientLibrary(IWorkspaceManager.class);
Set<String> names = manager.findAllComponentNames(new NullProgressMonitor());
for (String oneName : names) {
  List l = manager.findComponents(oneName, null, true, new NullProgressMonitor());
  while (l.size() > 0) {
ComponentHandle componentHandle = (ComponentHandle) l.remove(0);
IComponent component =(IComponent) this.teamRepo.itemManager().fetchCompleteState(componentHandle, new NullProgressMonitor());
if (oneName.contains("Test")) {
HashMap<String, Object> customAttributeMap = new HashMap<String, Object>();
customAttributeMap.put("MyAttribute", "An Example");
IGenericAttributes attributes = IGenericAttributes.FACTORY.newInstance(customAttributeMap);
scmService.setComponentCustomAttributes(componentHandle, attributes, null, null, null);
component = (IComponent) this.teamRepo.itemManager().fetchCompleteItem(component, IItemManager.REFRESH, this.monitor);
}
System.out.println("For name \"" + oneName + "\" retreived " + component.getCustomAttributes().size() + " attributes:" + component.getCustomAttributes().keySet());
}
}
Now, when I change the line customAttributeMap.put("MyAttribute", "An Example"); to customAttributeMap.put("MyOtherAttribute", "An Example"); I get an Exception that complains about the attribute being undefined in the project area. I know that you can set it via the web UI as shown on the picture in Ralph's blog here: https://rsjazz.files.wordpress.com/2016/02/scm-attributes.png?w=640.

But I am now wondering if there is any way to
  1. Read the list of available attributes for a specific type of item (e.g. Component) in order to avoid the Exception proactively
  2. Modify the list of attributes defined by the PA via the API instead of using the web interface (in order to add a previously undefined attribute to the PA and then set it for a component).
Unfortunately, analyzing the stack trace of the exception points me to some code in the ScmService that I cannot access So I would really appreciate any hint on where to start looking for the list of IGenericAttributes that are defined by the PA.


Comments
Ralph Schoon commented Mar 09 '16, 10:25 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER

Good question. Currently, I have no idea.

Accepted answer


permanent link
David Lafreniere (4.8k7) | answered Sep 04 '18, 10:17 a.m.
FORUM MODERATOR / JAZZ DEVELOPER
edited Sep 04 '18, 11:02 a.m.
In RTC 6.0.6, server-side API was added to IScmService that will return the list of custom attributes that are available on a given project area.
The API and Javadoc is shown below:
    /**
     * Return the defined custom attribute identifiers for the given type in the given project area.
     *
     * @param projectArea
     *            The project area. Never {@code null}.
     * @param artifactType
     *            The artifact type. One of:
     *            <ul>
     *            <li>{@link CustomAttributeConstants#ARTIFACT_FILE}</li>
     *            <li>{@link CustomAttributeConstants#ARTIFACT_STREAM}</li>
     *            <li>{@link CustomAttributeConstants#ARTIFACT_BASELINE}</li>
     *            <li>{@link CustomAttributeConstants#ARTIFACT_BASELINE_SET}</li>
     *            <li>{@link CustomAttributeConstants#COMPONENT}</li>
     *            </ul>
     * @param monitor
     *            A repository progress monitor, may be {@code null} if
     *            progress reporting and cancellation is not required.
     *
     * @return the defined attribute identifiers.
     *
     * @throws TeamRepositoryException
     *
     * @since 0.11.0.6 (RTC 6.0.6)
     *
     * @see CustomAttributeConstants
     */
    public String[] getDefinedCustomAttributeIdentifiers(
         IProjectAreaHandle projectArea,
         int artifactType,
         IRepositoryProgressMonitorHandle monitor)
               throws TeamRepositoryException;



In RTC 6.0.6, client-side API was added to IWorkspaceManager that will return the list of custom attributes that are available on a given project area.
The API and Javadoc is shown below:
    /**
     * Return the defined custom attribute identifiers for the given type in the given project area.
     *
     * @param projectArea
     *            The project area. Never {@code null}.
     * @param artifactType
     *            The artifact type. One of:
     *            <ul>
     *            <li>{@link CustomAttributeConstants#ARTIFACT_FILE}</li>
     *            <li>{@link CustomAttributeConstants#ARTIFACT_STREAM}</li>
     *            <li>{@link CustomAttributeConstants#ARTIFACT_BASELINE}</li>
     *            <li>{@link CustomAttributeConstants#ARTIFACT_BASELINE_SET}</li>
     *            <li>{@link CustomAttributeConstants#COMPONENT}</li>
     *            </ul>
     * @param monitor
     *            A progress monitor, or {@code null} if progress reporting is not desired.
     *
     * @return the defined attribute identifiers.
     *
     * @throws TeamRepositoryException
     *
     * @LongOp This is a long operation; it may block indefinitely; must not be
     *         called from a responsive thread.
     *
     * @since 0.11.0.6 (RTC 6.0.6)
     */
    public String[] getDefinedCustomAttributeIdentifiers(
         IProjectAreaHandle projectArea,
         int type,

         IProgressMonitor monitor)

               throws TeamRepositoryException;


Unfortunately, there is no API available to define/create a new custom attribute for a given SCM type in a project area. This will have to done manually by a Project Administrator using the web UI (or you can attempt the "unofficial solution" that Richard wrote in his answer for this question. Richard 's solution can also be used to determine the available custom attributes for those of you that are on a pre-6.0.6 server.

David Lafreniere selected this answer as the correct answer

One other answer



permanent link
Richard Pohl (2134) | answered Mar 16 '16, 12:51 p.m.
edited Mar 16 '16, 1:02 p.m.
After struggling a little, I found kind of a solution. I have to admit that the solution seems a bit awkward. It works at least for the 6.0.1 release of RTC.

For the first problem I had (downloading SCM attributes), it is possible to retrieve the information as follows.
IProcessClientService processClientService =
    (IProcessClientService) this.teamRepository.getClientLibrary(IProcessClientService.class);
IProjectArea area = (IProjectArea) this.teamRepository.itemManager().fetchCompleteState(projectAreaHandle, null);
IProcessConfigurationData configData =
    processClientService.getClientProcess(area, null).getProjectConfigurationData(
        ExtendedAttributeConstants.XML_CONFIGURATION_DATA_ID, null);
(where the constant XML_CONFIGURATION_DATA_ID is set to "com.ibm.team.scm.service.extendedAttributeDefinition")

With this configData you can do something like this:
for (IProcessConfigurationElement element : configData.getElements()) {
  // ...
  String extractedValue = element.getAttribute(xmlAttributeName);
}
(xmlAttributeName being the attribute you are interested in, e.g. "extendedAttributeDefinitionKey" for the name)

Now my second problem seems to be significantly more difficult to address. The only solution I could think of is this.
IProjectArea area = (IProjectArea) this.teamRepository.itemManager().fetchCompleteState(projectAreaHandle, null);
IProcessItemService processItemService =
    (IProcessItemService) this.teamRepository.getClientLibrary(IProcessItemService.class);
IProjectArea workingCopy = (IProjectArea) processItemService.getMutableCopy(area);
@SuppressWarnings("rawtypes")
Map procData = area.getProcessData();
IContent procContent = (IContent) procData.get(ProcessContentKeys.PROCESS_SPECIFICATION_KEY);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
this.teamRepository.contentManager().retrieveContent(procContent, outStream, null);
String xmlContent = outStream.toString(IContent.ENCODING_UTF_8);
Document xmlSpec = convertStringToDocument(xmlContent);
String updatedXml = buildUpdatedXML(xmlSpec, definitions);
IContent newContent =
    this.teamRepository.contentManager().storeContent(IContent.CONTENT_TYPE_XML, updatedXml, null);
workingCopy.getProcessData().put(ProcessContentKeys.PROCESS_SPECIFICATION_KEY, newContent);
processItemService.save(workingCopy, null);
where buildUpdatedXML replaces a node in the XML document:
private String buildUpdatedXML(final Document xmlSpec, final List definitions) {
    Element element = findExtendedAttributesDOMElement(xmlSpec);
    element.getParentNode().replaceChild(xmlSpec.importNode(generateDOMNode(definitions, element), true), element);
    return convertDocumentToString(xmlSpec);
}
The method findExtendedAttributesElement() does some traversal on the XML DOM. It finds or creates some parent node in the XML to which the new content (attribute definition) could be added. The method generateDOMNode(...) generates a new DOM node based on the attribute definition I would like to put on the server. The classes for attribute definitions (ExtendedAttributeDefinition) is my own POJO for holding some attributes of the attribute definition. The name is inspired by some class deep in the SDK sources (from which I took the general idea for part 1 of my solution). 

To me, this solution does not seem to be a very clean and stable one. Especially as I am using plain XML replacement that was considered as bad practice in several other discussions here.

So I would be grateful if anyone having a better solution to this could share it with me.

Your answer


Register or to post your answer.