Dlaczego migracje projektów Liferayowych na wyższą wersje nie są proste cz. 2

Przez Rafał Pydyniak | 2021-01-06

Witam! W pierwszej części Dlaczego migracje projektów Liferayowych na wyższą wersje nie są proste przedstawiłem jak mniej więcej w ogóle wyglądają migracje w projektach Liferayowych oraz pokazałem kilka przykładów potencjalnych problemów, które mogą nas spotkać przy migracji bazy danych. W tej części postaram się pokazać kilka problemów, które mogą się pojawić podczas migrowania samego kodu czyli naszych customowych modułów m.in. portletów.

Jak migrować kod

Ale na początku - jak w ogóle zabrać się za migracje kodu? No więc zacznijmy od tego, że jeśli zdecydowałeś się rozpocząć migracje swojego projektu to punktem startowym powinna być oficjalna dokumentacja. Przede wszystkim jest kilka ścieżek i przykładowo inaczej migruje się projekt 6.x (lub wcześniejszy) a inaczej projekt, którego wersja już jest w wersji 7.x i tylko chcemy podnieść wersje do najnowszej. Ogólne zasada oczywiście jest taka, że im wyżej jesteśmy tym łatwiej będzie zmigrować. Jednocześnie im więcej mamy kodu, który wykorzystuje API Liferaya tym więcej problemów może nas spotkać. Niemniej jednak polecam rozpocząć właśnie od dokumentacji bo nakreśli ona schemat działania. Oczywiście niektóre rzeczy można by przedstawić inaczej i prawdopodobnie schemat migracji można by zmienić, aczkolwiek nie o tym ma być ten wpis.

Błędy, które mogą nas spotkać.

No więc co takiego złego może nas spotkać podczas migracji? Zdecydowanie największym problemem podczas migracji kodu jest to, że niestety nie wszystkie zmiany są udokumentowane (chociażby w tak zwanych "Breaking changes"), a jeśli są to nie wystarczająco dobrze. Co w takim razie możemy zrobić? Niestety zostaje nam przejść przez migracje oraz uczyć się na bieżąco. Oczywiście na wiele z problemów znajdziemy jakieś rozwiązania, sami też możemy zapytać na forum, a jeśli mamy wersje DXP to nawet próbować z oficjalnym wsparciem. Niemniej jednak musimy być na to gotowi. Z dobrych wiadomości mam to, że kilka z takich rzeczy (zarówno drobnostek jak i tych bardziej uciążliwych) postaram się przedstawić poniżej, tak abyście mogli się uczyć na cudzych (moich) błędach.

Uwaga! Błędy te znalazłem jakiś czas temu. Może w momencie czytania (a nawet publikacji tego postu) niektóre z tych rzeczy już są w breaing changes - nie sprawdzałem tego. Niemniej jednak pisałem w swoim czasie kilka postów na forum Liferay więc kto wie, może zostało coś dodane.

Zmiana API do pobierania zdjęć w Web Contentach

Zacznijmy od tego jak wyglądał wpis XML dla zdjęcia w Web Content:

