Suporte de Sincronização

O Eclipse inclui APIs para a gestão e apresentação de um estado de sincronização entre recursos do espaço de trabalho e recursos noutra localização. Chamamos variante a um recurso fora do espaço de trabalho. A sincronização é o acto de apresentar as alterações entre recursos em diferentes lugares e, opcionalmente, permitir ao utilizador afectar o estado de sincronização mediante uma acção. As APIs de sincronização são ortogonais relativamente às APIs do RepositoryProvider e podem ser utilizadas sem um fornecedor de repositórios. O objectivo da API de sincronização consiste em facilitar a tarefa de implementar diferentes formas de apresentar o estado de sincronização dos recursos. Como tal, a API precisa de um meio de consultar o estado de sincronização dos recursos mas não de um meio para afectar esse estado. O meio para afectar esse estado compete ao implementador (embora a UI faculte ganchos para adicionar artigos de menu específicos dos fornecedores aos menus).

Terminologia

Antes de descrevermos a API de sincronização, será útil apresentar alguma terminologia e conceitos que se aplicam à abordagem da sincronização do espaço de trabalho.

Variante de Recurso: Podemos chamar variante de recurso a um recurso local que esteja correlacionado com um recurso que exista noutro lugar. Significa isto que os recursos geralmente são muito semelhantes mas podem diferir ligeiramente (quer devido a modificações ao recurso local quer a alterações efectuadas à cópia remota por outros utilizadores). Vemos esta situação como centrada no espaço de trabalho, em que a cópia local é o recurso e a eventual cópia remota é a variante do recurso.

Sincronizar: Chamamos sincronizar à acção de apresentar ao utilizador as diferenças entre variantes de recurso. A sincronização não afecta o estado das variantes, pois faculta uma vista para ajudar o utilizador a compreender as diferenças entre os diversos conjuntos de variantes. Porém, é comum permitir aos utilizadores afectar os estados das variantes (p.ex., dar entrada ou reverter) enquanto decorre a sincronização.

Sincronização bidireccional versus tridireccional: Há dois tipos básicos de determinar estados de sincronização: bidireccional e tridireccional. Uma comparação bidireccional considera somente o recurso local e uma única variante de recurso, denominada variante de recurso remota. Este tipo de comparação só pode mostrar as diferenças entre os dois recursos mas não pode dar pistas da interrelação dessas alterações. A maioria dos sistemas de repositórios de código suporta comparação tridireccional para determinar estados de sincronização. Este tipo de comparação implica o recurso local, uma variante de recurso remota e uma variante de recurso base. A variante de recurso base representa um antepassado comum dos recursos local e remoto. Isto permite estados de sincronização mais sofisticados que indicam a direcção da alteração.

Tabela 1: Os estados de sincronização

Bidireccional Tridireccional
Alterado
Eliminado
Adicionado
Alteração a partir
Alteração a chegar
Eliminação a partir
Eliminação a chegar
Adição a partir
Adição a chegar
Alteração em conflito
Eliminação em conflito
Adição em conflito

Conceitos Básicos - SyncInfo

As classes em org.eclipse.team.core.synchronize são utilizadas para descrever o estado de sincronização. A classe mais importante é SyncInfo porque é a classe que define realmente o estado de sincronização. Pode ser usada como se segue:

SyncInfo info =
getSyncInfo(resource); // this is a simulated method of obtaining the sync info
for a resource
int changekind = info.getKind();
if(info.getResourceComparator().isThreeWay()) {
if((changeKind & SyncInfo.DIRECTION_MASK) == SyncInfo.INCOMING) {
// do something
}
} else if(changeKind == SyncInfo.CHANGE) {
// do something else
}

A classe SyncInfo faculta os algoritmos de comparação bidireccional e tridireccional e o cliente deve facultar os recursos e uma classe que possa comparar os recursos (IResourceVariantComparator). De seguida é apresentado um comparador de variantes exemplo:

