It's all about the answers!

Ask a question

How to get custom attribute value with java api


haizi wu (6054248) | asked Nov 16 '12, 5:31 a.m.
 Hi all:
These days I was trying to create a Operation Participant(fellow-up action), I got all the files from https://jazz.net/library/article/634 to setup a extension developement enviroment, there are 6 labs there,  the function of this extension is submit a build when the state change to a specific state, and I have finished them all, now I am trying to make a littile modification to want to copy a custom attribute value to another attribute when the state changes, what I exactly want to do is that: 

     "Test User" is a custom attribute of contributor type, when I click save button, the value of the "Test User"   attribute is copied to the "Owner" attribute automatically.

I thought that it would be very easy, but when I look into it ,only I can do is get the value of build-in attributes, I dont know how to get/set the custom attribute value, I copy the code from jazz forum, but none of them works, I can give you some links found in jazz forum:

https://jazz.net/forum/questions/87341/how-to-fetch-the-workitem-built-in-custom-attribute-id-and-value
https://jazz.net/forum/questions/66558/wi-attribute-customization-get-custom-attribute-value
https://jazz.net/forum/questions/87541/getting-custom-attributes-names-via-java-api?page=1&focusedAnswerId=87543#87543

below is the code I work in and some code is copy from the page above:

7 answers



permanent link
haizi wu (6054248) | answered Nov 16 '12, 5:33 a.m.
/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2010. All Rights Reserved. 
 * 
 * Note to U.S. Government Users Restricted Rights:  Use, 
 * duplication or disclosure restricted by GSA ADP Schedule 
 * Contract with IBM Corp.
 *******************************************************************************/
package net.jazz.rtcext.workitem.extensions.service;


import java.awt.List;


import net.jazz.rtcext.workitem.extensions.common.IBuildOnStateChangeDefinitions;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.osgi.util.NLS;

import com.ibm.team.build.common.model.IBuildDefinition;
import com.ibm.team.build.internal.common.ITeamBuildRequestService;
import com.ibm.team.build.internal.common.ITeamBuildService;
import com.ibm.team.process.common.IProcessConfigurationElement;
import com.ibm.team.process.common.advice.AdvisableOperation;
import com.ibm.team.process.common.advice.IItemsResponse;
import com.ibm.team.process.common.advice.IProcessReport;
import com.ibm.team.process.common.advice.IReportInfo;
import com.ibm.team.process.common.advice.runtime.IOperationParticipant;
import com.ibm.team.process.common.advice.runtime.IParticipantInfoCollector;
import com.ibm.team.repository.common.IAuditableHandle;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.repository.service.AbstractService;
import com.ibm.team.repository.service.IRepositoryItemService;
import com.ibm.team.workitem.common.ISaveParameter;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IAttributeHandle;
import com.ibm.team.workitem.common.model.IState;
import com.ibm.team.workitem.common.model.IWorkItem;
import com.ibm.team.workitem.common.model.Identifier;


/**
 * 
 * If on the save of a work item of a certain type, the state has changed to a
 * certain state, run the specified build.
 * 
 * It is critical to understand that operation participants are managed as
 * singletons by the process component. Therefore, their methods, most notably
 * the run method must be reentrant. Operation participants must not rely on any
 * instance state variables (i.e. non-static fields).
 * 
 * While rare, it is occasionally the case that the complexity of the operation
 * to be performed and the number and interactions of methods and their data
 * interdependencies will present a case where the use of instance state
 * variables is highly desirable. In this case, another class will need to be
 * defined and an instance of that class created for each invocation of the run
 * method. The run method can then delegate the operation to the instance of
 * this second class. This second class can use instance state variables for its
 * implementation.
 * 
 */
