Wprowadzenie
Aplikacje są często rozwijane i utrzymywane niezależnie od siebie. Systemowo traktowane są jako oddzielne komponenty oprogramowania. Aplikacje, które nie są od siebie zależne, mogą być instalowane i aktualizowane niezależnie.
Merytorycznie może być jednak celowe, aby komunikacja między aplikacjami miała miejsce: przykładowo, poprzez dostęp do danych innej aplikacji lub powiadamianie innych aplikacji o określonych zdarzeniach w logice programowej danej aplikacji. Ta komunikacja jest najczęściej realizowana przez Hook Contracts, Widoki Obiektów i Klasy Stabilne, co prowadzi do zależności instalacyjnej między zaangażowanymi aplikacjami.
W artykule opisane zostały technologie interfejsowe, za pomocą których aplikacje mogą się ze sobą komunikować bez powstawania zależności instalacyjnej. Opisane zostaną również przypadki użycia dla takich interfejsów.
Grupa docelowa
- Programiści aplikacji
Opis
Wskazówki dotyczące użytkowania
Dla komunikacji między aplikacjami należy stosować opisane w tym artykule technologie interfejsowe tylko wtedy, gdy faktycznie nie jest pożądana zależność instalacyjna. Jeżeli w inny sposób istnieje już zależność instalacyjna, należy zamiast nich użyć Klas Stabilnych, Hook Contracts (z zależnością) i Widoków Obiektów.
Interfejsy udostępnione za pomocą opisanych technologii muszą być utrzymywane jako kompatybilne po ich pierwszym udostępnieniu. Ponieważ nie jest sprawdzana zależność, w systemie mogą znajdować się użytkownicy, którzy zostali stworzeni w oparciu o dowolną, dotychczas udostępnioną wersję interfejsu. Za pomocą opisanych w niniejszym dokumencie technologii interfejsowych możliwa jest komunikacja między aplikacjami również wtedy, gdy nie są one rozwijane w tym samym systemie deweloperskim aplikacji. W takim przypadku komunikacja nie może jednak być testowana w systemie deweloperskim aplikacji.
Interfejsy Klienta bez zależności instalacyjnej
Interfejs Klienta (Client Interface) jest interfejsem aplikacji dostawcy (Provider-App), w którym aplikacja użytkownika (User-App) może wywoływać metody. Zależność instalacyjna jest unikana poprzez to, że wywołania metod odbywają się za pośrednictwem Proxy Java.
Poniższa grafika pokazuje składowe Interfejsu Klienta bez zależności instalacyjnej. Aplikacja dostawcy (Provider-App) udostępnia interfejs (Interface), w którym definiowane są dostępne metody, i zawiera implementację tego interfejsu (Implementation). Aplikacja użytkownika (User-App) zawiera kopię tego interfejsu w swojej własnej przestrzeni nazw. W kodzie aplikacji użytkownika (Klasa Caller na grafice) metody są wywoływane w obiekcie Implementation za pośrednictwem Proxy Java.

