Una novedad de Eclipse 3.0 son las API destinadas a gestionar y visualizar el estado de sincronización entre recursos del área de trabajo y los recursos de otra ubicación. Un recurso situado fuera del área de trabajo se conoce como variante. La sincronización es el acto de visualizar los cambios entre los recursos de diferentes ubicaciones y, opcionalmente, permitir al usuario influir sobre el estado de sincronización realizando una acción. Las API de sincronización son ortogonales con respecto a las API RepositoryProvider y pueden utilizarse sin un proveedor de repositorios. La finalidad de la API de sincronización consiste en facilitar la tarea de implementar diferentes formas de presentar el estado de sincronización de los recursos. Así, la API requiere un medio para consultar el estado de sincronización de los recursos, pero no un medio para influir sobre el estado. El medio para influir sobre el estado se deja al implementador (aunque la UI proporciona ganchos para añadir opciones de menú específicas del proveedor a los menús).
Antes de describir la API de sincronización, resultará útil presentar parte de la terminología y los conceptos que se aplican al describir la sincronización del área de trabajo.
Variante de recurso: puede hacerse referencia a un recurso local que se correlaciona con un recurso que existe en otra ubicación como variante de dicho recurso. Es decir, los recursos son generalmente muy similares, pero difieren ligeramente (debido a modificaciones realizadas en el recurso local o a cambios efectuados en la copia remota por otros usuarios). Tomamos como punto de referencia el área de trabajo local, y nos referimos a la copia local como recurso y a las copias remotas como variantes del recurso.
Sincronizar: se llama sincronizar a la acción de mostrar al usuario las diferencias entre variantes de recursos. La sincronización no afecta al estado de las variantes, sino que proporciona una vista que ayuda al usuario a entender las diferencias existentes entre conjuntos diferentes de variantes. Sin embargo, es habitual permitir que los usuarios influyan sobre los estados de las variantes (por ejemplo, permitiendo la reincorporación o la reversión) durante la sincronización.
Sincronización de dos y tres vías: existen dos tipos básicos de determinación del estado de sincronización: de dos vías y de tres vías. Una comparación de dos vías sólo tiene en cuenta el recurso local y una sola variante de recurso, conocida como variante de recurso remoto. Este tipo de comparación sólo puede mostrar las diferencias existentes entre dos recursos, pero no puede ofrecer indicios de la interrelación de los cambios. La mayoría de los sistemas de repositorio de código dan soporte a una comparación de tres vías para la determinación del estado de sincronización. Este tipo de comparación implica al recurso local, a una variante de recurso remoto y a una variante de recurso base. La variante de recurso base representa un ancestro común de los recursos local y remoto. Esto permite estados de sincronización más sofisticados que indican la dirección del cambio.
Tabla 1: Los estados de sincronización
Dos vías Tres vías Cambiado
Suprimido
AñadidoCambio saliente
Cambio entrante
Supresión saliente
Supresión entrante
Adición saliente
Adición entrante
Cambio en conflicto
Supresión en conflicto
Adición en conflicto
Las clases de org.eclipse.team.core.synchronize se utilizan para describir el estado de sincronización. La clase más importante es SyncInfo, ya que es la clase que define realmente el estado de sincronización. Puede utilizarse del siguiente modo:
SyncInfo info = getSyncInfo(resource); // este es un método simulado para obtener la información de sincronización de un recurso
int changekind = info.getKind();
if(info.getResourceComparator().isThreeWay()) {
if((changeKind & SyncInfo.DIRECTION_MASK) == SyncInfo.INCOMING) {
// realizar alguna acción
}
} else if(changeKind == SyncInfo.CHANGE) {
// realizar alguna otra acción
}
La clase SyncInfo proporciona las algoritmos de comparación de dos y tres vías y el cliente debe proporcionar los recursos y una clase que pueda comparar los recursos (IResourceVariantComparator). A continuación figura un ejemplo de comparador de variantes:
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(); // calcular la información de sincronización
Este paquete también contiene colecciones diseñadas específicamente para contener SyncInfo y filtros que pueden aplicarse a las instancias de SyncInfo.
Como hemos visto en los ejemplos anteriores, las clases SyncInfo y IResourceVariantComparator proporcionan acceso al estado de sincronización de los recursos. Pero lo que aún no hemos visto es cómo se gestiona el estado. Un Suscriptor proporciona acceso al estado de sincronización entre los recursos del área de trabajo local y a un conjunto de variantes de recurso de estos recursos mediante una comparación de dos o tres vías, dependiendo de la naturaleza del suscriptor. Un suscriptor proporciona las siguientes posibilidades:
Las API no definen cómo se crea un suscriptor; esta operación se deja a cargo de las implementaciones específicas. Por ejemplo, el conector CVS crea un suscriptor cuando se realiza una fusión, otro para una comparación y otro al sincronizar el área de trabajo local con la rama actual.
Por tanto, volvamos a revisar el primer ejemplo de utilización de SyncInfo y veamos cómo puede utilizarse un suscriptor para acceder a SyncInfo.
// Crear un suscriptor de sistema de archivos y especificar que el
// suscriptor se sincronizará con la ubicación del sistema de archivos suministrada
Subscriber subscriber = new FileSystemSubscriber("c:\temp\repo");
// Permitir que el suscriptor renueve su estado
subscriber.refresh(subscriber.roots(), IResource.DEPTH_INFINITE, monitor);
// Recoger todos los estados de sincronización e imprimir
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() + "no existe en el área de trabajo");
}
printSyncState(subscriber, children[i]);
}
}
El punto importante que debe recordarse es que el suscriptor conoce los recursos que no existen en el área de trabajo y que pueden devolverse recursos no existentes desde Subscriber#members() y SyncInfo#getLocal().
Podríamos emplear más tiempo en explicar cómo gestionar el estado de sincronización, pero en lugar de ello veremos cómo se muestra el estado al usuario. ISynchronizeParticipant es el componente de interfaz de usuario que visualiza el estado de sincronización y permite al usuario influir sobre él. La vista Sincronizar visualiza los participantes en la sincronización, pero también es posible mostrarlos en diálogos y asistentes. Para suministrar a los usuarios soporte de visualización de cualquier tipo de estado de sincronización, incluso aquellos que no estén basados en SyncInfo y suscriptores, un participante es un componente muy genérico.
También existe un punto de extensión denominado org.eclipse.team.ui.synchronizeWizards, que permite añadir un asistente de creación de sincronizaciones. Con ello, el asistente se colocará en la acción de sincronización global y en la vista Sincronizar, de forma que los usuarios podrán crear fácilmente una sincronización del tipo elegido.
Sin embargo, si ha implementado un suscriptor, puede aprovechar un participante concreto denominado SubscriberParticipant , que suministrará las siguientes funciones:
La mejor manera de explicar estos conceptos es utilizarlos en el contexto de un ejemplo sencillo. Diríjase al ejemplo de sincronización del historial local
para ver cómo pueden utilizarse conjuntamente estos componentes. O bien, si desea punteros relativos a la utilización de las API más avanzadas, diríjase a la sección Más allá de los elementos básicos.