public class BuildOnStateChangeParticipant extends AbstractService implements
IOperationParticipant {

/**
* If a process uses the build on state change participant, this run method
* is called by the Jazz process engine whenever a work item is saved.
* @param operation
*            the operation in which to participate
*            <p>
*            The operation provides the runtime context for the
*            participant. This participant will use it to get information
*            about the work item that is being saved.
*            </p>
* @param participantConfig
*            the configuration element which configures this participant;
*            this corresponds to the XML element which declares this
*            participant in the process specification/customization.
*            <p>
*            This participant obtains the trigger work item type and state
*            from this parameter. The build definition id is also found
*            here.
*            </p>
* @param collector
*            the collector into which any info should be added
*            <p>
*            Participants report back failures and success via this
*            collector.
*            </p>
* @param monitor
*            the monitor used to report progress and receive cancellation
*            requests
*            <p>
*            Generally speaking, server side participants, like this one,
*            do not need to report progress to the monitor. Client side
*            participants should report progress. Checking for cancellation
*            after long operations performed by the participant is a good
*            practice on either client or server.
*            </p>
* @throws TeamRepositoryException
*             if one of the invoked service api throws it
*/

permanent link
haizi wu (6054248) | answered Nov 16 '12, 5:33 a.m.
 public void run(AdvisableOperation operation,
IProcessConfigurationElement participantConfig,
IParticipantInfoCollector collector, IProgressMonitor monitor)
throws TeamRepositoryException {
/*
* There are several reasons not to request a build. This code is
* structured to perform the most common and fastest cases first. The
* idea is to find out as quickly as possible that you have nothing to
* do. Depending on how the participant is configured (which process
* area(s), iteration(s) or roles it affects), it could be called for
* all work item saves that occur in the using project area.
*/

/*
* First check that the operation data is work item save data.
*/
Object data = operation.getOperationData();
ISaveParameter saveParameter = null;
if (data instanceof ISaveParameter) {
saveParameter = (ISaveParameter) data;
/*
* If the state id has not changed, do not build.
*/
IWorkItem newState = (IWorkItem) saveParameter.getNewState();
Identifier<IState> newStateId = newState.getState2();
Identifier<IState> oldStateId = null;
IWorkItem oldState = (IWorkItem) saveParameter.getOldState();
// System.out.println(newState.getHTMLSummary().toString());
// System.out.println(((java.util.List<IAttributeHandle>)newState.getCustomAttributes()).get(3).toString());
// IAttribute test = (IAttribute)newState.getCustomAttributes().get(3);
// //Object object = oldState.getValue(test.);
// //java.util.List<java.lang.String> list = newState.g;
//// IAttributeHandle attr = (IAttribute) test;
// IAttributeHandle handle = newState.getCustomAttributes().get(2);
// IRepositoryItemService itemService = getService(IRepositoryItemService.class);
// IAttribute attribute = (IAttribute) itemService.fetchItem(handle, IRepositoryItemService.COMPLETE);
// System.out.println("The attribute id: " + attribute.getIdentifier());
// System.out.println("The value of the custom attribute: " + newState.getValue(attribute));
com.ibm.team.workitem.common.model.IAttributeHandle handle = newState.getCustomAttributes().get(0);
//com.ibm.team.workitem.common.model.IAttribute attribute = (IAttribute) teamRepository.itemManager().fetchCompleteItem(handle, IItemManager.DEFAULT, new NullProgressMonitor());
// System.out.println("The attribute id: " + attribute.getIdentifier());
// System.out.println("The value of the custom attribute: " + newState.getValue(attribute));
System.out.println();
if (oldState != null) // New work item check.
oldStateId = oldState.getState2();
if ((newStateId != null) && !(newStateId.equals(oldStateId))) {
/*
* Perform the first stage of configuration parsing. This stage
* parses out the work item type and state ids. It just locates
* the build definition element but does not parse through it.
* The build definition is only parsed if we know a build is
* needed. This is to improved performance in the more common
* case when a build is not needed. Note that this performance
* consideration is a bit of a reach in this case. Parsing the
* build definition id is of trivial cost. The point is to learn
* the general pattern of delaying actions that often will not
* be needed and could be expensive. For example, what if the
* second stage of the parse was rare but involved querying the
* database? In that case, this sort of pattern would be very
* useful. The point in this workshop is to learn the pattern.
*/
ParsedConfig parsedConfig = new ParsedConfig();
parseConfig1(participantConfig, parsedConfig);
String newType = newState.getWorkItemType();
System.out.println(newState.getHTMLSummary().toString());
System.out.println(newState.getCustomAttributes().toString());

/*
* If the work item is not of the proper type, do not build. If
* the work item type id is null, the test will return false and
* a build will not be attempted.
*/
if (newType.equals(parsedConfig.fWorkItemTypeId)) {
/*
* Finally, if the new state is the target state, build.
* Again, a null id is handled in the same manner.
*/
if (newState.getState2().getStringIdentifier().equals(
parsedConfig.fWorkItemStateId)) {
/*
* Now it is time for the second stage of the
* configuration parse. Only build if the build
* definition id is not null.
*/
parseConfig2(parsedConfig);
if (parsedConfig.fBuildDefinitionId != null)
build(parsedConfig.fBuildDefinitionId, collector);
}
}
}
}
}

permanent link
haizi wu (6054248) | answered Nov 16 '12, 5:33 a.m.
 /**
* Request the specified build.
* @param buildId
*            the build definition id to run
* @param collector
*            the collector into which any info should be added
*            <p>
*            This participant will report back any failures it discovers,
*            for example, if the build definition does not exist.
*            </p>
* @throws TeamRepositoryException
*             if one of the invoked service api throws it
*/
protected void build(String buildId, IParticipantInfoCollector collector)
throws TeamRepositoryException {
/*
* The team build service is used to access team build information
* (engines, definitions, results, etc). We use it to get the desired
* build definition.
*/
ITeamBuildService buildService = getService(ITeamBuildService.class);
IBuildDefinition buildDef = buildService.getBuildDefinition(buildId);

/*
* If the build definition was found, try to run the build.
*/
if (buildDef != null) {
/*
* The team build request service is used to create, access and
* manipulate build requests. We will request a new build. The true
* and false in the build request parameters indicate that it is
* okay if multiple builds of this type are in the queue and that
* this is not a personal build. The first null indicates that we
* have no build properties to add or override (we will change this
* later). The second null means there are no build properties to
* delete.
*/
ITeamBuildRequestService brService = getService(ITeamBuildRequestService.class);
IItemsResponse response = brService.requestBuild(buildDef, null,
null, true, false);

/*
* If the build request has been submitted, report success back. It
* will show up in the team advisor if success reports are not being
* filtered out and the show details tree is expanded.
*/
if ((response != null) && (response.getFirstClientItem() != null)) {
String description = NLS
.bind(
"A new build request for build definition ''{0}'' was submitted.",
buildId);
IReportInfo info = collector.createInfo(
"Build request successful", description);
collector.addInfo(info);
} else {
/*
* The build request could not be created, report this back as
* an error. An error report will stop the work item save from
* succeeding and will show up in the team advisor.
*/
String description = NLS
.bind(
"The build request for build definition ''{0}'' could not be honored. It is likely that a build engine is not associated with the definition.",
buildId);
IReportInfo info = collector.createInfo(
"Unable to request build", description);
info.setSeverity(IProcessReport.ERROR);
collector.addInfo(info);
}
} else {
/*
* The build definition was not found, report this back as an error.
* An error report will stop the work item save from succeeding and
* will show up in the team advisor.
*/
String description = NLS
.bind(
"The build request for build definition ''{0}'' could not be found.",
buildId);
IReportInfo info = collector.createInfo("Unable to request build",
description);
info.setSeverity(IProcessReport.ERROR);
collector.addInfo(info);
}
}

/**
* This class is used retrieve results from the participant configuration
* parsing methods.
*/
private class ParsedConfig {
public String fWorkItemTypeId = null;
public String fWorkItemStateId = null;
public IProcessConfigurationElement fBuildConfigElement = null;
public String fBuildDefinitionId = null;
}

/**
* Parses the participant configuration and sets the work item type and
* state ids. It locates the build element but takes no further action.
* After this method is called, the build definition id will still be null.
* @param participantConfig
*            the participant configuration
* @param parsedConfig
*            parse results are stored here. Note that this method does not
*            validate the work item type or id, nor the build element. They
*            could still be null after this method completes. The build
*            definition id will be null.
*/
private void parseConfig1(IProcessConfigurationElement participantConfig,
ParsedConfig parsedConfig) {
/*
* Cycle through the child elements of the participant configuration
* looking for the trigger and build definition elements.
*/
IProcessConfigurationElement[] triggerOrBuild = participantConfig
.getChildren();
for (IProcessConfigurationElement element : triggerOrBuild) {
if (element.getName().equals(
IBuildOnStateChangeDefinitions.TAG_TRIGGER)) {
/*
* Found a trigger definition. Cycle through its child elements
* to find the work item and state ids.
*/
IProcessConfigurationElement[] children = element.getChildren();
for (int i = 0; i < children.length; i++) {
IProcessConfigurationElement child = children[i];
String elementName = child.getName();
if (elementName
.equals(IBuildOnStateChangeDefinitions.TAG_CHANGED_WORKITEM_TYPE)) {
parsedConfig.fWorkItemTypeId = child
.getAttribute(IBuildOnStateChangeDefinitions.ID);
} else if (elementName
.equals(IBuildOnStateChangeDefinitions.TAG_TRIGGER_STATE_ID)) {
parsedConfig.fWorkItemStateId = child
.getAttribute(IBuildOnStateChangeDefinitions.ID);
}
}
} else if (element.getName().equals(
IBuildOnStateChangeDefinitions.TAG_BUILD)) {
/*
* Found the build definition. For now, just set aside the
* element. It will only be parsed if we need to run a build.
*/
parsedConfig.fBuildConfigElement = element;
}
}
}

/**
* Second stage of the configuration parsing that handles the build
* definition.
* @param parsedConfig
*            the build definition element is now parsed and the build
*            definition id is updated. Note that the id is not validated by
*            this method and may still be null.
*/
private void parseConfig2(ParsedConfig parsedConfig) {
if (parsedConfig.fBuildConfigElement != null) {
IProcessConfigurationElement[] children = parsedConfig.fBuildConfigElement
.getChildren();
for (int i = 0; i < children.length; i++) {
IProcessConfigurationElement child = children[i];
String elementName = child.getName();
if (elementName
.equals(IBuildOnStateChangeDefinitions.TAG_BUILD_DEFINITION)) {
parsedConfig.fBuildDefinitionId = child
.getAttribute(IBuildOnStateChangeDefinitions.ID);
}
}
}
}

}