Połączenie zrealizowane przez Java-Proxy skopiowanego interfejsu w aplikacji użytkownika oraz implementacji w aplikacji dostawcy jest w dalszej części artykułu nazywane wiązaniem interfejsu. Interfejs w aplikacji dostawcy służy programiście aplikacji użytkownika jako artykuł oraz jako szablon do kopiowania. Adnotacja @InvocationTarget na interfejsie określa klasę implementacji, dzięki czemu znany jest cel wywołań metod. Specyfikacja jest tworzona jako ciąg znaków, dzięki czemu adnotacja może zostać przyjęta bez zmian w skopiowanym interfejsie. Tylko ta kopia jest wymagana do wywołania metody.
Provider-App B, Interface:
package com.provider.ext.app.b.template;
import com.cisag.pgm.appserver.InvocationTarget;
@InvocationTarget(“com.provider.ext.app.b.log.Implementation”)
interface Interface {
String method(…);
}
Provider-App B, Implementacja:
package com.provider.ext.app.b.log;
class Implementation implements com.provider.ext.app.b.template.Interface {
String method(…) { … }
}
User-App A, Interface:
Interfejs jest kopiowany do aplikacji użytkownika, przy czym należy jedynie wymienić pakiet:
package com.user.ext.app.a.log;
import com.cisag.pgm.appserver.InvocationTarget;
@InvocationTarget(“com.provider.ext.app.b.log.Implementation”)
interface Interface {
String method(…);
}
Istnieje połączenie między interfejsem w aplikacji użytkownika a implementacją w aplikacji dostawcy. Aby móc wywoływać metody, aplikacja użytkownika tworzy obiekt implementacji i obiekt Java-Proxy, który reprezentuje interfejs Interface za pomocą metody pomocniczej w PGM. Aplikacja użytkownika może następnie wywoływać metody na tym obiekcie Java-Proxy, przy czym wywołania są przekazywane do obiektu implementacji przez Java-Proxy.
Jeśli aplikacja dostawcy nie jest zainstalowana, zamiast obiektu proxy zwracana jest wartość null.
Przykład wywołania metody:
import com.user.ext.app.a.log.Interface;
:
CisProxyManager xm= CisEnviron-ment.getInstance().getProxyManager();
Interfejs intf = xm.createImplementation(Interface.class);
If (intf != null) {
String s = intf.method();
//...
}
Gdy wywoływana jest metoda, kolejka komunikatów nie jest izolowana. Odpowiada to zachowaniu podczas wywoływania stabilnych metod, ale różni się od hook contract.
Interfejs powinien być udokumentowany bezpośrednio w aplikacji dostawcy za pomocą JavaDoc. Oznacza to, że dokumentacja jest kopiowana podczas kopiowania interfejsu i jest dostępna w kodzie aplikacji użytkownika.
Wymagania dotyczące interfejsu
W aplikacji dostawcy interfejs powinien być zapisany w podprzestrzeni nazw template obok przestrzeni nazw log. Adnotacja @InvocationTarget pozwala na użycie interfejsu jako interfejsu klienta bez zależności od instalacji. W aplikacji użytkownika interfejs można wprowadzić w dowolnej przestrzeni nazw, nawet jako interfejs wewnętrzny.
Klasa implementacji nie może być klasą wewnętrzną. Obiekty tej klasy są tworzone przez CisProxyManager za pomocą CisFactory.createInstance().
Tylko metody dostępne w interfejsie Java mogą być wywoływane przez proxy. Użycie jakichkolwiek odziedziczonych metod prowadzi do wyjątku RuntimeException. Wyjątkiem są odziedziczone metody hashCode() i toString(). Mogą one być wywoływane, jeśli nie zostały nadpisane explicit w interfejsie Java.
Parametry i wartości zwracane
Metody w interfejsie mogą być wywoływane z parametrami i wartościami zwracanymi. Podczas gdy typy prymitywne (short, int, char itp.) mogą być używane bez ograniczeń, ograniczenia opisane poniżej mają zastosowanie do niektórych typów obiektów.
Widoki obiektów
Widoki obiektów są interfejsami, których nie można kopiować 1:1. Nie można ich zatem używać jako parametrów dla wiązania interfejsu.
Powiązanie interfejsu
Interfejsy, dla których dostępne jest powiązanie interfejsu, mogą być używane nie tylko jako interfejsy klienta, ale także jako parametry metod. Parametry metody muszą zostać utworzone w aplikacji użytkownika za pomocą createImplementation(.). W czasie wykonywania przekazane obiekty proxy są zastępowane odpowiednimi instancjami implementacji. Aplikacja dostawcy nie otrzymuje proxy, ale implementację ze swojego modułu, która bezpośrednio implementuje interfejs z aplikacji dostawcy.
Podobnie, interfejsy z wiązaniem interfejsu mogą być dostarczane jako zwrot. W tym przypadku metoda aplikacji dostawcy zwraca instancję implementacji. Jest ona dostarczana z instancją proxy dla aplikacji użytkownika, która realizuje interfejs aplikacji użytkownika.
Interfejsy, których powiązanie interfejsu ma zostać rozwiązane w czasie wykonywania, muszą zostać zarejestrowane jawnie przez aplikację użytkownika podczas tworzenia obiektu implementacji za pomocą createImplementation(.):
CisProxyManager.createImplementation( Class<?> intf, Class<?>… ad-ditionalInterfaces).
Niezarejestrowane interfejsy z wiązaniem interfejsu są traktowane jak zwykłe obiekty i przekazywane bezpośrednio, szczegóły dostępne w rozdziale Obiekty regularne.
Interfejsy można również zarejestrować później dla obiektu proxy, wywołując następującą metodę:
CisProxyManager.registerAdditionalInterfaces(Proxy, Class<?>… ad-ditionalInterfaces).
Zwykle jest to konieczne tylko wtedy, gdy obiekt implementacji został automatycznie dostarczony z instancją proxy jako wartością zwracaną wywołania metody, a nie został utworzony przez createImplementation(.).
Wiązanie interfejsów w kolekcjach
Jeśli kolekcje są parametrami lub wartościami zwracanymi wywołania metody, interfejsy z wiązaniem interfejsu zawarte w kolekcjach są rozwiązywane w czasie wykonywania. Warunkiem wstępnym jest to, że parametr lub wartość zwracana w metodzie jest zadeklarowana jako jeden z następujących interfejsów:
- java.lang.Iterable
- java.util.Collection
- java.util.List
- java.util.Set
- java.util.Map
- com.cisag.pgm.util.CisMap
Jeśli zadeklarowano inny interfejs kolekcji lub inną klasę kolekcji, nie ma powiązania interfejsu.
Podczas wywoływania metod kolekcje są opakowywane przez obiekty proxy, więc nie jest możliwe rzutowanie kolekcji na określoną klasę kolekcji.
Interfejsy zawarte jako elementy, których wiązanie interfejsu ma zostać rozwiązane w czasie wykonywania, muszą być jawnie zarejestrowane przez aplikację użytkownika, szczegóły można znaleźć w rozdziale Powiązanie interfejsu.
Kolekcje mogą być zagnieżdżane zgodnie z wymaganiami.
Obiekty regularne
Wszystkie obiekty, dla których nie ma zastosowania żadna z procedur opisanych w poprzednich rozdziałach, są przenoszone bezpośrednio. Dotyczy to w szczególności tablic.
W razie potrzeby bardziej złożone struktury danych mogą być mapowane w aplikacji dostawcy jako oddzielny interfejs z powiązaniem interfejsu, szczegóły można znaleźć w rozdziale Powiązanie interfejsu.
Przykład: Parametry obiektu
Obiekt kontenera UserData ma być używany jako parametr i wartość zwracana w metodach interfejsu. Zarówno interfejs, jak i obiekt kontenera są dostarczane jako interfejs z powiązaniem interfejsu.
Obiekt kontenera
Provider app B, interfejs:
package com.provider.ext.app.b.template;
:
@InvocationTarget("com.provider.ext.app.b.log.UserDataImplementation")
interface UserDataInterface {
void setData(...);
Object getData();
}
Provider app B, implementacja:
package com.provider.ext.app.b.log;
import com.provider.ext.app.b.template.UserDataInterface;
class UserDataImplementation implements UserDataInterface {
void setData(...) { ... }
Object getData() { ... }
}
User app A, Interface:
package com.user.ext.app.a.obj;
:
@InvocationTarget("com.provider.ext.app.b.log.UserDataImplementation")
interface UserDataInterface {
void setData(...);
Object getData();
}
Interfejs Interface wraz z jego implementacją
Provider app B, Interface:
import com.provider.ext.app.b.template.UserDataInterface;
:
interface {
void initUserData (UserDataInterface user);
UserDataInterface getUserData();
Boolean printUserDatas(java.util.list<UserDataInterface> datas);
}
Provider app B, implementacja:
import com.provider.ext.app.b.template.UserDataInterface;
:
class Implementation implements Interface {
void initUserData (UserDataInterface user) { ... }
UserDataInterface getUserData() { ... };
boolean printUserDatas(java.util.list<UserDataInterface> datas)
{ ... }
}
Aplikacja użytkownika A, interfejs:
Podczas przesyłania kontenera danych do aplikacji użytkownika A należy wymienić tylko pakiet i dostosować wszelkie importy.
import com.user.ext.app.a.log.UserDataInterface;
:
interface Interface {
String method(…);
void initUserData (UserDataInterface user);
UserDataInterface getUserData();
boolean printUserDatas(java.util.list<UserDataInterface> datas);
}
import com.user.ext.app.a.log.UserDataInterface;
:
interface Interface {
String method(...);
void initUserData (UserDataInterface user);
UserDataInterface getUserData();
boolean printUserDatas(java.util.list<UserDataInterface> datas);
}
Wywołania metod
Różne wywołania metod przez aplikację użytkownika A z UserData jako parametrem lub wartością zwracaną:
import com.user.ext.app.a.log.Interface;
import com.user.ext.app.a.log.UserDataInterface;
:
CisProxyManager xm= CisEnvironment.getInstance().getProxyManager();
UserDataInterface userData = xm.createImplementation(UserDataInterface.class);
Interfejs intf = xm.createImplementation(Interface.class, UserDataInterface.class);
If (intf != null) {
intf.initUserData(userData);
UserDataInterface userData2 = intf.getUserData();
java.util.ArrayList datas = new ArrayList();
datas.add(userData);
datas.add(userData2);
Boolean ok = printUserDatas(datas);
}
Hook Contract bez zależności od instalacji
Hook Contract to technologia interfejsu, w której aplikacja dostawcy zapewnia interfejs (hook), a aplikacje użytkownika mogą się zarejestrować, aby być wywoływane w ramach tego interfejsu.
Poniższy schemat przedstawia składniki hook contract , gdy są używane bez zależności od instalacji. Hook Contract z hookiem jest dostarczany w aplikacji dostawcy i są zaimplementowane w aplikacji użytkownika. W przypadku hook contract , implementacja hooka jest wywoływana przez aplikację dostawcy.

