Zmienianie zasobów metodą wsadową

Gdy zachodzi konieczność modyfikacji zasobów w obszarze roboczym, należy pamiętać, że inne moduły dodatkowe mogą również korzystać z tych samych zasobów. Interfejs API zasobów udostępnia stabilne mechanizmy, które informują moduły dodatkowe o zmianach dokonywanych w zasobach oraz pilnują, aby wiele modułów dodatkowych nie dokonywało zmian w danym zasobie w tym samym czasie. Zmiany dokonywane w obszarze roboczym przez moduły dodatkowe, o ile to możliwe, powinny zostać zgrupowane w jednostkach pracy wewnątrz elementu wykonywalnego środowiska roboczego. Te elementy wykonywalne pomagają zmniejszyć liczbę powiadomień generowanych przez zmiany. Pozwalają również zadeklarować część obszaru roboczego, w której zostanie dokonana zmiana, aby pozostałe moduły dodatkowe nie mogły jej zmieniać.

Protokół interfejsu IWorkspaceRunnable jest całkiem prosty. Element wykonywalny wygląda tak, jak długotrwała operacja lub zadanie platformy. Główna część pracy jest wykonywana wewnątrz metody run, a postęp jest raportowany do dostarczonego obiektu IProgressMonitor. Kod zmieniający obszar roboczy jest wykonywany wewnątrz metody run.

IWorkspaceRunnable myRunnable = 
	new IWorkspaceRunnable() {
		public void run(IProgressMonitor monitor) throws CoreException {
			//tu jest wykonywana właściwa praca
			...
		}
}

Gdy kod ma zostać uruchomiony, moduł dodatkowy przekazuje polecenie uruchomienia kodu obszarowi roboczemu. W ten sposób obszar roboczy może wygenerować wszystkie niezbędne zdarzenia zmian i zapewnić, że dwa moduły dodatkowe nie zmienią tego samego zasobu w tym samym czasie. (Nawet jeśli dany moduł dodatkowy nie używa do zmiany obszaru roboczego zadań w tle ani środowisk współbieżnych, inne moduły dodatkowe mogą tak robić).

Reguły planowania i blokowanie

Do uruchomienia elementu wykonywalnego obszaru roboczego używany jest protokół interfejsu IWorkspace. Preferowaną techniką jest użycie długiej forma metody run, która dostarcza regułę planowania i określa sposób rozgłaszania zdarzeń zmian zasobu.

Określenie reguły planowania w momencie uruchamiania elementu wykonywalnego obszaru roboczego umożliwia sprawdzenie, czy zmiany zasobów dokonywane przez obszar nie spowodują konfliktu ze zmianami wykonywanymi w innych wątkach. (Omówienie reguł planowania oraz protokołu interfejsu ISchedulingRule zawiera sekcja Reguły planowania). Na szczęście protokół interfejsu IResource zawiera protokół interfejsu ISchedulingRule, co oznacza, że dany zasób może być często regułą planowania dla samego siebie.

Czy coś jest niejasne? W tym momencie kod może pomóc wyjaśnić sprawę. Załóżmy, że dany moduł dodatkowy przygotowuje się do zmiany grupy zasobów w konkretnym projekcie. Jako reguły planowania dla dokonywanych zmian może użyć samego projektu. Poniższy fragment kodu uruchamia utworzony wcześniej element wykonywalny obszaru roboczego:

   IWorkspace workspace = ResourcesPlugin.getWorkspace();
workspace.run(myRunnable, myProject, IWorkspace.AVOID_UPDATE, null);

