Autosave when deleting attachments on an XPage

3. Dezember 2014 Posted by Christian Annawald

 

To download attachments in an application and also to be able to delete them, the FileDownload control is used on IBM Domino XPages. Unfortunately, this control comes with a little twist. If you click on the trash can icon to delete the attachment, it looks like the file is actually deleted. But the file is not finally deleted until the XPage is saved.

If you leave the XPage after the click on the trash can icon, without saving the document – after all, the attachment disappeared from the view, so why SHOULD you save the document anyway – and open it some time later again, the file is back.

 

Img. 1: Document including several attachments

 

Img. 2: Document after clicking the trash can icon of the last attachment

 

Img. 3: The last attachment is back again after leaving the document without saving and coming back to it afterwards

 

In order to execute the change, or rather the deletion, directly within the document, it is necessary to add the property setSave(true) to the event handler of the trash can icon. Unfortunately, you cannot do this within the Domino Designer. So a little programming work is needed.

Luckily I have found the appropriate code for most of this work in a discussion comment on stackoverflow by Sven Hasselbach: http://stackoverflow.com/questions/13101615/auto-save-doc-after-delete-of-attachment-in-file-download-control

Unfortunately, the original code always produced the following error when I used it with Notes 9:

X XPAGE ERROR: COM.XSP.IBM.ACTION.ACTIONGROUP INCOMPATIBLE WITH COM.IBM.XSP.ACTIONS.DELETEATTACHMENTSACTION

Therefore, I adjusted the source code, so that it works with Notes 8.5.3 and Notes 9 alike. To use it properly you only need to insert it into the BeforeRenderResponse event of the XPage.

 

<xp:this.beforeRenderResponse>

            <![CDATA[#{javascript:

            function overrideFileDownloadAction( fDownload ){

                        //Check whether the control exists or not

                        if( fDownload === null )

                           return;

                        //Call actual function

                        rekOverrideFileDownloadAction( fDownload, fDownload );

             }

/*

*This control iterates through all children of the FileDownload control

*and seeks the event handler

*/

            function rekOverrideFileDownloadAction( component:javax.faces.component.UIOutput,fDownload:com.ibm.xsp.component.UIFileDownload  ){

                        try{

                                   //Get children of the current element

                                   var children:java.util.List = component.getChildren();

                                   var it:java.util.Iterator = children.iterator();

                                   var curChild:javax.faces.component.UIOutput;

                                   //Loop over all children

                                               while( it.hasNext() ){

                                                         curChild = it.next();

                                                         if( typeof( curChild ) === 'com.ibm.xsp.component.xp.XspEventHandler' ){

                                                         //Event handler found

                                                         //set setSave to true

                                                         // to automatically save after the event

                                                           curChild.setSave(true);

                                               }

                                   //Event handler not found, therefore recursive call to investigate the children

                                   rekOverrideFileDownloadAction( curChild , fDownload );

                                               }

                        }catch(e){}   

            }

           

overrideFileDownloadAction( getComponent( 'IDFILEDOWNLOAD' ) );

//IDFILEDOWNLOAD = the ID of the FileDownload control

    }]]>

</xp:this.beforeRenderResponse>

 

If the above code is inserted into your XPage, the file is automatically saved after clicking on the trash can icon. The disadvantage of this is that it saves the complete document and not just delete the attachment. Therefore, you have to be careful to ensure that you have not previously changed the value of another field that should not be saved.

 

Wie spreche ich Java-Klassen mit LotusScript an? – LS2J und seine Tücken

1. Oktober 2014 Posted by Christian Annawald

 

Uns stellte sich schon das ein oder andere Mal die Frage, ob es möglich sei, die Programmlogik für XPages und Legacy Anwendungen gemeinsam zu pflegen und zu nutzen. Hierzu drängte sich uns direkt LS2J von IBM auf, ein Framework, das es ermöglicht, aus LotusScript heraus direkt Java-Klassen zu benutzen.

Hier bekommt ihr einen kurzen Überblick zu LS2J: http://www-01.ibm.com/support/knowledgecenter/SSVRGU_9.0.1/com.ibm.designer.domino.main.doc/LSAZ_LS2J_CONNECTING_WITH_JAVA.html

 

Grundsätzlich ist es recht einfach zu benutzen: Als erstes legt man sich eine Java-Klasse an z.B.:

public class myTestClass {

