Temat serializacji obiektów w języku Java jest z pozoru trywialny : wystarczy zaimplementować malutki interfejsik – i gotowe! Czasem jednak to, co wydaje się tak oczywiste, przysparza inżynierom pracy na wiele dni…
Za większością publikacji, więc również i za tą, kryje się głębsza historia opowiadająca o pewnym systemie informatycznym zaimplementowanym w języku Java, który można opisać nośnymi przymiotnikami, takimi jak „skalowalny”, „rozproszony”, „wielowarstwowy” itd.
Jak powszechnie wiadomo (a jeżeli nie, to od tej chwili będzie), inżynierowie, którzy rozpraszają systemy
, bardzo często stosują mechanizmy związane z replikacją danych. Takim mechanizmem jest z całą pewnością rozproszony bufor (cache) obiektów Java i to na nim będziemy się teraz skupiać.
Stosując mechanizmy buforowania zawsze dbamy o to, aby obiekty były „serializowalne”, ponieważ zawartość bufora może być zapisywana również na dyski lub do baz danych. W przypadku bufora rozproszonego (tzn. takiego, który dba o to aby każdy z systemów miał taką samą zawartość bufora) obiekty są przesyłane przez sieć, co również wymusza serializację.
Tyle tytułem wstępu – teraz konkrety.
Implementacja
- Rozwijamy system lokalnie (np. serwer aplikacji na własnej stacji roboczej).
- Od początku zakładamy stosowanie rozproszonego bufora.
- Implementujemy złożoną strukturę obiektów (wiele poziomów dziedziczenia, liczne asocjacje).
- Implementujemy serializowalność obiektów, które chcemy buforować.
- Testujemy wszystko, bufory są wypełniane, odczyt i zmiany są możliwe – działa!
Skalowanie
- Uruchamiamy napisany system na kilku maszynach.
- Testujemy wszystko, bufory są wypełniane, odczyt i zmiany są możliwe i… klops
– bufor nie synchronizuje się prawidłowo z innymi maszynami w sieci.
Ratowanie
- Gdzie popełniliśmy błąd ???
- Może to implementacja bufora jest niedojrzała – ma wiele błędów, które nie zostały poprawione przez producenta?
- Nie działa replikacja i tyle – pewnie trzeba zainstalować nową wersję bufora [standardowa akcja inżyniera numer 1
]. - Sprawdzamy: wszystko, co chcemy trzymać w cache, implementuje java.io.Serializable – czyli jest ok.
Problem
- Szperamy w logach i w kodzie (duuużo klas, konfiguracji itp. , itd.) i… tu pojawia się pytanie: dlaczego błędy dotyczą klas, które dziedziczą po innych ?
Więc tak na prawdę co poszło nie tak? Popatrzmy na fragment pseudokodu Java:
Zaprojektowaliśmy klasy, z których jedną (Bomber) chcemy zapisywać do bufora:
class Airplane {
public String name;
public Integer enginesCount;
}
class Bomber extends Airplane implements Serializable{
public Integer bombCount;
}
Teraz zapiszemy je do pliku mechanizmami dostępnymi w J2SE:
Bomber b = new Bomber();
b.name = "Sztukas";
b.enginesCount = 1;
b.bombCount= 4;
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("bombowiec.obiekt"));
out.writeObject(b);
out.close();
Skoro już zapisaliśmy obiekt, to teraz go odczytajmy i wypiszmy w konsoli, co tam „siedzi”:
ObjectInputStream in = new ObjectInputStream(new FileInputStream("bombowiec.obiekt"));
Bomber b = (Bomber) in.readObject();
in.close();
System.out.println("name="+b.name);
System.out.println("enginesCount="+b.enginesCount);
System.out.println("bombCount="+b.bombCount);
Oto co pokazała konsola:
name=null
enginesCount=null
bombCount=4
Czy to ten sam bombowiec? Oczywiście nie, a co zrobić, żeby to był „nasz”?
Proszę bardzo, modyfikujemy projekt naszych samolotów:
class Airplane implements Serializable {
public String name;
public Integer enginesCount;
}
class Bomber extends Airplane {
public Integer bombCount;
}
Jest różnica? Proszę sprawdzić własnoręcznie.
Uhm, przecież jaka ta Java jest prosta…
Autor: Daniel Ramotowski
