Cuando sea necesario modificar recursos en el espacio de trabajo, es importante recordar que otros plug-ins pueden estar trabajando con los mismos recursos. La API de recursos proporciona mecanismos robustos para mantener a los plug-ins informados acerca de los cambios realizados en el espacio de trabajo y para garantizar que varios plug-ins no modifiquen el mismo recurso simultáneamente. Cuando sea posible, las modificaciones realizadas por el plug-in en el espacio de trabajo deben procesarse por lotes en unidades de trabajo dentro de un ejecutable de espacio de trabajo. Estos ejecutables ayudan a reducir la cantidad de notificaciones de cambio generadas por los cambios. También permiten declarar qué parte del espacio de trabajo debe modificarse, a fin de evitar que otros plug-ins cambien la misma parte del espacio de trabajo.
El protocolo de IWorkspaceRunnable es bastante sencillo. Un ejecutable de espacio de trabajo tiene el mismo aspecto que una operación de larga ejecución o un trabajo de plataforma. El trabajo real se ejecuta dentro de un método run, y el progreso se notifica al IProgressMonitor suministrado. El código que manipula el espacio de trabajo se ejecuta dentro del método run.
IWorkspaceRunnable myRunnable = new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { //realizar aquí el trabajo real ... } }
Cuando llega el momento de ejecutar el código, el plug-in indica al espacio de trabajo que ejecute el código en su nombre. De este modo, el espacio de trabajo puede generar los eventos de cambio necesarios y asegurarse de que no haya dos plug-ins que modifiquen el mismo recurso simultáneamente. (Aunque el plug-in no utilice trabajos de segundo plano y la infraestructura de concurrencia para modificar el espacio de trabajo, puede que otros plug-ins lo estén haciendo).
El protocolo IWorkspace se utiliza para ejecutar un ejecutable de espacio de trabajo. La técnica preferida consiste en utilizar el formato largo del método run, que suministra una norma de planificación y especifica cómo se transmiten los eventos de cambio de recurso.
La especificación de una norma de planificación al ejecutar un ejecutable de espacio de trabajo permite a esta determinar si los cambios de recurso entrarán en conflicto con los cambios de espacio de trabajo producidos en otras hebras. (Consulte la sección Normas de planificación para obtener una visión general de las normas de planificación y del protocolo ISchedulingRule). Afortunadamente, el protocolo IResource incluye el protocolo para ISchedulingRule, lo que significa que un recurso puede utilizarse con frecuencia como norma de planificación para sí mismo.
¿Confundido? El código puede ayudar a clarificar este punto. Supongamos que el plug-in se está preparando para modificar un grupo de recursos de un proyecto determinado. Puede utilizar el propio proyecto como norma de planificación para realizar los cambios. El siguiente fragmento de código ejecuta el ejecutable de espacio de trabajo que hemos creado anteriormente:
IWorkspace workspace = ResourcesPlugin.getWorkspace(); workspace.run(myRunnable, myProject, IWorkspace.AVOID_UPDATE, null);
El ejecutable se pasa al espacio de trabajo, seguido del proyecto que el código está manipulando. Esto índica al espacio de trabajo que todos los cambios del ejecutable se encuentran en myProject. Las peticiones de cambio de myProject realizadas por otras hebras quedarán bloqueadas hasta que finalice este ejecutable. Del mismo modo, esta llamada quedará bloqueada si alguna otra hebra ya está modificando myProject. Al especificar qué parte del árbol de recursos modificará el ejecutable, se permite a otras hebras continuar modificando otras partes del espacio de trabajo. Es importante asegurarse de que la norma de recurso coincide con el trabajo realizado dentro del ejecutable. Cuando se utiliza una regla de planificación no nula, cualquier intento de acceder a un recurso situado fuera del ámbito de la norma de planificación desencadenará una excepción.
Hay dos reglas de planificación especiales que es importante tener en cuenta. En primer lugar, si utiliza una instancia de IWorkspaceRoot como la regla de planificación, significa que la hebra está bloqueando el acceso a todos los recursos del árbol. Mientras que una hebra alberga la regla raíz, ninguna otra hebra puede modificar el espacio de trabajo. A la inversa, una regla null indica que la hebra no bloqueará el acceso a los recursos del árbol. Mientras que una hebra con una regla nula puede modificar el espacio de trabajo, otras hebras no podrán realizar sus propias modificaciones. Las reglas de planificación IWorkspaceRoot y null ocupan extremos opuestos del espectro de simultaneidad. Con IWorkspaceRoot no hay simultaneidad y solo hay una hebra que modifique el espacio de trabajo. Con una regla null, la simultaneidad es máxima por que todas las hebras pueden modificar el espacio de trabajo a la vez.
El tercer parámetro del método run especifica si los eventos de cambio de recurso periódico deben transmitirse durante el ámbito de esta llamada. La utilización de IWorkspace.AVOID_UPDATE indica a la plataforma que suprima los eventos de cambio de recurso mientras el ejecutable se esté ejecutando y que transmita un evento al final de los cambios. Durante esta llamada, los demás ejecutables creados en el ejecutable se considerarán parte de la operación por lotes padre. Los cambios de recurso efectuados en estos ejecutables aparecerán en la notificación de cambio de recurso del padre.
En el ejemplo anterior, se presuponía que el código del ejecutable sólo modificaba recursos de un proyecto determinado. Esto facilitaba mucho la especificación de una norma de planificación para el ejecutable. En la práctica, puede ser más difícil calcular qué partes del espacio de trabajo resultan afectadas por un cambio determinado. Por ejemplo, el movimiento de un recurso de un proyecto a otro afecta a ambos proyectos. Puede utilizarse IResourceRuleFactory para facilitar el cálculo de una norma de recurso adecuada para determinados tipos de cambios de recursos. Puede obtener una fábrica de normas de recurso del propio espacio de trabajo.
IWorkspace workspace = ResourcesPlugin.getWorkspace(); IResourceRuleFactory ruleFactory = workspace.getRuleFactory();
La fábrica puede suministrar normas adecuadas para muchos tipos de operaciones. Si el ejecutable mueve un recurso de una ubicación a otra, puede obtener una norma adecuada para esta operación:
ISchedulingRule movingRule = ruleFactory.moveResource(sourceResource, destinationResource); workspace.run(myRunnable, movingRule, IWorkspace.AVOID_UPDATE, null);
Consulte el javadoc de IResourceRuleFactory para obtener la lista de normas disponibles. El plug-in de recursos utiliza estas normas para implementar la mayoría de operaciones de recursos. El examen del código que hace referencia a estos métodos de norma ayudará a mostrar cómo se utilizan en la práctica.
Pueden combinarse varias normas mediante MultiRule.
ISchedulingRule movingRule = ruleFactory.moveResource(sourceResource, destinationResource); ISchedulingRule modifyRule = ruleFactory.modifyResource(destinationResource); workspace.run(myRunnable, MultiRule.combine(movingRule, modifyRule), IWorkspace.AVOID_UPDATE, null);
También está disponible el formato corto del método run en IWorkspace. Se conserva a efectos de compatibilidad hacia atrás. El formato corto no incluye una norma ni un indicador de actualización.
workspace.run(myRunnable, null);
es en realidad lo mismo que llamar a
workspace.run(myRunnable, workspace.getRoot(), IWorkspace.AVOID_UPDATE, null);
Al especificar el directorio raíz del espacio de trabajo como norma de planificación, se colocará un bloqueo sobre todo el espacio de trabajo hasta que finalice el ejecutable. Esta es la forma más conservadora de realizar una actualización del espacio de trabajo, pero no es muy adecuada para otros plug-ins pensados para la concurrencia.