  public void doSomething() {

   System.out.println("Hallo Welt");

  }

 }

 

Als nächstes legt man sich ein wenig LotusScript Code an

Option Declare

 UseLSX "*javacon"

 Use "TestJavaLib"

Sub useJavaClass

      Dim js As New JavaSession

      Dim myClass As JavaClass

      Dim myObject As JavaObject

      Set myClass = js.GetClass("myTestClass")

      Set myObject = myClass.CreateObject

      Call myObject.doSomething()

End Sub

 

Nun fügt man den LotusScript-Teil z.B. in einen Button auf einer Form ein:

  

 

Durch das Benutzen des Buttons erhält man nun in der Java-Konsole folgende Ausgabe:

 

Wer ein weiterführendes Beispiel benötigt, wird hier fündig: http://notes.helsinki.fi/help/help8_designer.nsf/f4b82fbb75e942a6852566ac0037f284/236f3b6a4a38a1028525731b0049a6f0?OpenDocument

 

Leider ist es nur möglich, primitive Datentypen wie int, double, String, usw. zu übergeben - keine Objekte. Und das bringt uns leider zu unserem ersten Problem:

„Wie bekommen wir es hin, ein Notesdokument an eine Java Klasse zu übergeben?“

Jetzt wird jeder sagen „Ganz einfach: Übergib doch die Note ID und hol dir das Dokument aus der Datenbank“. Den Gedankengang hatten wir auch, leider hat sich hieraus ein neues, viel schwerwiegenderes Problem ergeben:

Versucht man, über die bekannten Wege an eine Session zu kommen, wird man kläglich scheitern.

 

Hier mal die Wege, die wir getestet haben:

Versuch 1:

  public class GetSessionV1 {

      public GetSessionV1() {

            NotesThread.sinitThread();

            Session session = NotesFactory.createSession();

      }

}

Resultat: "NotesException: Cannot create a session from an agent"

 

Versuch 2:

  public class GetSessionV2 extends AgentBase{

      public GetSessionV2() {

            Session s= getSession();

     }

}

Resultat: "Session ist null"

 

Den Weg über die DominoUtils haben wir außen vor gelassen, da wir ja nicht auf einer XPage unterwegs sind. Da wir also bis jetzt leider keine Möglichkeit gefunden haben, aus Java heraus das Lotus Notes Backend anzusprechen, ist unsere Idee erstmal auf Eis gelegt. Schade eigentlich.

Vielleicht habt ihr ja eine Idee zu dem Thema?

 

Blob- und Clob-Daten aus einer Oracle- in einer Notes-Datenbank verwenden

1. Oktober 2014 Posted by Christian Annawald

Heute gibt es eine kleine Erweiterung zu unserem Artikel „Oracle-DB mit Java einbinden“, in dem die Vorgehensweise beschrieben wird, wie man eine Verbindung zwischen einer IBM Notes-und einer Oracle-Datenbank aufbaut.

Im heutigen Beitrag gehe ich auf zwei spezielle Datentypen einer Oracle-Datenbank ein, den Blob und den Clob, und darauf, wie man die in ihnen enthaltenen Daten in einer Notes-Datenbank ablegt.

  • Der Blob (Binary Large Object) wird in der Regel verwendet, um ganze Dateien in Form eines BinaryStreams in der Datenbank abzulegen.
  • Der Clob (Character Large Object) ist - wie der Name schon sagt - für beliebig große Zeichenketten gedacht.

Beide Feldtypen benötigen allerdings eine leicht gesonderte Behandlung, um sie in eine Notes-Datenbank zu übertragen.


Als Erstes widme ich mich dem Blob:
Ich gehe in diesem Beispiel davon aus das die Datentabelle in der Oracle-Datenbank folgendermaßen konfiguriert ist:

 

Abb. 1: Aufbau einer Datentabelle in der Oracle-Datenbank

 

Der folgende Java-Agent erstellt pro Zeile, der Oracle-Datenbanktabelle ein Notes-Dokument, in welches die Daten der Zeile eingetragen werden.

import lotus.domino.*;

import java.io.*;

import java.sql.*;

import oracle.jdbc.internal.OracleResultSet;

import oracle.sql.BLOB;

import de.itwu.xKernel.base.ITOBinderElement;

public class JavaAgent extends AgentBase {

