When you need to modify resources in the workspace, it is important to keep in mind that other plug-ins might be working with the same resources. The resources API provides robust mechanisms for keeping plug-ins informed about changes in the workspace, and for making sure that multiple plug-ins do not modify the same resource at the same time. Where possible, your plug-in's modifications to the workspace should be batched in units of work inside a workspace runnable. These runnables help to reduce the amount of change notifications generated by changes. They also allow you to declare which part of the workspace is to be modified, so that other plug-ins can be locked out of changing the same part of the workspace.
The protocol for IWorkspaceRunnable
is fairly simple. A workspace runnable looks just like a long-running operation or platform job. The actual work is done inside
a run method, with progress reported to the supplied
IProgressMonitor. Code that
manipulates the workspace is performed inside the run method.
IWorkspaceRunnable myRunnable =
new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
//do the actual work in here
...
}
}
When it is time to the run the code, your plug-in tells the workspace to run the code on its behalf. This way, the
workspace can generate any necessary change events and ensure that no two plug-ins are modifying the same resource
at the same time. (Even if your plug-in is not using background jobs and the concurrency framework to modify the
workspace, other plug-ins may be doing so.)
IWorkspace protocol is used to run a workspace runnable. The preferred technique is using the long form of the run method which supplies a scheduling rule and specifies how resource change events are broadcast.
Specifying a scheduling rule when running a workspace runnable allows the workspace to determine whether the resource changes will conflict with workspace changes happening in other threads. (See Scheduling rules for an overview of scheduling rules and ISchedulingRule protocol.) Fortunately, IResource protocol includes the protocol for ISchedulingRule, which means that a resource can often be used as a scheduling rule for itself.
Confused? Code can help to clarify this point. Suppose your plug-in is getting ready to modify a bunch of
resources in a particular project. It can use the project itself as the scheduling rule for making the changes.
The following snippet runs the workspace runnable that we created earlier:
IWorkspace workspace = ResourcesPlugin.getWorkspace();
workspace.run(myRunnable, myProject, IWorkspace.AVOID_UPDATE, null);
The runnable is passed to the workspace, followed by the project that the code is manipulating. This tells the
workspace that all of the changes in the runnable are confined to myProject. Any requests by other threads
to change myProject will be blocked until this runnable completes. Likewise, this call will block if some
other thread is already modifying myProject. By specifying which part of the resource tree will be modified by
the runnable, you are allowing other threads to continue modifying other portions of the workspace. It is important
to be sure that your resource rule matches the work being done inside the runnable. Any attempt to access a
resource outside the scope of the scheduling rule will trigger an exception.
The third parameter to the run method specifies whether any periodic resource change events should be broadcast during the scope of this call. Using IWorkspace.AVOID_UPDATE tells the platform to suppress any resource change events while the runnable is running and to broadcast one event at the end of the changes. During this call, any other runnables created in the runnable will be considered part of the parent batch operation. Resource changes made in those runnables will appear in the parent's resource change notification.
In the example above, we assumed that the code inside our runnable only modified resources in a particular project.
This made it very easy to specify a scheduling rule for the runnable. In practice, it can be more difficult
to compute what parts of the workspace are affected by a particular change. For example, moving a resource from one
project to another affects both projects. IResourceRuleFactory
can be used to help compute an appropriate resource rule for certain kinds of resource changes. You can get
a resource rule factory from the workspace itself.
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IResourceRuleFactory ruleFactory = workspace.getRuleFactory();
The factory can supply rules appropriate for many kinds of operations. If your runnable is moving a resource
from one location to another, it can obtain a rule appropriate for this operation:
ISchedulingRule movingRule = ruleFactory.moveResource(sourceResource, destinationResource);
workspace.run(myRunnable, movingRule, IWorkspace.AVOID_UPDATE, null);
See the javadoc for IResourceRuleFactory
for the list of available rules. The resources plug-in uses these rules itself to implement most resource operations.
Browsing the code that references these rule methods will help demonstrate how they are used in practice.
Multiple rules can be combined using
MultiRule.
ISchedulingRule movingRule = ruleFactory.moveResource(sourceResource, destinationResource);
ISchedulingRule modifyRule = ruleFactory.modifyResource(destinationResource);
workspace.run(myRunnable, MultiRule.combine(movingRule, modifyRule), IWorkspace.AVOID_UPDATE, null);
The short form of the run method in
IWorkspace
is also available. It is retained for backward compatibility. The short form does not include a rule or an update
flag.
workspace.run(myRunnable, null);
is effectively the same as calling
workspace.run(myRunnable, workspace.getRoot(), IWorkspace.AVOID_UPDATE, null);
Specifying the workspace root as the scheduling rule will put a lock on the entire workspace until the runnable
is finished. This is the most conservative way to perform a workspace update, but it is not very friendly to
other concurrency-minded plug-ins.