It's all about the answers!

Ask a question

What other options do I have to setWorkflowAction other than relying on WorkitemOperation?


Andy Jewell (24225468) | asked Jan 07 '14, 11:49 a.m.
edited Jan 13 '14, 12:18 p.m.
The javadoc API says:

commit(WorkItemWorkingCopy[] workingCopies, org.eclipse.core.runtime.IProgressMonitor monitor) 
          Commits the operation for the given working copies.

But I don't see any examples in the Jazz biosphere that actually use it.  I'm having a problem where my code to update a workitem runs with (apparently) no issue, but I'm not seeing the updates reflected on the work items.

Specifically, I'm trying to extend WorkItemOperation to change the status of a custom work item type.  I can change other fields using the same code but when I try to change the workflow action, it doesn't work.  Here is what I'm using:

public class UpdateWorkItemStatus implements WorkitemResultProcessor {
private static Logger log = LoggerFactory
.getLogger(UpdateWorkItemStatus.class.getName());
String newaction ="";
IProgressMonitor monitor = null;
IProjectArea project_area = null;

public UpdateWorkItemStatus(IProjectArea pa,String statusaction,IProgressMonitor mon){
project_area = pa;
monitor = mon;
newaction = statusaction;
mon.subTask("Preparing to update");
}

public IProgressMonitor getMonitor() {
return monitor;
}
public String getNewaction() {
return newaction;
}
public void execute(IWorkItemHandle wh) throws RtcException {
StatusAssign chgstatus = new StatusAssign(newaction);
try {
log.trace("Creating StatusAssign object to update status");
chgstatus.run(wh,monitor);
} catch (TeamRepositoryException e) {
throw new RtcException("Problem setting status to '"+getNewaction()+"': "+e.getMessage());
}
}

class StatusAssign extends WorkItemOperation {
String newstatus_action = "";
public StatusAssign(String action) {
super("Updating workitem...", IWorkItem.FULL_PROFILE);
newstatus_action = action;
}
public StatusAssign(String name, ItemProfile profile) {
super(name, profile);
}
@Override
protected void execute(WorkItemWorkingCopy workingCopy,IProgressMonitor monitor) throws TeamRepositoryException {
String msg = String.format("Updating workitem %s: setting status to '%s'",workingCopy.getWorkItem().getId(),newstatus_action);
monitor.subTask(msg);
log.debug("Current workflow action is {}",workingCopy.getWorkflowAction());
                        // this workflow action does not stick
workingCopy.setWorkflowAction(newstatus_action);
// this part works
Timestamp testdate = new Timestamp(new Date().getTime());
workingCopy.getWorkItem().setDueDate(testdate);
                        // this part works, too
workingCopy.getWorkItem().setTarget(IterationHelper.getIteration(
(ITeamRepository) project_area.getOrigin(), (IProjectArea) project_area.getFullState()
, "HP MBE O 2013 11 04"));
}
}
}
Because it hasn't been working, I've been doing more and more arcane things trying to get it to work, including the forbidden setState2 and now wondering what the commit is for.  If the problem is that I'm doing something wrong, then my original question is wrong! 


Any ideas?


- Andy

Comments
Aradhya K commented Jan 08 '14, 12:29 a.m.
JAZZ DEVELOPER

How are you setting the value for the work item??

If you set the value in the OperationParticipant then then the values will not be persisted.


Andy Jewell commented Jan 10 '14, 1:36 p.m.

Sorry, I'm using the plain java API.  I will add more details to the question. 

Accepted answer