        public void NotesMain() {

 

                try {

                        Session session = getSession();

                        Database curdb = session.getCurrentDatabase();

                        //**1** Verbindung zur Oracle-Datenbank aufbauen

                        Class.forName("oracle.jdbc.OracleDriver");

                        Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521/SID", "USER","PASSWORT");

                        conn.setAutoCommit(false);

                        //**2** Datensätze aus Oracle anfordern

                        Statement stmt = conn.createStatement();

                        ResultSet rset = stmt.executeQuery("select * from TABELLE order by id");

                        //**3** Durchlaufen der Datensätze aus Oracle

                        while (rset.next()) {

                                try {

                                        //**4** Neues Notesdokument erzeugen

                                        Document newdoc = curdb.createDocument();

                                        newdoc.replaceItemValue("form", "Bild");

                                        newdoc.replaceItemValue("pic_Name", rset.getString("Name"));

                                        newdoc.replaceItemValue("pic_MimeType", rset.getString("MimeType"));

                                        //**5** Pro Tabellenzeile einen Unterordner erzeugen

                                        String Filename = "C:\\Temp\\"+rset.getString("ID")+"\\" + rset.getString("Name");

                                        File dir = new File("C:\\Temp\\"+rset.getString("ID")+"\\");

                                       dir.mkdir();

                                        //**6** Blob aus dem Oracle-Datensatz lesen und auf der Festplatte ablegen

                                        readBLOBToFileStream(((OracleResultSet) rset).getBLOB("Bild"), Filename);

                                        //**10** Datei in ein Richtextitem einfügen

                                        RichTextItem rtitem = newdoc.createRichTextItem("pic_BildDatei");

                                        rtitem.embedObject(EmbeddedObject.EMBED_ATTACHMENT, null,

                                        Filename, rset.getString("Name"));

                                        newdoc.save();

                                        newdoc.recycle();

                                } catch (Exception e) {

                                System.out.println("Fehler bei ID " + rset.getString("ID"));

                                e.printStackTrace();

                                }

                                finally

                                {

                                System.gc();

                                 }

 

                        }

                        stmt.close();

                } catch (Exception e) {

                        e.printStackTrace();

                }

        }

 

        public void readBLOBToFileStream(BLOB blobdata, String Filename)  throws IOException, SQLException

        {

                File outputBinaryFile = null;

                FileOutputStream outputFileOutputStream = null;

                InputStream blobInputStream = null;

                int chunkSize;

                byte[] binaryBuffer;

                int bytesRead = 0;

                int totBytesRead = 0;

                int totBytesWritten = 0;

                try {

                        //**7** Neue Datei erzeugen

                        outputBinaryFile = new File(Filename);

                        //**8** OutputStream erzeugen

                        outputFileOutputStream = new FileOutputStream(outputBinaryFile);

                        blobInputStream = blobdata.getBinaryStream();

 

                        //**9** Byteweise Daten des Blobs in Datei schreiben

                        chunkSize = blobdata.getChunkSize();

                        binaryBuffer = new byte[chunkSize];

 

                        while ((bytesRead = blobInputStream.read(binaryBuffer)) != -1) {

                                outputFileOutputStream.write(binaryBuffer, 0, bytesRead);

                                totBytesRead += bytesRead;

                                totBytesWritten += bytesRead;

                        }

                outputFileOutputStream.close();

                blobInputStream.close();

                } catch (Exception e) {

                e.printStackTrace();

                }

        }

}


Erläuterungen:

  1. Als Erstes muss der JDBC-Treiber geladen werden. Dazu benutzen wir die Funktion "Class.forName". Diese sucht den passenden Treiber aus der Laufzeitumgebung aus. Anschließend bauen wir mit "DriverManager.getConnection" die JDBC-Verbindung auf. Die IP-Adresse sowie SID, USER und PASSWORD sind natürlich durch die entsprechenden tatsächlichen Werte zu ersetzen. Ggf. ist auch der Port anzupassen (1521).
  2. Hier wird das SQL-Query definiert. Das hier angegebene Query ist natürlich nur als Beispiel zu verstehen. Anschließend wird mithilfe eines Statements und des "ResultSet" das Query ausgeführt.
  3. Das "ResultSet" wird mit einer while-Schleife durchlaufen, um die Ergebnisse auszulesen.
  4. Nun erzeugen wir uns in der Notes-Datenbank ein Notesdocument in das wir ein paar Daten eintragen.
  5. Wir legen für jede Tabellenzeile einen Ordner an (damit wir uns keine Dateien überschreiben), um im Anschluss die Datei aus dem Blob dort abzulegen.
  6. Nun wird die Methode readBLOBToFileStream mit dem Blob aus der Tabellenzeile und dem Dateinamen aufgerufen.
  7. Als Erstes legen wir mit dem Dateinamen eine neue Datei an.
  8. Nun initialisieren wir einen OutputStream mit der Datei und einen InputStream mit den Daten aus dem Blob.
  9. Im Anschluss werden alle Daten des InputStreams in den OutputStream bzw. in die Datei übertragen.
  10. Jetzt wird ein RichtextItem im Notesdocument angelegt und die zuvor erzeugte Datei angehängt.


