Zasoby i system plików

Kiedy platforma jest uruchomiona, a wtyczka zasobów jest aktywna, obszar roboczy jest reprezentowany przez instancję interfejsu IWorkspace, który udostępnia protokół umożliwiający uzyskanie dostępu do zawartych w nim zasobów. Instancja interfejsu IWorkspace odpowiada kolekcji powiązanych ze sobą plików i katalogów w jednym lub kilku systemach plików. Dostęp do obszaru roboczego można uzyskać z poziomu klasy wtyczki zasobów (zdefiniowanej w pakiecie org.eclipse.core.resources).

   IWorkspace workspace = ResourcesPlugin.getWorkspace();

Jeśli wtyczka zasobów nie jest uruchomiona, obszar roboczy istnieje wyłącznie w lokalnym systemie plików, a użytkownik może go wyświetlać i manipulować nim przy użyciu standardowych narzędzi plikowych. Przy omawianiu interfejsu API wtyczki zasobów zaprezentowana zostanie również struktura obszaru roboczego na dysku.

Przykładowe drzewo na dysku

Po uruchomieniu pakietu SDK platformy została wyświetlona prośba o podanie katalogu workspace. Jest to katalog, w którym różne wtyczki przechowują istotne metadane związane z określoną instancją platformy. Domyślnie wtyczka zasobów przechowuje poszczególne projekty w podkatalogach katalogu workspace. Podkatalogi te zawierają foldery i pliki zawarte w poszczególnych projektach.

Przyjmijmy, że wybrany został katalog c:\pakiet_SDK\workspace. Wewnątrz tego katalogu znajdują się podkatalogi nazwane odpowiednio do projektów obszaru roboczego WWW i Serwlet. Katalogi te określa się mianem katalogów treści projektów. Katalogi treści są tworzone przez platformę w momencie tworzenia projektu przez użytkownika.

W każdym katalogu znajdują się pliki i foldery składające się na dany projekt i rozmieszczone dokładnie tak samo, jak w drzewie zasobów w obszarze roboczym. Wszystkie nazwy plików są takie same, identyczna jest również treść tych plików, niezależnie od tego, czy dostęp uzyskuje się z poziomu systemu plików czy też z obszaru roboczego. Jedynym wyjątkiem jest plik .project, który zostanie omówiony w dalszej części.

   C:\MySDK\workspace  (katalog główny obszaru roboczego)
      .metadata\ (katalog metadanych platformy)
      MyWeb\ (katalog treści projektu MyWeb)
	 .project
         index.html
         images\
            logo.png
      MyServlet\ (katalog treści projektu MyServlet)
	 .project
         src\
            main.java
         bin\
            main.class

Platforma ma specjalny katalog .metadata, w którym przechowywane są wewnętrzne informacje platformy. Katalog .metadata obszaru roboczego jest swego rodzaju "czarną skrzynką". Przechowywane są w nim ważne informacje dotyczące struktury obszaru roboczego, takie jak odwołania projektu lub właściwości zasobu. Dostęp do tych danych powinien być uzyskiwany wyłącznie przy użyciu odpowiednich narzędzi za pośrednictwem interfejsu API platformy.  Pliki te nie powinny być nigdy edytowane ani nie należy nimi manipulować przy użyciu ogólnego interfejsu API systemu plików.

Dodatkowo każdy projekt ma własny plik .project, w którym przechowywane są metadane dotyczące projektu. Plik ten stanowi w zasadzie przechowywany na dysku odpowiednik informacji znajdujących się w interfejsie IProjectDescription projektu.  

Oprócz katalogu .metadata oraz plików .project foldery i pliki w katalogu obszaru roboczego są dostępne do użytku dla innych narzędzi.  Plikami i folderami można manipulować przy użyciu narzędzi, które nie są zintegrowane z platformą, np. edytorów tekstu lub systemowych programów narzędziowych. Należy jedynie pamiętać o konieczności zachowania ostrożności podczas edytowania tych plików, tak w środowisku roboczym, jak i zewnętrznie (nie różni się to niczym od sytuacji, w której użytkownik edytuje plik za pomocą dwóch niezależnych narzędzi). W środowisku roboczym dostępne są operacje odświeżania, które umożliwiają uzgodnienie widoku zasobów w obszarze roboczym z ich faktycznym stanem w systemie plików. Ponadto oferowana jest opcja okresowego odświeżania obszaru roboczego w celu odzwierciedlenia stanu systemu plików.

Przykładowe drzewo w kodzie

