Suporte a Sincronização

Novas no Eclipse 3.0 são as APIs para gerenciar e exibir o estado de sincronização entre os recursos do espaço de trabalho e os recursos em outro local. Referimo-nos a um recurso fora do espaço de trabalho como uma variante. Sincronizar é o ato de exibir as alterações entre os recursos em locais diferentes e opcionalmente permitir que o usuário afete o estado de sincronização pela realização de uma ação. As APIs de sincronização são perpendiculares às APIs do RepositoryProvider e podem ser utilizadas sem um fornecedor do repositório. A finalidade da API de sincronização é facilitar a tarefa de implementar maneiras diferentes de apresentar o estado de sincronização dos recursos. Assim, a API requer um meio de consultar o estado de sincronização dos recursos, mas não requer um meio de afetar o estado. Os meios de afetar o estado são deixados para o implementador (embora a UI forneça ganchos para incluir itens de menu específicos do fornecedor nos menus).

Terminologia

Antes que a API da sincronização seja descrita, é útil apresentar um pouco da terminologia e dos conceitos que são aplicados ao discutir a sincronização do espaço de trabalho.

Variante de Recurso: Um recurso local que é mapeado para um recurso que existe em outro local pode ser chamado de variante desse recurso. Ou seja, os recursos são normalmente muito semelhantes, mas são levemente diferentes (por causa de modificações no recurso local ou alterações feitas por outros usuários na cópia remota). Fazemos uma visualização central do espaço de trabalho local, utilizando como referência a cópia local como o recurso e qualquer cópia remota como as variantes de recurso.

Sincronizar: Referimo-nos a sincronizar como a ação de exibir para o usuário as diferenças entre as variantes de recurso. Sincronizar não afeta o estado das variantes, mas em vez disso , fornece uma visualização para ajudar o usuário a entender as diferenças entre diferentes conjuntos de variantes. No entanto, é comum permitir que os usuários afetem os estados das variantes (por exemplo, permitindo o registro de entrada ou reverter) ao sincronizar.

Sincronização de Duas mãos vs. Três mãos: Há dois tipos básicos de determinação do estado de sincronização: duas ou três mãos. Uma comparação de duas mãos considera somente o recurso local e uma variante de recursos única, chamada de variante de recursos remota. Esse tipo de comparação pode mostrar somente as diferenças entre os dois recursos, mas não pode oferecer sugestões sobre como as alterações estão interrelacionadas. A maioria dos sistemas repositórios de código suporta uma comparação de três mãos para determinação do estado de sincronização. Esse tipo de comparação envolve o recurso local, uma variante de recurso remota e uma variante de recurso base. A variante de recurso base representa um ascendente comum para os recursos locais e remotos. Isso permite estados de sincronização mais sofisticados que indicam a direção da alteração.

Tabela 1: Os estados de sincronização

Duas Mãos Três Mãos
Alterado
Excluído
Incluído
Alteração de Saída
Alteração de Entrada
Exclusão de Saída
Exclusão de Entrada
Inclusão de Saída
Inclusão de Entrada
Alteração em Conflito
Exclusão em Conflito
Inclusão em Conflito

Os Fundamentos - SyncInfo

As classes no org.eclipse.team.core.synchronize são utilizadas para descrever o estado de sincronzação. A classe mais importante é SyncInfo porque é a classe que, na verdade, define o estado de sincronização. Pode ser utilizado da seguinte maneira:

SyncInfo info = getSyncInfo(resource); // este é um método simulado de obter as informações de sincronização para um recurso
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 fornece algoritmos de comparação de duas ou três mãos, um cliente deve fornecer os recursos e uma classe que possa comparar os recursos (IResourceVariantComparator). Aqui está um exemplo de comparador variante:

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 coletas especificamente projetadas para conter SyncInfo e filtros que podem ser aplicados às instâncias SyncInfo.

Gerenciando o Estado de Sincronização

Como vimos nos exemplos acima, as classes SyncInfo e IResourceVariantComparator fornecem acesso ao estado de sincronização de recursos. Mas o que não vimos ainda é como o estado é gerenciado. Um Assinante fornece acesso ao estado de sincronização entre os recursos no espaço de trabalho local e um conjunto de variantes de recurso para estes recursos utilizando uma comparação de duas ou três mãos, dependendo da natureza do assinante. Um assinante fornece os seguintes recursos:

As APIs não definem como um Assinante é criado, isso é deixado para as implementações específicas. Por exemplo, o plug-in do CVS cria um Assinante quando uma mesclagem é desempenhada, outro para uma comparação e outro ao sincronizar o espaço de trabalho local com a ramificação atual.

Vamos rever nosso primeiro exemplo de utilização de SyncInfo e ver como um Assinante poderia ser utilizado para acessar SyncInfo.

// Crie um assinante do
sistema de arquivos e especifique se o 
// assinante sincronizará com o local do sistema de arquivo fornecido
Subscriber subscriber = new FileSystemSubscriber("c:\temp\repo");

// Permita que o assinante atualize seu estado
subscriber.refresh(subscriber.roots(), IResource.DEPTH_INFINITE, monitor);

// Colete todos os estados de sincronização e imprima
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]);
}
}

Um ponto importante a ser lembrado é que o Assinante sabe sobre os recursos que não existem no espaço de trabalho e os recursos não existentes podem ser retornados de Subscriber#members() e SyncInfo#getLocal().

Exibindo o Estado de Sincronizações na UI

Poderíamos passar mais tempo explicando como gerenciar o estado de sincronização, mas em vez disso vamos ver como mostrar o estado para o usuário. Um ISynchronizeParticipant é o componente da interface do usuário que exibe o estado de sincronização e permite que o usuário afete seu estado. A Visualização de Sincronização exibe os participantes da sincronização, mas também é possível mostrar isso em diálogos e assistentes. Para fornecer suporte para os usuários e mostrar qualquer tipo de estado de sincronização para o usuário, mesmo os que não tem SyncInfo e Assinantes como base, um participante é um componente muito genérico.

Há também um ponto de extensão chamado org.eclipse.team.ui.synchronizeWizards para incluir um assistente de criação de sincronização. Isso colocará seu assistente na ação de sincronização global e na Visualização de Sincronização para que os usuários possam criar facilmente uma sincronização do seu tipo.

No entanto, se você implementou um Assinante, você pode se beneficiar de um participante concreto chamado SubscriberParticipant que fornecerá a seguinte funcionalidade:

A melhor maneira de explicar esses conceitos é vê-los utilizados no contexto de um exemplo simples. Vá para o exemplo de sincronização de histórico local para verificar como todos esses pedaços podem ser utilizados juntos. Ou se você deseja ponteiros sobre como utilizar APIs mais avançadas, vá para Além dos Fundamentos.