Nun zu den Clobs:
Um Clobs, in meinem Fall einen beliebig langen Text, auszulesen, habe ich den Weg über den BufferedReader und StringBuffer gewählt.
Der Clob wird also mit Hilfe eines BufferedReaders in einen StringBuffer übertragen, wodurch er im Anschluss als normaler String abgegriffen werden kann:
 

public String ClobToString(Clob clobData)

{

   //**1** Prüfen, ob der Clob nicht leer ist

   if(clobData != null)

   {

      BufferedReader br = new BufferedReader(clobData.getCharacterStream());

      String aux;

      StringBuffer strOut = new StringBuffer();

      //**2**Zeilenweise die Daten des Clobs in den StringBuffer übertragen

      while ((aux = br.readLine()) != null) {

         strOut.append(aux);

         strOut.append(System.getProperty("line.separator"));

      }

   }

   return strOut.toString());

}

Erläuterungen:

  1. Als Erstes prüfen wir, ob der Clob Daten enthält.
  2. Nun übertragen wir zeilenweise den Text des Clobs in einen StringBuffer und geben ihn als normalen Java String zurück.

 

Der Methodenaufruf könnte folgendermaßen ausehen:

...

document.replaceItemValue("CBLOBFELD", this.ClobToString(RESULTSET.getClob("CBLOBFELD")));

...

 

Mit diesen Methoden könnt ihr also auch Daten aus Blobs und Clobs in einer Notes-Datenbank nutzbar machen.

Wenn euch noch andere Stolperfallen bei der Verbindung zwischen Oracle- und Notes-Datenbanken über den Weg laufen, schreibt sie uns doch bitte in die Kommentare.

 

Oracle Application Express 3.1.2 startet nach Rückspielen des Backups nicht mehr

1. Oktober 2014 Posted by Christian Annawald

Letzte Woche sollte ich für ein Kundenprojekt einen Oracle Application Express Server als Entwicklungsumgebung aufsetzen, um anschließend eine Schnittstelle mit einem Domino Server zu realisieren. Dazu sollten die Live-Daten vom Produktivserver auf den neu aufgesetzten Server kopiert werden.

An sich ist das keine große Sache. Als erstes installiert man den Oracle Application Express Server und trägt sich selber in die Windowsgruppe ORA_DBA ein. Nun kopiert man das Flashbackup von dem Originalserver auf den neu installierten Server an dieselbe Stelle im Verzeichnispfad z.B. C:\oraclexe\app\oracle\flach_recovery_area (Default-Pfad).

Als nächstes führt man die Restore.exe im Pfad C:\oraclexe\app\oracle\product\10.2.0\server\BIN aus.

Nun wird automatisch

  1. der Server heruntergefahren,
  2. das Backup eingespielt und
  3. der Server neu gestartet.

Ob alles geklappt hat, kann man im Browser unter der Adresse http://127.0.0.1:8080/apex/ überprüfen. Hier kann man sich nun wie beim Originalserver anmelden.

 

Abb. 1: Anmeldung am Oracle Application Express Server

 

Nun kommt der eigentliche Knackpunkt: Es werden beim Zurückspielen des Backups leider alle Einstellungen des Originalservers übertragen, so auch der Servername für die Datenbank und der Listener. Dies führte in unserem Fall dazu, dass der Listener ohne zuerst erkennbaren Grund nicht mehr startete. Hier kann uns das Logfile des Listener weiterhelfen, welches unter dem folgenden Pfad zu finden ist:
C:\oraclexe\app\oracle\product\10.2.0\server\NETWORK\log\listener.txt (Vorsicht, die Datei kann sehr groß sein!)