<dynamic-content language-id="en_US" alt="" name="XXX.png" title="XXX.png" type="document" [url=http://fileEntryId="12345"><![CDATA[/documents/111/222/XXX.png/QQQQQQ-WWWW-EEEE-CCCC-DDDDDDD?t=1588696601862]]></dynamic-content>

Natomiast dla porównania 7.2:

<dynamic-content [url=http://language-id="en_US"><![CDATA[{"groupId":"126435","alt":"","name":"XXX.png","title":"XXX.png","type":"journal","uuid":"QQQQ-WWW-EEE-CCC-ec48068bbf39","fileEntryId":"716022","resourcePrimKey":"716460"}]]></dynamic-content>

Jaka jest różnica? No więc kluczowy jest brak ścieżki pliku. Zamiast tego mamy JSON'a z kilkoma informacjami o naszym zdjęciu. Jaki w tym problem? No więc taki, że częstym podejściem w 7.0 było pobieranie ścieżki do zdjęcia w następujący sposób:

final com.liferay.portal.kernel.xml.Document document = SAXReaderUtil.read(article.getContentByLocale(locale.toString()));

final String imageUrl = document.valueOf(``"//dynamic-element[@name='Image']/dynamic-content"``);

W wersji 7.2 robiąc w ten sposób kończymy z pobraniem JSON'a, który następnie ląduje w naszym SRC:

JSON in image SRC

Aby do tego nie dopuścić musimy zmienić nasz kod na następujący (przykładowo):

final com.liferay.portal.kernel.xml.Document document = SAXReaderUtil.read(article.getContentByLocale(locale.toString())); final String imageJsonString = document.valueOf("//dynamic-element[@name='Image']/dynamic-content"); JSONObject imageJsonObject = JSONFactoryUtil.createJSONObject(imageJsonString); FileEntry fileEntry = PortletFileRepositoryUtil.getPortletFileEntry(imageJsonObject.getLong("fileEntryId")); String imageUrl = PortletFileRepositoryUtil.getDownloadPortletFileEntryURL(serviceContext.getThemeDisplay(), fileEntry, StringPool.BLANK);

Analogiczna zamiana może być potrzebna np. w naszym templacie napisanym w FTL, aczkolwiek przełożenie powyższego kodu Javowego nie powinno być problemem.

Warto zobaczyć ten wpis na blogu Liferaya, w którym możemy wyczytać, że zmiana ta została przeprowadzona bo developerzy sądzili, że nikt z tego i tak nie korzysta. Niestety brakuje także lepszej alternatywy w postaci np. API dzięki, któremu programiści w ogóle nie musieliby się przejmować takimi szczegółami implementacyjnymi i wspomniane rozwiązanie wielokrotnie było sugerowane jako najlepsze.

Zmiana pól w Search API

W Liferayu 7.2 czy też 7.1 nastąpiła zmiana w nazewnictwie pól w Search API. Zmiana polega na tym, że w 7.0 pole w klasie

com.liferay.portal.kernel.search.Document

Wyglądało mniej więcej tak:

ddm__keyword__328630__PublicationDate_en_US_String_sortable

W wyżej wersji natomiast wygląda tak:

ddm__keyword__328630__PublicationDate_String_sortable

Różnica? Fragment _en_US. Oczywiście ma to pewien sens - nie tłumaczymy raczej pola PublicationDate i stąd niepotrzebne rozróżnienie. Niemniej jednak może się okazać, że wyszukując w naszym np. portlecie używamy tego fragmentu w jakimś kontekście i nasz kod przestanie działać poprawnie. Słowa kluczowe dla wyszukiwania tego typu problemów to "indexSearchHelper", "Document" czy "SearchContext" a przykładowy fragment kodu może wyglądać tak:

final BooleanQuery publicationDateQuery = new BooleanQueryImpl(); publicationDateQuery.addRangeTerm(String.format(PUBLICATION_DATE_TERM_FIELD_PATTERN, newsDescriptionStructureId), sdf.format(from), sdf.format(to)); indexSearcherHelper.search(searchContext, publicationDateQuery).getDocs()

Gdzie nasz pattern to:

private static final String PUBLICATION_DATE_TERM_FIELD_PATTERN = "ddm__keyword__%s__PublicationDate_%s";

Czyli możemy wyszukiwać po nazwie pola (patrz addRangeTerm) i podawać tam nazwę pola wraz z naszym fragmentem odnośnie języka. Sam kod nadal będzie działać i nie dostaniemy żadnych błędów natomiast rezultat będzie inny gdyż wyszukujemy po nazwie pola, która nie istnieje (bo została zmieniona).

Błąd ten jest pewnie z tych rzadszych bo Search API nie jest pewnie aż tak popularne niemniej jednak może być ciężki do znalezienia ze względu na brak jakichkolwiek komunikatów o błędzie.

Uprawnienia do podglądu Webcontentów

Kolejna zmiana, której nie doszukałem się w breaking changes.

Zdjęcie widoku uprawnień dla Web Contentów
Zdjęcie widoku uprawnień dla Web Contentów

Mając webcontent z uprawnieniami jak na screenie powyżej nasz portal może zachować się różnie w zależności od wersji. Ma to związek z jednym z ustawień Liferaya dostępnym pod (w 7.2):

Control panel → configuration → system settings → web content

Konfiguracja ta nosi nazwe "Article view permissions check enabled" i za jej pomocą możemy ustawić czy Liferay powinien weryfikować uprawnienie "View" dla Web Contentów czy nie. Czyli:

  • Mamy przełącznik wyłączony - każdy może obejrzeć nasz WebContent chociaż uprawnienia (patrz screen wyżej) wskazują, że jedynie Owner powinien mieć taki dostęp
  • Mamy przełącznik włączony - weryfikacja następuje i rzeczywiście przykładowo Guest nie ma dostępu do WebContentu

Zmianą między wersjami jest to, że w wersji 7.0 domyślnie to ustawienie jest wyłączone (każdy ma dostęp), natomiast w wersji 7.2 jest domyślnie włączone.

Możemy więc mieć niespodziankę - wcześniej każdy mógł oglądać nasze WebContenty a teraz nikt nie może. Oczywiście zakładając, że nie definiowaliśmy tych uprawnień wcześniej.

Zmiana metody do pobrania aktualnego URLa w FTLu

W wersji 7.0.x mogliśmy pobierać aktualny URL używając następującego kodu:

url = request.attributes['CURRENT_URL']

Po podniesieniu naszego kodu już niestety to nie zadziała. Zamiast tego możemy użyć

url = portalUtil.getCurrentURL(request)

To akurat drobnostka, ale również nieudokumentowana.

Czy to wszystko?

Oczywiście nie. Powyższe przykłady to kilka błędów, które możemy spotkać. Niemniej jednak błędy przede wszystkim z Search API oraz z pobieraniem adresów zdjęć uważam za te idealnie pokazujące niespodziewane problemy, które mogą nas spotkać. Dla kontrastu dodałem także dwa błędy, które są zdecydowanie prostsze do znalezienia i poprawnienia, tak aby pokazać, że nie wszystkie błędy są aż tak strasznie ukryte (szczególnie jak w przypadku zmiany w Search API).

Mam nadzieję, że powyższy wpis choćiaż trochę pomoże Ci podczas migracji.

Pozdrawiam!

Copyright: Rafał Pydyniak