Interfejs API zasobów umożliwia manipulowanie drzewem zasobów w kodzie. Przedstawionych zostanie tu kilka fragmentów kodu, które pozwolą uzyskać lepsze zrozumienie tego interfejsu API. Interfejs API zasobów jest definiowany w ramach zestawu interfejsów w pakiecie org.eclipse.core.resources. Dostępne są interfejsy dla wszystkich typów zasobów, takich jak IProject, IFolder czy IFile. Rozbudowany wspólny protokół jest zdefiniowany w interfejsie IResource. Użyty zostanie również interfejs IPath z pakietu org.eclipse.core.runtime, który reprezentuje ścieżki z podziałem na segmenty, na przykład ścieżki do zasobów oraz ścieżki systemu plików.

Manipulowanie zasobami przypomina manipulowanie plikami przy użyciu funkcji java.io.File. Interfejs API bazuje na uchwytach.  Użycie funkcji interfejsu API takiej jak getProject czy getFolder powoduje zwrócenie uchwytu zasobu.  Nie ma gwarancji, że dany zasób istnieje (ani nie jest to wymagane), dopóki użytkownik nie spróbuje użyć takiego uchwytu. Jeśli spodziewane jest istnienie zasobu, można skorzystać z metody exists, aby się co do tego upewnić.  

Aby nawigować w obszarze roboczym z poziomu wtyczki, należy najpierw uzyskać interfejs IWorkspaceRoot, który reprezentuje najwyższy poziom hierarchii zasobów w obszarze roboczym.

   IWorkspaceRoot myWorkspaceRoot = ResourcesPlugin.getWorkspace().getRoot();

Po uzyskaniu głównego poziomu obszaru roboczego można już uzyskać dostęp do projektów w obszarze roboczym.

   IProject myWebProject = myWorkspaceRoot.getProject("MyWeb");
   // w razie potrzeby otwarcie
   if (myWebProject.exists() && !myWebProject.isOpen())
      myWebProject.open(null);

Zanim możliwa będzie praca z projektem, należy go otworzyć. Otwarcie projektu powoduje odczytanie jego struktury z dysku i utworzenie w pamięci obiektu reprezentującego drzewo zasobów projektu. Otwarcie projektu to operacja o charakterze jawnym, ponieważ każdy otwarty projekt korzysta z pamięci w celu wewnętrznego przedstawienia drzewa zasobów, a otwarte projekty uczestniczą w różnych zdarzeniach związanych z cyklem życia zasobu (np. budowaniu), które mogą być czasochłonne. Nie można zasadniczo uzyskać dostępu do zamkniętych projektów - będą wyświetlane jako puste projekty, nawet jeśli odpowiadające im zasoby wciąż istnieją w systemie plików.

Należy zauważyć, że podczas manipulowania zasobami wiele z tych przykładowych zasobów przekazuje parametr null. Wiele operacji na zasobach może powodować znaczne obciążenie systemu, dlatego towarzyszy im raportowanie postępu i możliwość anulowania przez użytkownika. Jeśli tworzony kod dysponuje interfejsem użytkownika, przekazuje się zwykle interfejs IProgressMonitor, który pozwala wtyczce zasobów raportować postęp podczas pracy z zasobem i umożliwia w razie potrzeby anulowanie operacji przez użytkownika.  W tym przykładzie przekazana zostanie po prostu wartość null, oznaczającą brak monitora postępu.

Kiedy projekt jest już otwarty, można uzyskać dostęp do jego folderów i plików, a także tworzyć dodatkowe. W przedstawionym poniżej przykładzie utworzony zostanie zasób plikowy na bazie treści pliku znajdującego się poza obszarem roboczym.

   IFolder imagesFolder = myWebProject.getFolder("images");
   if (imagesFolder.exists()) {
      // utworzenie nowego pliku
      IFile newLogo = imagesFolder.getFile("newLogo.png");
      FileInputStream fileStream = new FileInputStream(
         "c:/MyOtherData/newLogo.png");
      newLogo.create(fileStream, false, null);
      // metoda create zamyka strumień pliku
   }

W powyższym przykładzie w pierwszym wierszu uzyskiwany jest uchwyt do folderu obrazów. Należy sprawdzić, czy folder istnieje (exists), zanim będzie można z nim pracować. Podobnie przy pobieraniu pliku newLogo uchwyt nie reprezentuje rzeczywistego pliku, dopóki plik nie zostanie utworzony w ostatnim wierszu.  W tym przykładzie plik tworzony jest przez zapełnienie go treścią pliku logo.png.

Następny fragment kodu jest podobny do poprzedniego. Różnica polega na tym, że zamiast tworzyć nowy plik na bazie treści starego, plik newLogo jest kopiowany z oryginalnego logo.

   IFile logo = imagesFolder.getFile("logo.png");
   if (logo.exists()) {
      IPath newLogoPath = new Path("newLogo.png");
      logo.copy(newLogoPath, false, null);
      IFile newLogo = imagesFolder.getFile("newLogo.png");
      ...
   }