public class TimestampVariantComparator
implements IResourceVariantComparator {	
protected boolean compare(IResourceVariant e1, IResourceVariant e2) {
if(e1.isContainer()) {
if(e2.isContainer()) {
return true;
}
return false;
}
if(e1 instanceof MyResourceVariant && e2 instanceof MyResourceVariant) {
MyResourceVariant myE1 = (MyResourceVariant)e1;
MyResourceVariant myE2 = (MyResourceVariant)e2;
return myE1.getTimestamp().equals(myE2.getTimestamp());
}
return false;
}
protected boolean compare(IResource e1, IResourceVariant e2) {

}
public boolean isThreeWay() {
return true;
}
}

SyncInfo info = new SyncInfo(resource, variant1, variant2, new TimestampComparator());
info.init(); // calculate the sync info

Este pacote também contém recolhas especificamente concebidas para conter SyncInfo e filtros que possam ser aplicados a instâncias SyncInfo.

Gerir o estado de sincronização

Como vimos nos exemplos supra, as classes SyncInfo e IResourceVariantComparator facultam acesso ao estado de sincronização dos recursos. Mas ainda não vimos como gerir o estado. Um Subscriber (subscritor) faculta acesso ao estado de sincronização entre os recursos no espaço de trabalho local e um conjunto de variantes destes recursos, com uma comparação bidireccional ou tridireccional, consoante a natureza do subscritor. Um subscritor faculta as seguintes capacidades:

As APIs não definem o modo de criação de um Subscritor, pois tal compete às implementações específicas. Por exemplo, o plug-in CVS cria um Subscritor quando se realiza uma intercalação, outro para uma comparação e ainda outro ao sincronizar o espaço de trabalho local com a actual ramificação.

Voltemos assim ao primeiro exemplo da utilização de SyncInfo e vejamos como se pode usar o Subscritor para aceder a SyncInfo.

// Create a file system subscriber
and specify that the
// subscriber will synchronize with the provided file system location
Subscriber subscriber = new FileSystemSubscriber("c:\temp\repo");

// Allow the subscriber to refresh its state
subscriber.refresh(subscriber.roots(), IResource.DEPTH_INFINITE, monitor);

// Collect all the synchronization states and print
IResource[] children = subscriber.roots();
for(int i=0; i < children.length; i++) {
printSyncState(children[i]);
}

...

void printSyncState(Subscriber subscriber, IResource resource) {
System.out.println(subscriber.getSyncInfo(resource).toString());
IResource[] children = subscriber.members(resource);
for(int i=0; i < children.length; i++) {
IResource child = children[i];
if(! child.exists()) {
System.out.println(resource.getFullPath() + " doesn't exist in the workspace");
}
printSyncState(subscriber, children[i]);
}
}

O importante a recordar é que o Subscritor conhece recursos que não existem no espaço de trabalho e que podem ser devolvidos recursos inexistentes de Subscriber#members() e SyncInfo#getLocal().

Apresentar o estado de sincronização na UI

Poderíamos explicar mais como gerir estados de sincronização mas vamos em contrapartida ver como obter realmente o estado mostrado ao utilizador. Um ISynchronizeParticipant e o componente de interface de utilizador que apresenta o estado de sincronização e permite ao utilizador afectar este estado. A vista Sincronizar apresenta participantes na sincronização, mas também é possível mostrá-los em caixas de diálogo e assistentes. A fim de facultar suporte para os utilizadores mostrarem algum tipo de estado de sincronização ao utilizador, mesmo aqueles não baseados em SyncInfo e Subscritores, um participante é um componente muito genérico.

Também há um ponto de extensão chamado org.eclipse.team.ui.synchronizeWizards para adicionar um assistente de criação de sincronizações. Isto coloca o assistente na acção de sincronização global e na vista Sincronizar, para que os utilizadores possam criar facilmente uma sincronização do seu tipo.

Contudo, se tiver implementado um Subscritor, poderá beneficiar de um participante concreto chamado SubscriberParticipant , o qual proporciona as seguintes funcionalidades:

A melhor forma de explicar estes conceitos é vê-los em utilização no contexto de um simples exemplo. Avance para o exemplo sincronização do histórico local para ver como se podem utilizas estas peças todas juntas. Se preferir pistas para a utilização das APIs mais avançadas, siga para Além dos Conceitos Básicos.