11.1 Oberflächenelement-Berechtigungen in UI-Anwendungen
Berechtigungen auf Oberflächenelementen von Anwendungen können anwendungsspezifisch oder, wenn die Anwendung Ansichten besitzt, ansichtenspezifisch festgelegt werden.
11.1.1 Anwendungsspezifische Oberflächenelement-Berechtigungen
Berechtigungen auf den Oberflächenelementen einer Anwendung ohne Ansichten funktionieren standardmäßig automatisch dadurch, dass nach dem Ausführen der init()-Methode der Anwendung mithilfe der Methode CisUiApplication.setApplication(VisualElement) alle Elemente des Identifikations- und Arbeitsbereichs mit der Anwendung verknüpft werden. Werden nach dem init() neue Elemente durch die Anwendung (z. B. beim Bauen von Karteireitern) oder den „SubApplicationManager“ (z. B. beim Instanziieren von „SubApplications“) in die Container-Hierarchie eingefügt, dann erben diese automatisch diese Eigenschaften.
Die folgende Abbildung zeigt den Technischen Eigenschaftsdialog so eines Oberflächenelements:
Technische Eigenschaften für das Feld „Benutzer“, das mit einer Anwendung verknüpft ist.
11.1.2 Ansichtenspezifische Oberflächenelement-Berechtigungen
Bei Anwendungen mit Ansichten möchte man nun je nach Anwendung auch den ganzen Identifikations- bzw. Arbeitsbreich oder Teile (Views, Felder) davon als ansichtenspezifisch deklarieren. Hierzu kann über das Flag „AUTOMATIC_VISUAL_ELEMENTS_DISABLED“ im Konstruktor von „CisUiApplication“ der Automatismus deaktiviert werden. Nachdem alle Anwendungsviews gebaut wurden, kann dann bestimmt werden, welche Teile anwendungsspezifisch sind und welche ansichtenspezifisch. Dazu dient die Methode setApplication(). Diese verknüpft das Feld mit der Anwendung und, falls angegeben, der Ansicht, und wendet die Oberflächenberechtigungen an (unsichtbar, read-only…). Sie kann sowohl in der init()-Methode als auch zu späteren Zeitpunkten (z. B. bei Komponenten die erst später gebaut werden) aufgerufen werden.
Ein Beispiel:
… XYZUiApplication …
…
identArea.add(commonView)
identArea.add(specialView);
setApplication(identArea)
// by default the identarea has no view
setApplication(specialView, ApplicationView.SPECIAL)
// mark all elements of the view a belonging to the view SPECIAL of the application.
Die folgende Abbildung zeigt den Technischen Eigenschaftsdialog eines Oberflächenelements der Anwendung, dass einer Ansicht zugeordnet ist:
Technische Eigenschaften für das Feld „Anwendung“, das mit einer Ansicht verknüpft ist.
11.1.3 Oberflächenelemente ohne Berechtigungen
Ist der Automatismus über das Flag „AUTOMATIC_VISUAL_ELEMENTS_DISABLED“ in der Anwendung abgeschaltet und wurde ein Oberflächenelement nicht der Anwendung oder einer Ansicht mit der Methode setApplication() zugeordnet, dann hat das Feld keinen Bezug und somit keine Zuordnung von Berechtigungen.
Die folgende Abbildung zeigt den Technischen Eigenschaftsdialog eines solchen Oberflächenelements:
Technische Eigenschaften für den Button „Zuordnungen“, der nicht mit einer Anwendung verknüpft ist.
11.2 Don’ts
In Comarch ERP Enterprise ist grundsätzlich verboten:
- Die Verwendung von lang.System.out. Die Verwendung von System.out.println() ist für Test und Entwicklung zulässig, da diese Ausgaben nur auf der Konsole des lokalen SAS erscheinen. Die System.out.println() müssen unbedingt vor dem Einchecken der Sourcen entfernt werden.
- Die Ausgabe von Exceptions auf die Konsole über printStackTrace().
- Der Zugriff auf Methoden deren Name mit einen Unterstrich „_“ beginnt.
- Die Verwendung vonlang.Class.forName() zum Laden von Klassen.
- Das Fangen von lang.Throwable oder java.lang.Error.
- Weiterer Code nach dem Aufruf von exit().
- Die Verwendung von Wert-Konstanten in OQL-Statements. Für Parameter sind immer Platzhalter („?“) zu verwenden und die entsprechenden set()-Methoden.
Beachten Sie unbedingt, dass bei Attributen von Business Objects vom Typ „date“ und „guid“ die Referenzsemantik verwendet wird. Die Business-Object-Instanz darft nicht unabsichtlich geändert werden.
Folgende Beispiele veranschaulichen das Problem:
Die Business-Object-Instanz wurde mit getObject() und Flag READ gelesen.
Date d= bo.getTimestamp();
d.setTime(…); // Error!
byte[] g= bo.getGuid();
g[i]=x; // Error!
Die Business-Object-Instanz wurde mit getObject() und Flag READ_UPDATE bzw. READ_WRITE gelesen:
bo.setGuid(g);
g[i]=x; // Error!
bo.setTimestamp(d);
d.setTime(…); // Error!
An den Fehlerstellen dürfen die Variablen „d“ und „g“ nicht verändert werden, da sich sonst die Business-Object-Instanz ändert. Im schlimmsten Fall lesen auch andere Anwendungen durch so eine Änderung falsche Daten, da das Business Object im Shared Cache gehalten wurde. Der SAS ist in einem inkonsistenten Zustand.
In den Namensräumen com.cisag.app.* bzw. in den Partnernamensräumen com.<Entwicklungspräfix>.app.* ist Folgendes verboten:
- Die Verwendung nicht finaler statischer Variablen elementaren Typs (byte[], boolean, short, int, long, double, String, …).
- Die Verwendung statischer komplexer Typen mit änderbarem Zustand (Arrays, Collections, Objekte mit set()-Methoden,…).
- Die Verwendung von Klassen aus dem Namensraum cisag.sys.*. Nur durch Tool-Kommandos wie bzw. crtbo generierte Klassen (Business Objects, Mapper, States, SearchMapper, OqlMapper) in den .obj-Unternamensräumen dürfen diese Klassen verwenden..
- Das Ableiten des Namens eines anderen Entwicklungsobjektes aus einem Java-Klassennamen mithilfe getName().
Ein Beispiel:
Häufig wird in einer Anwendung eine Stringtable verwendet, welche, um deren Zugehörigkeit zu kennzeichnen, wie die Anwendung heißt. Die Anwendung „com.cisag.app.sales.order.ui.OrderMaintenance“ würde den Namen ihrer Stringtable wie folgt ermitteln:
OrderMaintenance.class.getName()+”:CREATE_ FROM_PROPOSAL_TITLE”
Das funktioniert in der durch den Partner angepassten Anwendung nicht mehr. Denn dann würde Class.getName() den Namen der abgeleiteten Klasse liefern („com.<Entwicklungspräfix>. …“) und die Anweisung würde eine Stringtable verwenden, die nicht existiert bzw. wo die Konstante nicht definiert ist.
Ein anderes Beispiel wäre die Bildung des Namens einer Suche:
setSearchName( xxxx.class.getName );
Bei allen Anwendungs-, Prüf- und Logikklassen sollte der Default-Konstruktor ohne Parameter explizit angegeben sein. Das gilt für alle Klassen, die über die Klasse CisFactory instanziiert werden.
11.3 Ressourcen sparen
11.4
Achten Sie darauf, dass Key-, Search- und Dynamic-OQL-Iteratoren immer explizit geschlossen werden, da sie ein „CisResultSet“ für den Lesezugriff benutzen und dieses eine exklusive Datenbankverbindung dafür reserviert. Die dem System zur Verfügung stehenden Datenbankverbindungen sind begrenzt. Durch die Verwendung der finally()-Klausel kann das Schließen auch bei auftretenden Exceptions sichergestellt werden.
Wird der „CisObjectIterator“ nur für reine Existenzprüfungen (z. B. bei Löschprüfungen) verwendet, sollte man zur Vermeidung unnötiger Lesezugriffe die Ergebnisanzahl auf 1 begrenzen. Dazu dient die Methode setMaxRows() des „CisObjectIterators“. Wird für die Existenzprüfung das „CisResultset“ verwendet, fällt auch das Attributmapping der Business-Object-Attribute weg. Es muss ein konkretes Attribut angegeben werden.
Generell sollte vermieden werden, unnötige Java-Objekte zu erzeugen, da das ein teuerer Vorgang ist und die Instanzen auch wieder durch den Garbage Collector entfernt werden müssen.
Dazu einige Beispiele:
- Wenn Business-Object-Instanzen in einer Hashtable/HashSet zu führen sind, dann sollten Sie die ERP-System-Implementierungen CisHashMap/CisHashSet benutzen, die GUIDs direkt als Schlüssel aufnehmen können. So entfällt die Erzeugung von String-Objekten über toHexString() als Schlüssel.
- Statt eine Instanz der Klasse „Boolean“ über deren Konstruktor zu erzeugen, kann man die Konstanten TRUE und FALSE der Klasse „Boolean“ nutzen:
b = value ? Boolean.TRUE:Boolean.FALSE
statt
b = new Boolean(value)
- Zur Verkettung (Konkatenation) von Zeichenketten sollte die Klasse „StringBuilder“ und deren append()-Methode verwendet werden anstatt die Strings über „+“ zu verketten. Das ist nur dann sinnvoll, wenn mehrere Zeichenketten hintereinander oder in Schleifen verkettet werden sollen.
- Generell sollte versucht werden, die Zahl der Datenbank-Zugriffe zu minimieren, da das eine „teure“ Operation ist.
11.5 Verwendung des Java-Konstrukts „Assert“
Liefert der auszuwertende Ausdruck einer Assert-Anweisung „false“, dann führt das zu einem „AssertionError“. .
Ein Error wird an zentraler Stelle behandelt. Da beim Auftreten eines Errors der Application-Server normalerweise in einem inkonsistenten Zustand ist, wird der Application-Server beendet und alle ungesicherten Daten aller angemeldeten Benutzer gehen verloren.
Die Verwendung von „assert“ im ERP-System-Code sollte also unbedingt nur dann erfolgen, wenn damit Fälle abgefangen werden, die bei normaler Funktion des Application-Servers nicht auftreten und bei deren Auftreten der Application-Server beendet werden soll. Für die Prüfung der anderen Fälle wird „if (!condition) throw new …Exception“ verwendet.
11.6 Zuweisungen in Konstrutoren und Deklarationen
Die nachfolgenden Konventionen beziehen sich vor allem auf die Programmierung von Anwendungen.
Die Klasse „CisUiApplication“ hat eine Methode init(), in der alle von der Anwendung benötigten Instanzvariablen initialisiert werden können. Alle Zuweisungen für Variablen, die vorher erfolgen, werden zu einem Zeitpunkt ausgeführt, zu dem die Anwendung selbst noch nicht die aktive Anwendung ist (Bis zum Abschluss des Konstruktors gibt es sie noch nicht.). Das hat zur Folge, dass die Aufrufe der PGM-Schnittstellen nicht dieser Anwendung zugeordnet werden können, sondern (falls geöffnet) der zum Zeitpunkt des Konstruktoraufrufes aktiven Anwendung.
Die Information über die Instanzvariablen wird ausgewertet, um den Partnern die selektive Übersetzung von Anwendungen zu ermöglichen. Durch die fehlerhafte Zuordnung werden diese Daten verfälscht. Deshalb muss bei der Anwendungsentwicklung darauf geachtet werden, dass Zuweisungen immer erst in der init()-Methode vorgenommen werden.
Das folgende fehlerhafte Beispiel zeigt Zuweisungen für Variablen, die erst in der init()-Methode erfolgen dürfen:
// Impliziter Teil des Konstruktors
…
private Action action=new Action(1, “com.cisag.app…Action”);
private String text=null;
…
public MyApplication() {
// Expliziter Teil des Konstruktors.
super(AUTOMATIC_ENTER | AUTOMATIC_EXIT);
text=RepositoryInfo.getText(“com.cisag.app…..StringTable:TEXT.txt”);
…
}
Nachfolgend die korrekte Initialisierung in der init()- Methode:
private Action action;
private String text;
public MyApplication() {
super(AUTOMATIC_ENTER | AUTOMATIC_EXIT);
}
public void init() {
action=new Action(1, “com.cisag.app…Action”);
text=RepositoryInfo.getText(“com.cisag.app…..StringTable:TEXT.txt”);
…
}
11.7 Verwendung von „InputStream.available()“
Die Methode available() der Klassen „java.io.InputStream“ und deren Ableitungen kann nicht verwendet werden, um zu ermitteln, wieviel Bytes noch zu erwarten sind bzw. ob noch Daten folgen. Da es sich um einen Stream handelt, ist gar nicht bekannt, wie viele Bytes noch folgen werden. Die existierenden Implementierungen (GZipStream, FileInputStream, …) liefern abhängig von der JDK-Version, der Schachtelung von Streams usw. abweichende Ergebnisse.
Deswegen ist besser, die Methode read(byte[]) zu verwenden oder mit anderen read..()-Methoden bis zum Ende des Streams (EOFException) zu lesen.