Na koniec utworzony zostanie kolejny folder obrazów i przeniesiony zostanie do niego nowo utworzony plik. Efektem ubocznym przeniesienia pliku będzie zmiana jego nazwy.

   ...
   IFolder newImagesFolder = myWebProject.getFolder("newimages");
   newImagesFolder.create(false, true, null);
   IPath renamedPath = newImagesFolder.getFullPath().append("renamedLogo.png");
   newLogo.move(renamedPath, false, null);
   IFile renamedLogo = newImagesFolder.getFile("renamedLogo.png");

Wiele metod interfejsu API zasobów zawiera flagę boolowską force, która określa, czy zasoby niezsynchronizowane z odpowiadającymi im plikami w lokalnym systemie plików mają pomimo to zostać zaktualizowane. Więcej informacji na ten temat zawiera sekcja IResource. Można również użyć metody IResource.isSynchronized, aby ustalić, czy określony zasób jest zsynchronizowany z systemem plików.

Odwzorowywanie zasobów na położenia na dysku

W przykładowym drzewie zasobów założono, że wszystkie katalogi treści projektu znajdują się w katalogu workspace, poniżej katalogu głównego platformy (C:\MySDK\workspace). Jest to domyślna konfiguracja projektów. Katalog treści projektu można jednak odwzorować na dowolny katalog w systemie plików, nawet znajdujący się na innym komputerze.

Dzięki możliwości odwzorowania położenia wybranego projektu niezależnie od innych projektów użytkownik może przechowywać treść projektu w miejscu, które odpowiada charakterowi tego projektu i wymaganiom zespołu projektowego. Katalog treści projektu należy traktować jako "katalog otwarty". Oznacza to, że użytkownicy mogą tworzyć, modyfikować i usuwać zasoby przy użyciu środowiska roboczego i wtyczek lub bezpośrednio za pomocą narzędzi i edytorów operujących na systemie plików.

Nazwy ścieżek do zasobów nie są kompletnymi ścieżkami systemu plików. Ścieżki zasobów zawsze zależą od położenia projektu (zwykle jest to katalog workspace). Aby uzyskać pełną ścieżkę zasobu w systemie plików, należy wykonać zapytanie dotyczące jego położenia przy użyciu metody IResource.getLocationURI. Nie można jednak skorzystać z metody IProjectDescription.setLocation do zmiany położenia, ponieważ jest to po prostu metoda ustawiająca struktury danych.  

Z kolei aby uzyskać odpowiedni obiekt zasobu na podstawie ścieżki w systemie plików, można skorzystać z metody IWorkspaceRoot.findFilesForLocationURI lub IWorkspaceRoot.findContainersForLocationURI.

Interfejs API zasobów i system plików

Przy korzystaniu z interfejsu API zasobów do modyfikowania drzewa zasobów obszaru roboczego, oprócz aktualizacji obiektów zasobów modyfikowane są również pliki w systemie plików. A jak przedstawia się kwestia zmian w plikach zasobów wykonywanych poza interfejsem API platformy?

Zmiany zasobów dokonywane zewnętrznie nie zostaną odzwierciedlone w obszarze roboczym i obiektach zasobów, dopóki nie zostaną wykryte przez wtyczkę zasobów. Wtyczka ta korzysta również z mechanizmu odpowiedniego dla każdego rodzimego systemu operacyjnego w celu wykrycia zewnętrznych zmian wprowadzonych w systemie plików. Dodatkowo klienci mogą korzystać z interfejsu API zasobów do uzgadniania obszaru roboczego i obiektów zasobów z lokalnym systemem plików dyskretnie i bez konieczności interwencji ze strony użytkownika. Użytkownik może również jawnie wymusić odświeżanie w widoku nawigatora zasobów w środowisku roboczym.

Wiele z metod interfejsów API zasobów zawiera parametr force, który określa sposób obsługi zasobów niezsynchronizowanych z systemem plików. Szczegółowe informacje na temat tego parametru można znaleźć w informacjach dodatkowych na temat interfejsów API dla poszczególnych metod. Dodatkowe metody w interfejsie API umożliwiają programowe sterowanie funkcją odświeżania systemu plików, na przykład IResource.refreshLocal(int depth, IProgressMonitor monitor). Informacje na temat poprawnej składni i kosztów zawiera sekcja IResource.

W wtyczkach, które mają dostarczać własny mechanizm okresowego odświeżania obszaru roboczego na podstawie stanu zewnętrznego systemu plików, można skorzystać z punktu rozszerzenia org.eclipse.core.resources.refreshProviders. Więcej informacji na ten temat zawiera sekcja Dostawcy odświeżania.