Do obszaru roboczego przekazywany jest element wykonywalny, a zaraz za nim projekt, który jest zmieniany przez kod. To jest informacja dla obszaru roboczego, że wszystkie zmiany w elemencie wykonywalnym ograniczają się do elementu myProject. Wszystkie żądania zmian w elemencie myProject z innych wątków zostaną zablokowane do czasu zakończenia działania tego elementu wykonywalnego. To wywołanie zostanie także zablokowane, jeśli inny wątek będzie w trakcie dokonywania zmian w elemencie myProject. Określając część drzewa zasobu, która będzie modyfikowana przez element wykonywalny, można umożliwić innym wątkom kontynuowanie modyfikowania pozostałych części obszaru roboczego. Ważne jest, aby się upewnić, że reguła zasobu jest zgodna z pracą wykonywaną wewnątrz elementu wykonywalnego. Próba uzyskania dostępu do zasobu spoza zasięgu reguły planowania wyzwoli wyjątek.

Trzeci parametr metody run określa, czy w okresie danego wywołania mają być rozgłaszane jakiekolwiek okresowe zdarzenia zmiany zasobu. Użycie parametru IWorkspace.AVOID_UPDATE nakazuje platformie pominięcie wszelkich zdarzeń zmiany zasobu w trakcie wykonywania elementu wykonywalnego i rozgłoszenie jednego zdarzenia po zakończeniu wprowadzania zmian. Wszystkie elementy wykonywalne utworzone w trakcie tego wywołania w danym elemencie wykonywalnym będą traktowane jako część nadrzędnej operacji wsadowej. Zmiany zasobów dokonane przez te elementy wykonywalne pojawią się w powiadomieniu o zmianie zasobu tej operacji nadrzędnej.

Fabryka reguł zasobów

W powyższym przykładzie zakładaliśmy, że kod wewnątrz elementu wykonywalnego zmienia zasoby tylko w konkretnym projekcie. To złożenie bardzo ułatwiło określenie reguły planowania dla elementu wykonywalnego. W praktyce może być dużo trudniej stwierdzić, na które części obszaru roboczego wpłynie konkretna zmiana. Na przykład przenoszenie zasobu między projektami wpływa na każdy z nich. Do obliczenia odpowiedniej reguły zasobu dla konkretnego rodzaju zmian zasobu można użyć interfejsu IResourceRuleFactory. Fabrykę reguł zasobów można uzyskać z obszaru roboczego.

   IWorkspace workspace = ResourcesPlugin.getWorkspace();
IResourceRuleFactory ruleFactory = workspace.getRuleFactory();

Fabryka może dostarczyć odpowiednie reguły dla operacji różnego rodzaju. Jeśli element wykonywalny przenosi zasób z jednego położenia do drugiego, to może otrzymać regułę odpowiednią dla tej operacji:

ISchedulingRule movingRule = ruleFactory.moveResource(sourceResource, destinationResource);
workspace.run(myRunnable, movingRule, IWorkspace.AVOID_UPDATE, null);

Listę dostępnych reguł zawiera dokumentacja javadoc dotycząca interfejsu IResourceRuleFactory. Moduł dodatkowy zasobów używa tych reguł do implementowania większości operacji związanych z zasobami. Sposób używania tych metod reguł w praktyce można poznać przeglądając kod, który się do nich odwołuje.

Kilka reguł można połączyć przy użyciu klasy MultiRule.

ISchedulingRule movingRule = ruleFactory.moveResource(sourceResource, destinationResource);
ISchedulingRule modifyRule = ruleFactory.modifyResource(destinationResource);
workspace.run(myRunnable, MultiRule.combine(movingRule, modifyRule), IWorkspace.AVOID_UPDATE, null);

Ignorowanie reguł

W interfejsie IWorkspace dostępna jest również krótka forma metody run. Została zachowana w celu zagwarantowania kompatybilności wstecznej. Ta krótka forma nie zawiera reguły ani flagi aktualizacji.

workspace.run(myRunnable, null);

Powyższy zapis jest równoważny zapisowi wywołania:

workspace.run(myRunnable, workspace.getRoot(), IWorkspace.AVOID_UPDATE, null);

Określenie elementu głównego obszaru roboczego jako reguły planowania zablokuje cały obszar roboczy do momentu zakończenia działania elementu wykonywalnego. To jest najbardziej konserwatywna metoda aktualizowania obszaru roboczego, która jest mało przyjazna dla innych współbieżnych modułów dodatkowych.