permanent link
Ralph Schoon (57.0k23642) | answered Jan 13 '14, 11:45 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER
Won't fit in a comment. This would be another approach:
	private static String changeStatus(ITeamRepository repo,
			Integer workItemId, Boolean buildStatus, String actionOK,
			String actionKO, IProgressMonitor monitor)
			throws TeamRepositoryException {

		IWorkItemClient workItemClient = (IWorkItemClient) repo
				.getClientLibrary(IWorkItemClient.class);
		IWorkItemCommon workItemCommon = (IWorkItemCommon) repo
				.getClientLibrary(IWorkItemCommon.class);

		IWorkItem wi = workItemClient.findWorkItemById(workItemId,
				IWorkItem.FULL_PROFILE, monitor);
		if (null == wi) {
			return ("work item " + workItemId + " cannot be found");
		}
		IDetailedStatus status = null;
		String actionName = buildStatus ? actionOK : actionKO;
		IWorkItemWorkingCopyManager copyManager = workItemClient
				.getWorkItemWorkingCopyManager();

		try {
			copyManager.connect(wi, IWorkItem.FULL_PROFILE, monitor);
			WorkItemWorkingCopy wc = copyManager.getWorkingCopy(wi);
			// wc.getWorkItem().getsetState2(arg0)
			IWorkflowInfo wfInfo = (IWorkflowInfo) workItemClient
					.findWorkflowInfo(wi, monitor);
			Identifier[] actionList = wfInfo.getActionIds(wi
					.getState2());
			String actionId = null;
			for (int i = 0; i < actionList.length; i++) {
				Identifier action = actionList[i];
				String name = wfInfo.getActionName(action);
				if (name != null) {
					if (name.equals(actionName)) {
						actionId = action.getStringIdentifier();
						break;
					}
				}
			}
			if (null == actionId) {
				wc.setWorkflowAction(null);
			} else {
				wc.setWorkflowAction(actionId);
				// wc.setWorkflowAction(null);
			}
			status = wc.save(monitor);
			if (status.getCode() != org.eclipse.core.runtime.IStatus.OK) {
				return "Error: " + status.getDetails();
			}
		} catch (Exception e) {
			return "Unexpected error: " + e.getMessage();
		} finally {
			copyManager.disconnect(wi);
		}
		return null;
	}
Andy Jewell selected this answer as the correct answer

Comments
Andy Jewell commented Jan 13 '14, 11:49 a.m.

Thanks, Ralph, will try that right now.  On thing I've been wondering about (since setWorkflowAction(string) was not working for me), is the action name the simple/display name of the action or is it some other underlying stringified name of the action Identifier? 


Ralph Schoon commented Jan 13 '14, 11:58 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER

I would suggest to read https://rsjazz.wordpress.com/2012/11/26/manipulating-work-item-states/ to understand how that works and how to get the action.


Andy Jewell commented Jan 13 '14, 12:11 p.m.

Yes, I have that read but actually you can't tell from the article nor any other examples I've read what the literal action name is that's being passed.


However, it's a moot point because your last sample worked for me!  I'm a bit disappointed because the WorkitemOperation approach is so much cleaner but the nice thing about this approach is. .. that it works!  Really appreciate your help, Ralph!!


1
Ralph Schoon commented Jan 13 '14, 12:19 p.m. | edited Jan 13 '14, 12:20 p.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER

As far as I can see from jumping through my blog entry it is the ID of the action. That is not necessarily the same as the display name (as usually) especially for the shipped templates. You would have to look into the process XML, unfortunately. Here an example screen shot:



I am not sure if I created the path helper you can download on the blog to accept the name instead. should be easy enough to to though. Please also note, the action will only work if you are in a state where this action is valid.


Ralph Schoon commented Jan 13 '14, 12:27 p.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER

If the last code snippet works, the operation should also work. The code below is (if you look at the WorkitemOpetration source code the same code essentially WorkItemOperation runs. The first example is, I believe, a work item operation that does exactly what you want. I think I created it as an example for exactly that purpose. If it does not work, there can be reasons as
- WorkflowAction not found (passed the name not the ID)
- WorkflowAction not applicable in this state
- Another operational behavior that prevents you from changing the state.

I would be surprised if there are other reasons why it should not work.


Andy Jewell commented Jan 13 '14, 1:25 p.m.

This is great, info, thank you.  I guess this code works because of the looping done to associate the action name with the action identifier whereas in my original WorkItemOperation code, I was just passing the action name.  I will try updating the WorkItemOperation code with the ID and see if it works.


Andy Jewell commented Jan 13 '14, 1:38 p.m. | edited Jan 13 '14, 1:47 p.m.

 Bahdaboom, that worked :)  I'm going to file a documentation bug on this, that was a bit frustrating but thanks for your help!


Ralph Schoon commented Jan 14 '14, 1:42 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER

Why would you? This is all over the place. You can never use the name of anything. You have to find the ID. Enumeration Literals https://rsjazz.wordpress.com/2012/08/20/manipulationg-work-item-enumeration-values/ attributes https://rsjazz.wordpress.com/2013/01/02/working-with-work-item-attributes/, wherever you look at.

One of the reasons is the API is the API used for developing the client or the server and they know the ID's and only display the display name for convenience.


Andy Jewell commented Jan 14 '14, 10:58 a.m.

I disagree, having the API reader assume a string argument is an ID, a name, a fully resolve classname, an alias, or whatever other string type is obvious is not a good practice for someone writing an API.  It's a simple change and has already been made.


Ralph Schoon commented Jan 14 '14, 11:04 a.m. | edited Jan 14 '14, 11:05 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER

Well, you can create your enhancement request.

However, as far as I am aware, the API is not explicitely written as an API for an external user, it is the internal API exposed. And in that case display names, that can be in Chinese, or German or French, or any other language, and that can be changed by the user, are not a good, reliable identifier. Therefore other identifiers, that are more constant are used all over the place. There are other issues with identifiers that don't necessarily have to be unique as well.

showing 5 of 10 show 5 more comments

3 other answers



permanent link
Ralph Schoon (57.0k23642) | answered Jan 13 '14, 3:02 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER
Andy, looking at it, the commit operation basically is one of the last steps in the WorkItemOperation and it saves the work item working-copies. That is about it.

If you want to change the state of the work item,you can provide the work item with a workflowAction ( .setWorkFlowAction). See http://rsjazz.wordpress.com/2012/11/26/manipulating-work-item-states/ for more details. There is a deprecated method .setState2() on IWorkItem that you could use to directly set a state. However, that is a bit unsafe, because you could trigger a state change that does not exist and it might also avoid all the behavior defined related to the workflow.

Comments
Andy Jewell commented Jan 13 '14, 11:06 a.m.

Yes, thanks, Ralph, I am using setWorkflowAction but unfortunately, it's not working and I can't figure out why.  I'll have to try setState2.


Ralph Schoon commented Jan 13 '14, 11:35 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER

Where do you use the setWorkflowAction in the operation? This code used to work for me:

    private static class WorkItemSetWorkflowActionModification extends
            WorkItemOperation {

    private String fWorkFlowAtion;

    public WorkItemSetWorkflowActionModification(String workFlowAtion) {
        super("Modifying Work Item State", IWorkItem.FULL_PROFILE);
        fWorkFlowAtion = workFlowAtion;
    }

    @Override
    protected void execute(WorkItemWorkingCopy workingCopy,
            IProgressMonitor monitor) throws TeamRepositoryException {
        workingCopy.setWorkflowAction(fWorkFlowAtion);
    }

    public void setfWorkFlowAtion(String fWorkFlowAtion) {
        this.fWorkFlowAtion = fWorkFlowAtion;
    }
}

permanent link
Ralph Schoon (57.0k23642) | answered Jan 08 '14, 3:51 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER
Andy, it would help if you describe what and where (client API/Server API) you are trying to use the API.

I only implicitly use that API. On the client I usually use a WorkItemOperation that overwrites some method as described for example here: https://rsjazz.wordpress.com/2012/08/01/uploading-attachments-to-work-items/ or here https://jazz.net/wiki/bin/view/Main/ProgrammaticWorkItemCreation. I think that eventually uses the commit() method.

In the server API I use typically saveWorkItem2() or saveWorkItem3() as described in e.g. this blog: https://rsjazz.wordpress.com/2012/07/31/rtc-update-parent-duration-estimation-and-effort-participant/

Comments
Andy Jewell commented Jan 10 '14, 1:37 p.m.

Yes, those articles are pretty much the basis of everything I've done... :)  Will add more details to the original question. 


permanent link
Andy Jewell (24225468) | answered Jan 13 '14, 1:41 p.m.
 Updated WorkitemOperation that finds the correct action identifier based on the passed name.  Not using the path as Ralph mentions above, just brute force:
@Override
protected void execute(WorkItemWorkingCopy workingCopy,IProgressMonitor monitor) throws TeamRepositoryException {
    String msg = String.format("Updating workitem %s: setting status to '%s'",workingCopy.getWorkItem().getId(),newstatus_action);
    monitor.subTask(msg);
    log.debug("Current workflow action is {}",workingCopy.getWorkflowAction());
    ITeamRepository repo = (ITeamRepository) workingCopy.getWorkItem().getOrigin();
    IWorkItemClient client = (IWorkItemClient) repo.getClientLibrary(IWorkItemClient.class);
    IWorkflowInfo wf = client.findWorkflowInfo(workingCopy.getWorkItem(), monitor);
    Identifier<IWorkflowAction>[] actionList = wf.getActionIds(workingCopy.getWorkItem().getState2());
    String actionId = null;
    for (int i = 0; i < actionList.length; i++) {
    Identifier<IWorkflowAction> action = actionList[i];
    String action_name = wf.getActionName(action);
    if (action_name != null) {
      if (action_name.equals(newstatus_action)) {
      actionId = action.getStringIdentifier();
      break;
      }
    } else {
      log.error("identifier for action {} was null",newstatus_action);
    }
    }
    workingCopy.setWorkflowAction(actionId);
  }
}

Your answer


Register or to post your answer.