permanent link
Ralph Schoon (60.5k33643) | answered Nov 16 '12, 11:39 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER
Hi,

the issue with work items and attributes is that the EMF implementation only provides access to very few attributes directly using methods. Otherwise you have to get the IAttribute using its ID or by iterating the built in and custom attributes of a work item and the using getValue(iAttribute).

Plase see https://rsjazz.wordpress.com/2012/07/31/rtc-update-parent-duration-estimation-and-effort-participant/ the method getDuration() uses this technique.

Comments
haizi wu commented Nov 16 '12, 12:01 p.m. | edited Nov 16 '12, 12:06 p.m.

thanks you, first I want to know is it possible? and if it is ,could you  give me the code that I can finish this extension, I really need them, by the way, I cannot visit  https://rsjazz.wordpress.com/2012/07/31/rtc-update-parent-duration-estimation-and-effort-participant/


thanks in advance!


permanent link
Ralph Schoon (60.5k33643) | answered Nov 16 '12, 12:15 p.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER
Can you try the following:

The advantage with blogs is that they all provide RSS feeds so you can easily read any blog using a web based feed readers even if the main WordPress site is blocked.

In case of WordPress blogs, you can append the string ".nyud.net" to the blog URL and it should open just fine. For instance, if the main blog is located at labnol.wordpress.com, you can access a mirror image of this site from labnol.wordpress.com.nyud.net.