Hier sind uns folgende Zeilen entgegengesprungen:

Gestartet mit Pid=1176
Listen auf: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(PIPENAME=\\.\pipe\EXTPROC_FOR_XEipc)))
Fehler beim Hören auf: (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=ORIGINALSERVERNAME)(PORT=1521)))
TNS-12545: CONNECT nicht erfolgreich, da Ziel-Host oder -Objekt nicht vorhanden
 TNS-12560: TNS: Fehler bei Protokolladapter
  TNS-00515: CONNECT nicht erfolgreich, da Ziel-Host oder -Objekt nicht vorhanden
   32-bit Windows Error: 1001: Unknown error
Hört nicht mehr auf: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(PIPENAME=\\.\pipe\EXTPROC_FOR_XEipc)))

Um dieses Problem zu beheben, müssen wir in zwei Konfigurationsdateien den Servernamen des Originalservers mit dem Namen des neu installierten Servers ersetzen. Datei 1 ist die Konfiguration für den Datenbank-Service:

C:\oraclexe\app\oracle\product\10.2.0\server\NETWORK\ADMIN\tnsnames.ora

Hier gibt es den Punkt:


(DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = "SERVERNAME")(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = XE)
    )
  )

Hier sollte letztendlich im Bereich SERVERNAME im einfachsten Fall der Name des lokalen Rechners stehen.

Als zweites sollte man sich im selben Pfad die Datei „Listener.ora“ vornehmen. Hier gibt es denselben Punkt:

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC_FOR_XE))
      (ADDRESS = (PROTOCOL = TCP)(HOST = "SERVERNAME")(PORT = 1521))
    )
  )

Auch hier sollte der Bereich SERVERNAME den Namen des lokalen Rechners enthalten. Sonst kann es passieren, dass der Listener immer noch versucht, sich mit dem Originalserver zu verbinden.

Ich hoffe der Artikel erspart euch eine aufwendige Suche durch die Oracle Foren.


 

Hilfsmaske integriert neue XPages-Funktionen in alte Notes Anwendungen

21. November 2013 Posted by Christian Annawald

Heute wollen wir euch am Beispiel einer Timeline, die wir vor Kurzem in ein aktuelles Kundenprojekt integriert haben, eine simple Möglichkeit vorstellen, wie man alte Notes Anwendungen um moderne Funktionen erweitert.

Die Timeline symbolisiert hierbei einen beliebigen Applikationsbaustein, der auf der XPages-Technologie von IBM Notes und Domino basiert. Derartige Bausteine können in jede Anwendung integriert werden. In diesem konkreten Fall ermöglicht es die Timeline, dass die zeitliche Auslastung von verschiedensten Ressourcen übersichtlich dargestellt wird (siehe Abb. 1). Ob es sich dabei nun um Teile des Maschinenparks,  Lagerplätze oder auch spezialisierte Mitarbeiter handelt, spielt keine Rolle und ist hauptsächlich von der Anwendung abhängig, in die der Baustein integriert wird. Hierbei gibt es ebenfalls vielseitige Szenarien, vom einfachen Zeiterfassungstool zum komplexen Projektmanagement-System.

 

Abb. 1: Auf XPages basierte Timeline

 

Da die Timeline eine XPage ist, könnt ihr von überall her auf die Ressourcenplanung zugreifen - ob vom Client, von euren mobilen Endgeräten, wie Smartphones und Pads, oder ganz einfach vom Webbrowser aus, den Möglichkeiten sind quasi keine Grenzen gesetzt.  Ein weiterer Vorteil der XPages-Technologie gegenüber der herkömmlichen Notes-Entwicklung sind mögliche Funktionalitäten wie das Filtern nach Mitarbeitergruppen und das Wechseln der Ansicht  (z.B.: Wochenansicht, Arbeitswochenansicht oder Monatsansicht), die die Timeline auch auf kleineren Bildschirmen übersichtlich halten.

Die moderne und aufgeräumte Benutzeroberfläche der Timeline ist auf der einen Seite ein großes Plus, auf der anderen Seite aber auch einer der Gründe, warum die XPage nicht ohne weiteres in alte Notes Anwendungen integriert werden kann. Versucht Notes nämlich die XPages mit Hilfe seiner alten Web-Engine zu rendern und darzustellen, werden viele Funktionen und Designs, die auf Javascript oder CSS basieren, nicht korrekt oder gar nicht angezeigt.

