Repository providers
A repository provider (
RepositoryProvider
)
is the central class in the implementation of your
repository. This class is responsible for configuring a project
for repository management and providing the necessary hooks for resource
modification. Providers are mapped to a project using project
persistent properties. The mechanism for mapping providers to
a project is not central to the team API, but you'll need to be aware of
it when filtering out resources in your UI. For the most part, you'll be
using team API to work with projects and associate them with your provider.
To implement a provider, you must define a repository using
org.eclipse.team.core.repository
and supply a class derived from
RepositoryProvider
.
We'll use the CVS client as an example to see how this
works.
Extension point
The
org.eclipse.team.core.repository
extension point is used to
add a repository definition. Here is the markup for the CVS client.
<extension
point="org.eclipse.team.core.repository">
<repository
class="org.eclipse.team.internal.ccvs.core.CVSTeamProvider"
id="org.eclipse.team.cvs.core.cvsprovider">
</repository>
</extension>
This registers your team provider with the team support plug-in and
assigns
an id that should be used when your provider is associated with a
project. The specified class for the repository must extend
RepositoryProvider
.
Implementing a RepositoryProvider
The class identified in the extension must be a subclass of
RepositoryProvider
.
Its primary responsibilities are to configure and deconfigure a project
for
repository support, and supply any necessary resource modification
hooks.
The CVS client serves as a good example. Its repository provider
is CVSTeamProvider.
public class CVSTeamProvider extends RepositoryProvider {
...
RepositoryProvider
defines two abstract methods, configureProject and
deconfigure. All providers must implement these methods.
A project is configured
when it is first associated with a particular repository
provider. This
typically happens when the user selects a project and uses the team
wizards to
associate a project with your repository. Regardless of how the
operation
is triggered, this is
the appropriate time to compute or cache any data about the project
that you'll
need to provide your repository function. (Assume that mapping
the project
to your provider has already happened. You'll be taking care of
this in
your configuration wizard.)
The
CVS provider simply broadcasts the fact that a project has been
configured:
public void configureProject() throws CoreException {
CVSProviderPlugin.broadcastProjectConfigured(getProject());
}
We won't follow the implementation of the plug-in broadcast
mechanism. Suffice to say that any parties that need to compute
or initialize project specific data can do so at this time.
A project is deconfigured when the user no longer wants to associate a team
provider with a project. It is up to your plug-in to implement the user
action that causes this to happen (and unmapping the project from your team
provider will happen there). The deconfigure method is the
appropriate time to delete any project related caches or remove any
references to the project in the UI. The CVS provider flushes project
related caches kept in its views and broadcasts the fact that the project is
deconfigured.
public void deconfigure() throws CoreException {
...
try {
EclipseSynchronizer.getInstance().flush(getProject(), true, true /*flush deep*/, null);
} catch(CVSException e) {
throw new CoreException(e.getStatus());
} finally {
CVSProviderPlugin.broadcastProjectDeconfigured(getProject());
}
}
Configuring a project
Typically, the first step in building a team UI is implementing a
wizard page that allows users to configure a project for your plug-in's team
support. This is where your team provider's id will be added to
the project's properties. You participate in project configuration by
contributing to the
org.eclipse.team.ui.configurationWizards
extension point. This wizard is shown when the user chooses
Team > Share Project....
We'll look at this in the context of the CVS client implementation.
Here is the CVS UI markup for its configuration wizard:
<extension
point="org.eclipse.team.ui.configurationWizards">
<wizard
name="%SharingWizard.name"
icon="icons/full/wizards/newconnect_wiz.png"
class="org.eclipse.team.internal.ccvs.ui.wizards.SharingWizard"
id="org.eclipse.team.ccvs.ui.SharingWizard">
</wizard>
</extension>
As usual, plug-ins supply a class that implements the
extension and a
unique id to identify their extension. The name
and icon
are shown in the first page of the project configuration wizard if
there are
multiple providers to choose from.
Once the user has selected a provider, the next page shows the
specific
configuration information for your provider. (If your provider is
the only
team provider plug-in installed, then the wizard skips directly to your
page.) Your wizard must implement
IConfigurationWizard,
which initializes the wizard for a specified workbench and
project. The rest of the implementation depends on the design of your wizard.
You must gather up any information needed to associate the project with your
team support.
When the wizard is completed, you must map your team provider to
the project
using
RepositoryProvider.map(IProject,
String)
. Mapping handles the assignment of the correct
project persistent property to your project.
The CVS client does this work in its provider's setSharing
method, which is called when its wizard is finished:
public void setSharing(IProject project, FolderSyncInfo info, IProgressMonitor monitor) throws TeamException {
// Ensure provided info matches that of the project
...
// Ensure that the provided location is managed
...
// Register the project with Team
RepositoryProvider.map(project, CVSProviderPlugin.getTypeId());
}
Finding a provider
Static methods in
RepositoryProvider
make it easy for clients to map projects to providers and to find the providers
associated with a given project.
-
map(IProject, String) - instantiates a provider of the
specified provider id and maps the specified project to it. This
call sets the proper project persistent property on the project.
-
unmap(IProject, String) - removes the association of the
specified provider id from the specified project. Leaves the
project unassociated with any team provider.
-
getProvider(IProject) - answers the provider for a given
project. Can be used to find any team provider for a
project.
-
getProvider(IProject, String) - answers the provider for
a given project with the specified provider id. Can be used to
check whether a particular team provider type is associated with a
given project. It is commonly used by providers to quickly check
whether a given project is under their care. This call is safer
for clients since it does not return a provider that does not match the
client's id.
Repository Providers and Capabilities
If a product chooses to add a Repository plug-in to a capability, it
should bind the capability to the repository id. Here are the two steps
to take to enable a RepositoryProvider as a capability:
- Bind the capability to the repository provider id. This allows the
Team plug-in to activate/disable based on repository provider ids.
<activityPatternBinding
activityId="org.eclipse.team.cvs"
pattern="org\.eclipse\.team\.cvs\.core/.*cvsnature">
</activityPatternBinding>
- Next bind the capability to all UI packages for the provider:
<activityPatternBinding
activityId="org.eclipse.team.cvs"
pattern="org\.eclipse\.team\.cvs\.ui/.*">
</activityPatternBinding>
There are two capability triggers points defined by the Team plug-ins.
The first is the Team > Share
Project... wizard which allows filtering of repository providers
based on the enabled/disabled state of workbench capabilities, and the
other is the Team plug-in auto-enablement trigger.
Resource modification hooks
Most of the interesting function associated with a repository
provider occurs as the user works with resources in the project that is
configured for the provider. In order to be aware of changes the
user makes to a resource, the provider can implement resource modification hooks. The
resources plug-in provides these hooks as extension points. The
documentation for
IMoveDeleteHook
,
FileModificationValidator
and
ResourceRuleFactory
describe the details for implementing these hooks.
The team plug-in optimizes and simplifies the association of the
hook with
appropriate resources by registering generic hooks with the resources
plug-in. These generic hooks simply look up the repository
provider for a
given resource and obtain its hook. This has the advantage of
calling only
one provider hook rather than having each provider implementation
register a
hook that must first check whether the resource is managed by the
provider.
What this means to your plug-in is that you provide any necessary
hooks by overriding methods in
RepositoryProvider
.
The default implementation of these methods answers null,
indicating that no hook is necessary (except for the resource rule
factory, as described below):
-
getMoveDeleteHook - answers an
IMoveDeleteHook
appropriate for the provider. This hook allows providers to
control how moves and deletes occur and includes the ability to prevent
them from happening. Implementors can provide alternate
implementations for moving or deleting files, folders, and
projects. The CVS client uses this hook to monitor folder
deletions and ensure that any files contained in deleted folders are
remembered so that they can later be deleted from the repository if
desired.
-
getFileModificationValidator2 - answers an
FileModificationValidator
appropriate for the provider. This hook allows providers to
pre-check any modifications or saves to files. This hook is
typically needed when a repository provider wants to implement pessimistic
versioning. In pessimistic versioning, a file must be checked
out before modifying it, and only one client can check out a file at
any given time. Pessimistic versioning could be implemented by
checking out a file (if not already checked out) whenever a file is
edited, and checking the file back in when it is saved. CVS uses
this hook when using the watch/edit mode, but by default CVS
uses an optimistic versioning scheme that does not require this hook.
-
getRuleFactory - answers a resource rule factory
appropriate for the provider. Providers should always override this
method as the default factory locks the workspace for all operations
for backwards compatibility reasons. Provides should subclass
ResourceRuleFactory
and override those rules required to ensure that the proper rules are
obtained for operations that invoke the move/delete hook and file
modification validator. The rule methods of particular interest to
repository providers are:
-
deleteRule - move/delete hook
-
moveRule -move/delete hook
-
validateEditRule - file modification validator
validateEdit
-
modifyRule - file modification validator validateSave