Wordpress is blocked in China.

Here is the code:

get an attribute from the ID
        IWorkItem parent = (IWorkItem)workItemServer.getAuditableCommon().resolveAuditable(parentHandle,IWorkItem.FULL_PROFILE,monitor).getWorkingCopy();
        IAttribute timeSpentAttribute = wiCommon.findAttribute(parent.getProjectArea(), WORKITEM_ATTRIBUTE_TIMESPENT, monitor);
        IAttribute correctedEstimateAttribute = wiCommon.findAttribute(parent.getProjectArea(), WORKITEM_ATTRIBUTE_CORRECTEDESTIMATE, monitor);

Read the value

private long getDuration(IWorkItem child, IAttribute attribute, IProgressMonitor monitor) throws TeamRepositoryException {
    long duration = 0;
    if(attribute!=null && child.hasAttribute(attribute)){
        Long tempDuration = (Long)child.getValue(attribute);
        if(tempDuration!=null&& tempDuration.longValue()>0)
            return tempDuration.longValue();
        }
    return duration;
}
You can also search the forum and Jazz.net for getValue and setValue




Comments
haizi wu commented Nov 17 '12, 7:37 a.m.

 I am sorry that I am not familiar with the java api, I copied your code, but it gives me a lot of unresolved variable such as  parentHandle, wiCommon, WORKITEM_ATTRIBUTE_TIMESPENT, so would you please give more detail