Um dieses Problem zu lösen, benötigt man aber einfach nur eine inhaltslose „Hilfsmaske“. Diese wird so in die alte Notes Anwendung integriert, dass sie, sobald Sie angesprochen wird, an ihrer Stelle die XPage in einem herkömmlichen Webbrowser rendern lässt. Um diesen Effekt zu erzielen, müsst ihr in den Masken-Eigenschaften unter dem Default-Reiter lediglich die entsprechende XPage eintragen, die statt der „Hilfsmaske“ angezeigt werden soll, sowohl im Bereich „ On Open“ also auch in „On Web Access“ (siehe Abb. 2). Damit wird die XPage komplett funktionsfähig in einem neuen Fenster des Notes Clients gerendert. Im Webbrowser oder auf mobilen Endgeräten ist die Timeline natürlich wie gewohnt über eine URL erreichbar die folgendermaßen aussieht: “http://SERVERNAME/DATENBANKPFAD/Timeline.xsp“.

 

Abb. 2: Masken-Eigenschaften der Hilfsmaske, an deren Stelle die XPage „Timeline“ geöffnet wird

 

Die dargestellten Ressourcentermine der Timeline können durch einen einfachen Klick auf den entsprechenden Eintrag geöffnet werden. Innerhalb des Notes Clients wird ein Noteslink auf das Dokument erzeugt. Dieser öffnet in einem neuen Fenster den ausgewählten Termin als Notes Dokument, so dass ihr die Einträge auch bearbeiten könnt. Ist kein Notes Client auf eurem Gerät installiert, wird der Termin in einem Web-Dialog mit den wichtigsten Daten angezeigt (Start-, Enddatum, Ersteller, und Beschreibung).

IBM Domino XPages ermöglichen somit aber nicht nur die Überwindung der IBM Notes Systemgrenze. Sie stellen gleichzeitig auch einen Rückkanal zur Steuerung altbekannter und fest integrierter Notes und Domino Applikationen zur Verfügung. Aufgrund der Tatsache, dass XPages vollständig in eurer Domino-Infrastruktur eingebettet sind, könnt ihr eure bereits bestehenden traditionellen Notes Anwendungen ohne Probleme mit modernen Funktionen erweitern –und das nicht nur im Lese- sondern auch im Bearbeiten-Modus.

 

SAP aus IBM Notes und Domino / Java ansprechen

6. November 2013 Posted by Christian Annawald

Heute habe ich euch mal etwas zu einer hilfreichen SAP-Schnittstelle zu IBM Notes und Domino zusammengeschrieben, mit der man z.B.

  • einen Reset des SAP Benutzerpassworts über eine Notes und Domino Anwendung veranlassen,
  • Daten aus Notes und Domino Datenbanken in SAP übertragen oder
  • SAP Dokumente zur Übertragung in Lotus Notes auslesen kann.

Selbstverständlich dürft ihr diese Schnittstelle nur verwenden, wenn ihr eine gültige SAP Lizenz besitzt! Außerdem benötigt ihr einen gültigen SAP Service Marketplace Account, um euch die beiden erforderlichen Installationsdateien unter http://service.sap.com/connectors herunterladen zu können.

Ihr benötigt die Installationsdateien sapjco3.dll und sapjco3.jar. Bitte achtet darauf, dass ihr die richtige Version für euer System benutzt, es gibt nämlich eine 32bit- und eine 64bit-Version. Die zu benutzende Version ist abhängig vom Windows Betriebssystem.

Um die Dateien nun in eine Datenbank einzubinden, legt ihr einen Ordner "libs" unter "Webcontent" im Package Explorer des Domino Designers an und importiert die beiden Dateien in diesen Ordner. Nun macht ihr ein Rechtsklick auf die jar und wählt im Kontextmenü "Build Path" und anschließend "Add to Build Path" aus. Zusätzlich müssen die beiden Dateien noch auf den Server gelegt werden, und zwar an die folgende Stelle: %lotusnotes%\domino\jvm\lib\ext.

Nun den Domino Server einmal neu starten. (Vorsicht: Nach einem Update des Servers kann es sein, dass der Ordner aufgeräumt wird und die .jar- und .dll-Dateien neu an die Stelle kopiert werden müssen!)