W aplikacji dostawcy nie jest wymagane specjalne oznaczenie ani hooka, ani hook contract do użytku bez zależności od instalacji. Hooki muszą spełniać warunki określone w rozdziale Parametry i wartości zwracane, aby mogły być używane bez zależności od instalacji.
W aplikacji użytkownika implementacja hook contract musi być oznaczona do użytku bez zależności od instalacji. W tym celu atrybut dependency elementu contract w definicji XML jest ustawiony na false (wartość domyślna to true). Dzięki takiemu oznaczeniu implementacja hook contract może być również aktywowana jako obiekt deweloperski bez definicji hook contract lub interfejsu klasy Java, a zatem nie jest zależna od instalacji definicji hook contract.
W przypadku implementacji hook istnieje wybór między:
- skopiowaniem interfejsu hooka do aplikacji użytkownika i wyprowadzeniem z niego implementacji
- lub skopiować metody hooka do implementacji z niezmienioną sygnaturą i, z przyczyn technicznych, bezpośrednio zaimplementować pusty interfejs com.cisag.pgm.base.Hook.
W obu przypadkach tworzone jest powiązanie interfejsu. Zaleca się skopiowanie interfejsu hooka do aplikacji użytkownika w celu uniknięcia błędów programistycznych.
Jeśli aplikacja dostawcy nie jest dostępna w czasie wykonywania i dlatego brakuje definicji hook contract, hook lub implementacja hooka nie zostanie wywołana.
I odwrotnie, jeśli aplikacja użytkownika nie jest dostępna w czasie wykonywania, wywołanie hooka przez aplikację dostawcy – podobnie jak w przypadku hook contract z zależnością instalacyjną – nie ma żadnego efektu.
Jeśli definicja hook contract jest dostępna w czasie wykonywania, ale nie zawiera interfejsu haooka, jego wywołanie również nie ma żadnego efektu. W takim przypadku aplikacja dostawcy jest prawdopodobnie zainstalowana w nieaktualnej wersji. To, czy jest to problem, musi być oceniane indywidualnie dla każdego przypadku.
We wszystkich innych aspektach hook contract zachowują się tak, jak opisano w dokumentacji Hook Contract.
Przykład
Implementacja hook contract
: <contract dependency="false">com...b...HookContractDef</contract> <hook> <interface>com.provider.ext.app.b.hook.HookInterface</interface> <implementation>com.user.ext.app.a.log.HookImpl</implementation> </hook> :
Aplikacja użytkownika A, skopiowany interfejs hook(opcjonalnie)
package com.user.ext.app.a.log;
/* Skopiowany interfejs com.provider.ext.app.b.hook.HookInterface */
public interface HookInterface extends com.cisag.pgm.base.Hook {
:
}
Aplikacja użytkownika A, implementacja hook
class HookImpl implements com.user.ext.app.a.log.HookInterface {
:
}
lub
class HookImpl implements com.cisag.pgm.base.Hook {
:
}
Parametry i wartości zwracane
Dla parametrów i wartości zwracanych metod hook obowiązują następujące ograniczenia:
- Interfejs z wiązaniem interfejsu i implementacją w aplikacji proxy może być używany w metodach haków jako parametry i wartości zwracane; automatyczne dopasowywanie typów odbywa się za pośrednictwem serwerów proxy. Nie dotyczy to jednak elementów w kolekcjach: mogą one być przekazywane, ale nadal korzystają z interfejsu aplikacji dostawcy.
- ponadto te same ograniczenia mają zastosowanie do parametrów i wartości zwracanych w umowach hook, jak w interfejsach klienta.
W przypadku korzystania z hook contract bez zależności od instalacji, interfejsy, których powiązanie interfejsu ma zostać rozwiązane w czasie wykonywania, muszą być jawnie zarejestrowane przez aplikację dostawcy. W przypadku wywołania hooka, aplikacja dostawcy najpierw generuje implementacje hooka:
Collection<Hook> CisHookManager.getHookImplementations(Class con-tract, Class interf)
lub
Hook CisHookManager.getHookContainer(Class contract, Class interf).
Następujące metody są następnie wywoływane w celu zarejestrowania dodatkowych interfejsów dla wiązania interfejsów parametrów lub wartości zwracanych:
CisProxyManager.registerAdditionalInterfaces(Collection<Hook>, Class<?>… additionalInterfaces)
lub
CisProxyManager.registerAdditionalInterfaces(Hook, Class<?>… additionalInterfaces)
Obiekty z obsługą wiązania interfejsów, które mają być przekazywane jako parametry, są tworzone bezpośrednio przez aplikację dostawcy dla wywołania hooka, jak pokazano w poniższym przykładzie. W przeciwieństwie do tego, aby zwrócić obiekt z wiązaniem interfejsu, obiekt ten jest najpierw tworzony w implementacji hooka za pomocą CisProxyMana-ger.createImplementation(.).
[exampleParametry dla wywołań hook
Przykład pokazuje wywołanie hooka w aplikacji dostawcy z obsługą wiązania interfejsu. Aplikacja dostawcy przekazuje implementację interfejsu w wywołaniu haka.
import com.provider.ext.app.b.template.Interface; import com.provider.ext.app.b.template.UserDataInterface; : CisProxyManager xm= CisEnvironment.getInstance().getProxyManager(); UserDataInterface userData = new UserData(); Interfejs hook = hm.getHookContainer(com.provider.ext.app.b.ContextClass.class, Interface.class); xm.registerAdditionalInterfaces(hook, UserDataInterface.class); hook.initUserData(userData);
Implementacja hooka używa kopii interfejsu w swojej sygnaturze metody i może go użyć do uzyskania dostępu do przekazanego obiektu.
][/example]
Powiązane (przyjazne) aplikacje
Metody opisane powyżej wykorzystują sprzężenie interfejsu między zaangażowanymi aplikacjami, tj. aplikacja A musi zapewnić interfejs, za pośrednictwem którego aplikacja B może uzyskać do niej dostęp. Interfejsy te są przydatne w przypadku wywołań funkcjonalnych z prostą strukturą parametrów. Nie nadają się one na przykład do wymiany danych masowych w związku z dostępem do bazy danych. Jak opisano powyżej, parametry metody muszą być indywidualnie przypisane do proxy za pomocą createImplementation(.). Jeśli wymieniane mają być kompletne obiekty biznesowe, każda pojedyncza instancja musi być przygotowana osobno. Przede wszystkim metody Persistence service nie mogą być używane bezpośrednio do odczytu obiektów biznesowych w zwykły sposób.
Tak zwane „przyjazne aplikacje” umożliwiają aplikacji A dostęp do niektórych obiektów deweloperskich aplikacji B, tak jakby sama je utworzyła. Jest to szczególnie przydatne w przypadku obiektów biznesowych i klas Java.
Oprócz „przyjaznych aplikacji”, poniżej opisano inne możliwe typy zależności między dwiema aplikacjami. Można je zdefiniować w aplikacji Panel System w polu Typ wskazać opcję Grupa systemów na zakładce Przyporządkowania modułu.
Brak zależności
Domyślnie aplikacja A nie może odwoływać się do żadnych obiektów deweloperskich z aplikacji B. Próby zameldowania lub aktywowania odpowiednich elementów z aplikacji A powodują wyświetlenie komunikatu o błędzie. Oznacza to, że nie mogą powstać żadne zależności instalacyjne. Jeśli zależność między dwiema aplikacjami nie jest wyraźnie zdefiniowana w grupie systemowej, ten status ma zastosowanie.
Wymagany moduł
Aplikacja B może zostać wprowadzona jako Wymagany moduł dla aplikacji A. Ma to następujące skutki:
- Obiekty deweloperskie z aplikacji B mogą być wywoływane w aplikacji A. Chociaż jest to dozwolone, nie jest zalecane, dlatego prowadzi do ostrzeżenia.
- Klasy Java z aplikacji A mogą wywoływać metody klas z aplikacji B. Jeśli jednak metody te nie zostaną zidentyfikowane jako stabilne, pojawi się ostrzeżenie.
- Wpływ na instalację: wszystkie aplikacje zadeklarowane jako „wymagane” przez aplikację A muszą być już zainstalowane w systemie docelowym, w przeciwnym razie instalacja zostanie anulowana.
Przyjazny (powiązany) moduł
Aplikacja B może zostać zadeklarowana jako „przyjazny moduł” aplikacji A. Ma to następujące skutki:
- Obiekty deweloperskie z aplikacji B mogą być wywoływane w aplikacji A. Nie jest wyświetlane żadne ostrzeżenie.
- Klasy Java z aplikacji A mogą wywoływać metody klas z aplikacji B. Nie ma ostrzeżenia dla niestabilnych metod.
- Wpływ na instalację: nie jest konieczne, aby aplikacja B była już zainstalowana podczas instalacji aplikacji A.
Programowanie
Programowanie odbywa się w środowisku, w którym zainstalowane są wszystkie uczestniczące aplikacje. W tym przypadku programista może uzyskać dostęp do metod z innych aplikacji w czasie kompilacji przy użyciu wszystkich zwykłych środków, pod warunkiem, że są one zadeklarowane jako public. W związku z tym, w przypadku obsługi błędów podczas ewidencjonowania, niezmiennie obowiązuje zasada: tylko wtedy, gdy wszystkie używane symbole i odniesienia mogą zostać pomyślnie rozwiązane, a nowo opracowana lub zmieniona klasa może zostać skompilowana bezbłędnie, jest ona zaewidencjonowana.
Utworzone pliki Java-class zawierają w ten sposób odniesienia do wywoływanych metod innych aplikacji.
W czasie działania Java Virtual Machine (JVM) podczas pierwszego dostępu do innej klasy próbuje ona załadować tę klasę do pamięci. Jeżeli w trakcie tego procesu stwierdzone zostanie, że odpowiedni plik class nie jest dostępny, zgłaszany jest wyjątek NoClassDefFoundError-Exception, który domyślnie powodowałby ponowne uruchomienie serwera aplikacji.
W związku z tym należy zapobiec próbie załadowania przez JVM nieistniejącej klasy. Można to zrobić na dwa sposoby: za pomocą środków Java lub za pomocą API Comarch ERP Enterprise.
- W Javie wyjątek może zostać przechwycony przez blok try-catch. Zapewnia to jednak najmniejszą kontrolę nad przebiegiem programu, ponieważ wyjątek pojawia się tylko podczas rzeczywistej próby dostępu.
- Większą kontrolę oferują metody isAppInstalled, których można użyć do sprawdzenia w czasie wykonywania, czy żądana aplikacja jest zainstalowana. W zależności od wyniku, sekwencja programu może być następnie specjalnie kontrolowana. Dostęp do powiązanej aplikacji jest często częścią bloku instrukcji, np. z przygotowawczymi, niekrytycznymi wywołaniami metod, które powinny zostać całkowicie pominięte, jeśli aplikacja nie jest zainstalowana.
Klasa com.cisag.pgm.util.PluginUtility oferuje 2 metody isAppInstalled(..) z różnymi sygnaturami:
- Static boolean isAppInstalled(String developmentPrefix, String appName)
- Static boolean isAppInstalled(CisModuleId)
Przykład
Aplikacja App2 uzyskuje dostęp do obiektu biznesowego z App1.
Package com.ado.ext.app.app2;
import com.cisag.pgm.util.PluginUtility;
import com.ado.ext.app.app1.obj.App1TestBO;
:
App1TestBO b;
if (PluginUtility.isAppInstalled("ado", "app1")) {
System.out.println("Start reading...");
b = App1TestBO.newTransientInstance();
CisObjectIterator<App1TestBO> it = b.retrieve_instances();
while (it.hasNext()) {
b = it.next();
System.out.println(" wartość: " + b.getValue());
}
} else {
System.out.println("Wtyczka nie została zainstalowana");
}
:
Deklaracja zmiennej App1TestBO b; nie generuje żadnego kodu wykonywalnego i dlatego nie musi być zagnieżdżana.
Kod ten można skompilować w systemie deweloperskim tylko wtedy, gdy istnieje plik klasy com/ado/ext/app/app1/obj/App1TestBO.class, częściowo z powodu linii b = App1TestBO.newTransientInstance();. W czasie wykonywania kod w instrukcji if może zostać wykonany tylko wtedy, gdy dostępne są również klasy mappera App1TestBO.
Wytyczne programistyczne
Dostępność klas
Możliwość adaptacji istniejących klas Java powinna zostać zachowana z jak najmniejszą liczbą ograniczeń. Do adaptacji wymagane jest jednak, aby klasa mogła być w pełni kompilowana na danym systemie deweloperskim. Z tego względu należy wziąć pod uwagę kilka kwestii podczas uzyskiwania dostępu do powiązanych aplikacji, aby klasy pozostały adaptowalne.
Klasa z dostępem do powiązanych aplikacji może być edytowana tylko w tych systemach deweloperskich, w których zainstalowane są odpowiednie powiązane aplikacje. W przeciwnym razie klasa nie może być włączona do zadania deweloperskiego, ponieważ nie będzie można jej ponownie zaewidencjonować z powodu błędów kompilacji. Dotyczy to zarówno klas dodanych ręcznie, jak i automatycznie do zadania deweloperskiego.
Dostosowywanie klas Java
Problem może wystąpić zawsze wtedy, gdy w systemie niższego poziomu S2 ma zostać zaadaptowana klasa A z systemu S1, która zawiera dostępy do powiązanej aplikacji B. Zgodnie z założeniem, aplikacja B musi być dostępna w S1. Nie może to być jednak wymóg w S2, zwłaszcza jeśli w S2 mają zostać wprowadzone tylko adaptacje klasy A, które nie mają odwołań do aplikacji B, a odnoszą się wyłącznie do standardowego kodu aplikacji.
Należy pamiętać o następujących kwestiach, aby kod był jak najbardziej elastyczny („najlepsza praktyka”):
- Klasa A nie powinna mieszać dostępu do klasy B z zaprzyjaźnionej aplikacji z rozbudowaną logiką własną.
- Można to osiągnąć, na przykład, poprzez utworzenie oddzielnej klasy A’, która zawiera metody Wrapper dla faktycznego dostępu do B, podczas gdy logika własna pozostaje w A i wywołuje metody Wrapper z A’.
- W ten sposób klasa A może być nadal dostosowywana i kompilowana w S2, podczas gdy A’ musi być dostępna w S2 jedynie jako plik class.
Dodatkowo należy jawnie oznaczyć klasę A’ adnotacją @UsesFriendApps jako adaptowalną tylko warunkowo.
Klasy nieadaptowalne
Za pomocą adnotacji @UsesFriendApps można bezpośrednio określić w deklaracji klasy, że klasa ta uzyskuje dostęp do powiązanej aplikacji. Wpływa to przede wszystkim na decyzję, czy ta klasa może być adaptowana. Konkretnie, adnotacja określa, czy klasa Java może ewentualnie nie zostać włączona do zadania deweloperskiego.
Klasa z adnotacją może zawsze zostać uwzględniona w zadaniu programistycznym, jeśli została utworzona w bieżącym systemie. Jeśli pochodzi z innego systemu, włączenie zależy od parametrów adnotacji. Jeśli adnotacja zostanie użyta bez dalszych parametrów (@UsesFriendApps class …), klasa może zasadniczo nie zostać uwzględniona w zadaniu deweloperskim. Wyraźnie określając używane powiązane aplikacje, można zdefiniować, które aplikacje muszą być zainstalowane w systemie programistycznym S2, aby klasa mogła być nadal uwzględniona w zadaniu deweloperskim. Aby to zrobić, należy określić aplikacje jako listę ciągów w formacie
Dostosowywanie obiektów biznesowych
Jeśli obiekt biznesowy zostaje zmieniony w zadaniu deweloperskim, to poprzez wywołanie crtbo dodawane są do zadania deweloperskiego ewentualnie już istniejące klasy Update. Użycie Persistence service w klasach Update jest niedozwolone. Dotyczy to również zapytania poprzez isAppInstalled(..): odpowiednia konstrukcja z zagnieżdżonym dostępem do innej aplikacji nie może być tutaj stosowana.
Ograniczenia dla parametrów
W przyszłości oddzielne aplikacje będą mogły korzystać z oddzielnych bibliotek, w szczególności również z różnych wersji tej samej zewnętrznej biblioteki. W tym celu każda aplikacja będzie zarządzać własnym obszarem pamięci, w którym jej biblioteki są ładowane lokalnie. Prowadzi to jednak do tego, że obiekty z dwóch lokalnych bibliotek nie są ze sobą kompatybilne. (Przyczyna techniczna: Java używa własnego Classloader dla każdej aplikacji, a dwie klasy mogą być na siebie mapowane tylko wtedy, gdy zostały załadowane przez ten sam Classloader).
Z tego powodu należy unikać stosowania obiektów z obcej biblioteki jako parametrów lub wartości zwracanych przez powiązaną metodę.
Korzystanie z innych interfejsów
Klasa com.cisag.pgm.appserver.CisModuleId zawiera metodę, której można użyć do określenia, czy aplikacja jest zainstalowana. To sprawdzenie może być używane przed użyciem niektórych interfejsów API (wyszukiwania OQL, Resultsets). Oznacza to, że te interfejsy API mogą być również używane bez zależności od instalacji.
Jeśli id jest CisModuleId sprawdzanej aplikacji, metoda instancji boolean isInstalled() dostarcza informacji, czy aplikacja może być używana. Metoda ta uwzględnia również klucz licencyjny aplikacji, ale nie ustawienia Konfiguracji.