permanent link
sam detweiler (12.5k6185201) | answered Nov 17 '12, 8:36 a.m.
edited Nov 17 '12, 8:38 a.m.
here is some code I have in an Advisor to see all the workitem attributes

           ISaveParameter p = (ISaveParameter)data;
            // get the affected object
            IAuditable auditable = p.getNewState();
           
            // if this is a workitem
            if (auditable instanceof IWorkItem)
            {               
                // reference the right object type (cast)
                IWorkItem workItem = (IWorkItem) auditable;
                // get the worker objects
                IAuditableCommon iac = p.getSaveOperationParameter().getAuditableCommon();
                WorkflowManager wfm = new WorkflowManager(iac);   
               
                if(false)
                {
                    IWorkItemCommon workItemCommon = iac.getPeer(IWorkItemCommon.class);
                    // loop thru all the attributes in the project
                    // attributes are project wide, not workitem specific
                    for(IAttribute ia:workItemCommon.findAttributes(p.getOldProcessArea().getProjectArea(), monitor) )
                    {
                        // if this attribute is available on this workitem
                        if(workItem.hasAttribute(ia))
                        {
                            System.out.println("processing for variable="+ia.getDisplayName()+" attrib type="+ia.getAttributeType());
                            try
                            {
                                // attempt to get the enumeration literal for this attribute
                                // will throw exception if not an enum
                                IEnumeration<ILiteral> enumeration = (IEnumeration<ILiteral>) workItemCommon.resolveEnumeration(ia, monitor);
                                if(enumeration!=null)
                                {
                                    // get the literal specifically
                                    String[] iaval = ia.getValue(iac, workItem, monitor).toString().split(":");
                                   
                                    // if present
                                    if(iaval.length>1 && iaval[1]!=null)
                                    {
                                        // loop thru the literal to value mappings                               
                                        for (ILiteral literal : enumeration.getEnumerationLiterals()) 
                                        {
                                            // if this literal matches the attribute value
                                            if(literal.getIdentifier2().getStringIdentifier().equalsIgnoreCase(iaval[1]))
                                            {
                                                // display the usable name
                                                System.out.println("attribute name="+ia.getIdentifier() +", type"+"="+ia.getAttributeType()+ " literal="+literal.getIdentifier2().getStringIdentifier()+" literal name="+literal.getName());                                       
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                            catch (Exception e)
                            {
                                //System.out.println("Exception="+e.toString());
                            }                       
                        }
                    }               
}

Comments
haizi wu commented Nov 17 '12, 9:43 a.m.

 Thank you for your reply, I have got the custom attribute value, and next I'd like to copy that value to another attribute when i click the save button, but I am wondering if it is possible, this operation participant is for Workitem Saving operation, if I copy the value to another attribute, actually that equals I am editing the workitem,  then I have to SAVE it, but if I save it, it will trigger the operation participant, it is the endless process,  that will  be impossible. is that right?


I tried to use newState.setValue(attribute, "Hello") statement to set the attribute's value, but it didnot work.

if it is still possible, I want to know how to copy the attribute to another attribute

thanks in advance!



permanent link
sam detweiler (12.5k6185201) | answered Nov 17 '12, 3:14 p.m.
edited Nov 17 '12, 3:24 p.m.
correct, if u modify the workitem and do a save again, then another call will be made to the advisor..

1st you should never modify anything in an advisor.. it is supposed to ADVISE
so you should do this in a PARTICIPANT, cause it PARTICIPATES in the transaction

you need to set a flag in the data that you can check when you are called a second time (recursion)

The saveWorkItem3() operation takes an additional parameter, a set of strings. This can be used to detect that a subsequent trigger of the participant was caused by this save operation. The following code inserted into the run() operation would allow to detect the recursion

to save the workitem and pass the parameter
        additionalParams = new HashSet();
        additionalParams.add(IExtensionsDefinitions.UPDATE_PARENT_DURATION_EXTENSION_ID);

       workItemServer.saveWorkItem3(parent, null, null,additionalParams);

to check for the parameter

 if (saveParameter.getAdditionalSaveParameters().contains( IExtensionsDefinitions.UPDATE_PARENT_DURATION_EXTENSION_ID))
        return;
    }

Your answer


Register or to post your answer.