Um nicht wie auf der SAP-Seite beschrieben immer eine Datei im Klartext auf die Festplatte legen zu müssen, bietet sich die folgende alternative Vorgehensweise an: Wir erzeugen uns einen eigenen DestinationDataProvider der im einfachsten Fall wie folgt aussieht:

import java.util.*;

import com.sap.conn.jco.ext.*;
public class MyDestinationDataProvider implements DestinationDataProvider {

Map<String, Properties> propertiesForDestinationName = new HashMap<String, Properties>();

         public void addDestination(String destinationName, Properties properties) {

                     propertiesForDestinationName.put(destinationName, properties);

         }

         public Properties getDestinationProperties(String destinationName) {

                     if (propertiesForDestinationName.containsKey(destinationName)) {

                                return propertiesForDestinationName.get(destinationName);

                     } else {

                                throw new RuntimeException("JCo destination not found: " + destinationName);

                     }

         }

         public void setDestinationDataEventListener(DestinationDataEventListener eventListener) {  // nothing to do      }

         public boolean supportsEvents() {  return false;  }

}

 

Die folgende Klasse baut eine Verbindung zum SAP-Server und kann einfach in bestehende Projekte eingebunden werden:

import java.util.*;

import com.sap.conn.jco.*;

import com.sap.conn.jco.ext.DestinationDataProvider;

 

public class SapCon implements Serializable {

       private static final long serialVersionUID = -7048651837683314761L;

       private Properties mConnectProperties;

       private JCoDestination mDestination;

       /**

   * Instantiates a new SapCon object.

   * @param server type: String (IP Adresse des Servers)

   * @param sysNumber type: String (Systemnummer ist eine zweistellige Zahl)

   * @param clientNumber type: String (Die Clientnummer ist Standardmäßig 500 kann aber variieren daher beim SAP support nachfragen)

   * @param sapUser type: String (Ein Benutzer der im Backend eine Verbindung zu SAP aufbauen darf)

   * @param sapUserPw type: String (Passwort des SAPbenutzers)

        * @param language type: String (Sprache in der das SAP System betrieben wird)

        */

       public SapCon(String server, String sysNumber, String clientNumber, String sapUser, String sapUserPw, String language) {

             super();

             try { connectSAP( server,  sysNumber,  clientNumber,  sapUser,  sapUserPw,  language);

      } catch (Exception e) {  e.printStackTrace(); }

   }

       /** Connect sap.

    * @throws Exception*/

       public void connectSAP(String server, String sysNumber, String clientNumber, String sapUser, String sapUserPw, String language) throws Exception {

             // Connect zum SAP System

             mConnectProperties = new Properties();

             mConnectProperties.setProperty(DestinationDataProvider.JCO_ASHOST, server);

             mConnectProperties.setProperty(DestinationDataProvider.JCO_SYSNR, sysNumber);

             mConnectProperties.setProperty(DestinationDataProvider.JCO_CLIENT, clientNumber);

             mConnectProperties.setProperty(DestinationDataProvider.JCO_USER, sapUser);

             mConnectProperties.setProperty(DestinationDataProvider.JCO_PASSWD, sapUserPw);

             mConnectProperties.setProperty(DestinationDataProvider.JCO_LANG, language);

             // Angaben für eine Pooled Verbindung

             mConnectProperties.setProperty(DestinationDataProvider.JCO_POOL_CAPACITY, "3");

             mConnectProperties.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT, "10");

             // Eigenen DestinationDataProvider erzeugen

      MyDestinationDataProvider provider = new MyDestinationDataProvider();

      provider.addDestination("A01", mConnectProperties);

             // provider hinzufügen

     com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(provider);

             mDestination = JCoDestinationManager.getDestination("A01");

             // Provider wieder entfernen sonst gibts beim nächsten mal Hinzufügen

             // eine Fehlermeldung

             try { com.sap.conn.jco.ext.Environment.unregisterDestinationDataProvider(provider);

             } catch (Exception e) { e.printStackTrace();}

   }

       /** Gets the destination.

    * @return the destination    */

       public JCoDestination getDestination() {   return mDestination;     }

}

 

Nun kommen wir dazu, wie man einen RFC(Bapi) im SAP aufruft. RFCs(Bapis) sind Schnittstellen-Funktionen die von außen durch die sapjco-Schnittstelle angesprochen werden können. Hier ist es möglich, Parameter zu übergeben und zu empfangen. Die Funktionalität dieser RFCs kann durch einen fachkundigen ABAP-Entwickler festgelegt werden z.B. Reset des SAP Benutzerpassworts, Übertragung von Notesdatebanken in SAP, Auslesen von SAP Dokumenten zur Übertragung in Lotus Notes usw.

Hier der entsprechende Code mit kleinen Beispielen:

import java.util.*;

import com.sap.conn.jco.*;
 

public class RFCCaller {

         private SapCon sapCon;

         public void runBapi() throws Exception {

                    // RFC Aufrufen

                    JCoFunction function = sapCon.getDestination().getRepository().getFunction("%RFC namen eintragen%");

                    if (function == null)

                                  throw new RuntimeException("RFC im System nicht gefunden");

                    // setzen eines Skalaren Importparameters

                    function.getImportParameterList().setValue("%PARAMTER NAME%", "PARAMTER als STRING");

                    // setzen einer Tabelle als Importparameters

                    JCoTable sapTable= function.getImportParameterList().getTable("%TABELLEN NAME%");

                    sapTable.appendRow();

                    sapTable.setValue(0, "Wert des Feldes");

                    // RFC ausführen

                    function.execute(sapCon.getDestination());

                    // auslesen einer Tabelle mit einer spalte

                    JCoTable antwortTabelle = function.getTableParameterList().getTable("ANTWORT TABELLE");

                    for (int i = 0; i < antwortTabelle.getNumRows(); i++)

                    {           antwortTabelle.setRow(i);

                                Iterator<JCoField> it = antwortTabelle.iterator();

                                while (it.hasNext()) {

                                           JCoField field = it.next();

                                           System.out.println(field.getName()+":"+field.getString());

                                }

                    }

                    // Auslesen einer Struktur

                    JCoStructure exportStructure = function.getExportParameterList().getStructure("%STRUKTUR NAME%");

                    for (JCoField field : exportStructure) {

                                 if (field.getName().equalsIgnoreCase("FELDNAME")) {

                                              System.out.println(field.getString());  }

                    }

         }

}

 

Weitere Infos findet ihr in der SAP Online Dokumentation. Viel Spaß beim SAP ansprechen! Falls ihr Fragen habt, ruft uns bitte einfach an (05251 288160) oder schreibt uns ein Mail (info@itwu.de).

 

Java für IBM Domino XPages – Teil 2: Wie man mit Hilfe von Java in den Domino XPages Dateien in den Resources verarbeitet

23. Oktober 2013 Posted by Christian Annawald

In der Version 8.5.3 von IBM Notes und Domino können wir recht einfach mit Hilfe von Java in XPages auf Dateien in den Resources zugreifen und diese somit verarbeiten:

Die Klasse "ClassLoader" gibt uns die Möglichkeit, zur Laufzeit der Anwendung Dateien anhand ihrer URL zu laden. Dazu wird mit folgendem Ausdruck ein Classloader Objekt erzeugt:

ClassLoader classLoader = com.ibm.domino.xsp.module.nsf.NotesContext.getCurrent().getModule().getModuleClassLoader();

Durch die folgende Zeile wird die Resource geladen und als Inputstream verfügbar gemacht. Hierbei ist darauf zu achten, dass die URL vollqualifiziert sein muss (http://%SERVER%/%DATENBANKPFAD%/%DATEINAME).

InputStream inStream = classLoader.getResourceAsStream(new java.net.URL("URL").getFile());

 

Java für IBM Domino XPages – Teil 1: Mit Java auf den Kontext der aktuellen XPage zugreifen

15. Oktober 2013 Posted by Christian Annawald

Viele haben sich bestimmt schon mal gefragt "Wie komme ich eigentlich in Java an den Kontext der aktuellen XPage?". Hier kommt die Antwort für Anwendungen, die mit IBM Domino Version 8.5.3 und höher entwickelt werden:

Über die folgenden zwei Zeilen wird mit Hilfe des "FacesContext" der "XSPContext" initialisiert.

FacesContext facescontext = FacesContext.getCurrentInstance();
XSPContext context= ServletXSPContext.getXSPContext(facescontext );

Nun lassen sich über die Variable "context" Funktionen wie "getURL()", "getUrlParameter(arg0)" oder "getUser()"
aufrufen - ganz so als wäre man im SSJS auf der XPage.