ReplaceSubstring in LotusScript

Full code for the function is inserted below as well as an example.

Please note that the code may wrap…

Sub Initialize
   'declarations
   Dim s As New NotesSession
   Dim dc As NotesDocumentCollection
   Dim doc As NotesDocument
   Dim result As Variant

   Dim look_for(1) As String
   look_for(0) = TEST_OLD_URL_RESOURCE
   look_for(1) = TEST_OLD_URL_SCANNET

   Dim replace_with(1) As String
   replace_with(0) = TEST_NEW_URL_RESOURCE
   replace_with(1) = TEST_NEW_URL_SCANNET

   Dim process_fields(5) As String
   process_fields(0) = "txtLinksDisplayData_1"
   process_fields(1) = "txtLinksDisplayData_2"
   process_fields(2) = "txtLinksTargetData_1"
   process_fields(3) = "txtLinksTargetData_2"
   process_fields(4) = "txtLinks_1"
   process_fields(5) = "txtLinks_2"

   Set dc = s.CurrentDatabase.UnprocessedDocuments
   Set doc = dc.GetFirstDocument
   While Not (doc Is Nothing)
      'loop fields and replace
      Forall field In process_fields
         result = ReplaceSubstring(doc.GetItemValue(field), look_for, replace_with)
         Call doc.ReplaceItemValue(field, result)
      End Forall

      'save
      Call doc.Save(True, False)

      'get next
      Set doc = dc.GetNextDocument(doc)
   Wend
End Sub
Public Function ReplaceSubstring(look_in As Variant, look_for As Variant, replace_with As Variant) As Variant
   'validate parameters
   If Isarray(look_for) And Isarray(replace_with) Then
      'both are arrays - make sure they are the same size
      If Ubound(look_for)  Ubound(replace_with) Then
         'different size
         Error 9999, "The look_for and replace_with arrays must be of the same size."
      End If
   Elseif (Isarray(look_for) Xor Isarray(replace_with))  Then
      'either look_for is an array and replace_with isn't or nice versa
      Error 9999, "If look_for is an array so must replace_with or vice versa."
   End If

   If Isarray(look_in) Then
      'handle all entries in the array
      Dim result_array() As Variant
      Dim result As Variant
      Redim result_array(Ubound(look_in))
      Dim count As Integer

      'loop
      Forall v In look_in
         'make sure we do not run on objects
         If Not Isobject(v) Then
            'do replace
            result = pReplaceSubstring(v, look_for, replace_with)

            'store result in array
            result_array(count) = result
         Else
            'keep original value
            Set result_array(count) = v
         End If

         'increment count
         count = count + 1
      End Forall

      'return result
      ReplaceSubstring = result_array
   Else
      'handle single value - make sure we do not run on objects
      If Not Isobject(look_in) Then
         'do replace
         ReplaceSubstring = pReplaceSubstring(look_in, look_for, replace_with)
      Else
         'return original value
         Set ReplaceSubstring = look_in
      End If
   End If
End Function

Private Function pReplaceSubstring(Byval look_in As Variant, look_for As Variant, replace_with As Variant) As Variant
   'make sure the input is a string
   If Typename(look_in)  "STRING" Then
      'return original value
      If Isobject(look_in) Then
         Set pReplaceSubstring = look_in
      Else
         pReplaceSubstring = look_in
      End If
      Exit Function
   End If

   'is there an array of things to look for ?
   If Isarray(look_for) Then
      'yes - handle all values
      Dim i As Integer

      'loop - arrays garanteed to be the same size be calling function
      For i=0 To Ubound(look_for)
         'do replacesubstring
         look_in = pReplaceSingleValue(look_in,look_for(i), replace_with(i))
      Next

      'return
      pReplaceSubstring = look_in
   Else
      'nope - single value
      pReplaceSubstring = pReplaceSingleValue(look_in,look_for, replace_with)
   End If

End Function

Private Function pReplaceSingleValue(look_in As Variant, look_for As Variant, replace_with As Variant) As String
   'declarations
   Dim index_start As Integer
   Dim index_stop As Integer
   Dim first_part As String
   Dim last_part As String
   Dim result As String

   'look for the value
   index_start = Instr(1, look_in, look_for)
   If index_start > 0 Then
      'we found the value - get the part before what we are
      'looking for (if not at the beginning)
      If index_start > 1 Then
         first_part = Mid$(look_in, 1, index_start-1)
      End If

      'calcuate the stop index
      index_stop = index_start + Len(look_for)

      'get the last part (if not past the end of the string)
      If index_stop < Len(look_in) Then
         last_part = Mid$(look_in, index_stop)
      End If

      'concatenate the parts
      result = first_part + replace_with + last_part

      'return
      pReplaceSingleValue = result
   Else
      'substring not found - return the original value
      pReplaceSingleValue = look_in
   End If
End Function

Finally got JBoss 3.2.6 to connect to DB2 7.1 under Linux

SQL1224N A database agent could not be started to service a request,
or was terminated as a result of a database system shutdown
or a force command. SQLSTATE=55032

This was the error I kept getting without being able to find out why. A couple of things I have tried:

  • Upgrading JBoss from 3.2.6 to 3.2.7 (which made me try point 2 since 3.2.6 doesn’t run under Java SDK 1.3.1 even though it says so on jboss.org)
  • Downgrading from Sun Java 1.4.2_07 to IBM Java 1.3.1
  • Upgrade to various DB2 Fixpacks culminating with Fixpack 13

I found the solution as technote 1044438 on IBM’s website (I also took a PDF print to make sure I could find it in the future). The technote only mentions AIX but the solution works on Linux as well.

The solution is to create another DB2 node pointing to the same DB2 server and cataloging the database using this “dummy” node instead of directly using the local protocol. I have not idea why this solves the problem.

Caveat when using Sametime in iNotes

Make sure the STLinks files can be correctly loaded in iNotes. This depends on setting the correct hostname in the “Fully qualifies hostname” field on the server document of the Sametime server and making sure the protocol used to fetch files from the Sametime server using HTTP is the same as the protocol used for iNotes. This means that if iNotes runs over HTTPS, iNotes will also try to fetch Sametime files from the Sametime server using HTTPS. If the Sametime server doesn’t support HTTPS set the iNotes_WA_SametimeProtocol notes.ini variable to http: (the trailing colon is important). This will tell iNotes to use HTTP instead of HTTPS in the above example.

Creating HTML using DOM and JavaScript

We use a lot of XML fetching in our web applications to fetch information from the server and processing it with JavaScript afterwards. What we normally do is to have a target division hardcoded in the correct location on the page and use JavaScript to add content.

The below example is taken out of context and it utilizes a custom JavaScript object that does all the dirty work and calls the processXML(boolean success) method once the XML is ready for processing. The interesting part is where the HTML is dynamically built using the createElement(String name) and appendChild(Element e) functions.

Searcher.prototype.processXML = function(success) {
  // how did we fare ?
  if (!success) {
    window.status = "Something went wrong while getting the XML while searching - aborting...";
    return;
  }

  try {
    // loop databases
    var databases = this.getElementsByTagName("database");
    if (null == databases) return;
    for (var i=0; i<databases.length; i++) {
      // get next database
      var database = databases[i];

      // get the title and path of the database
      var db_title = this.getTagValue(database.childNodes, "title");

      // get all results for database
      var results = null;
      for (var j=0; j<database.childNodes.length; j++) {
        if (database.childNodes[j].tagName == "results") {
          results = database.childNodes[j];
        }
      }

      // get result count
      var r_count = results.getAttribute("count");

      // create element for db title
      var elem_db = document.createElement("div");
      elem_db.id = "database" + i;
      elem_db.innerHTML = db_title + " (" + r_count + " resulter)";
      elem_db.className = "searchresults_database";
      elem_db.onmouseover = changeCursor;
      elem_db.onmouseout = changeCursor;
      elem_db.onclick = clickDatabase;
      this.pResults.appendChild(elem_db);

      // create element for result section (collapsed)
      var elem_results = document.createElement("div");
      elem_results.id = "results" + i;
      elem_results.className = "searchresults";
      elem_results.style.visibility = "hidden";
      elem_results.style.display = "none";
      this.pResults.appendChild(elem_results);

      // loop results
      for (var j=0; j<results.childNodes.length; j++) {
        if (results.childNodes[j].tagName == "result") {
          // get next resultsitem
          var r = results.childNodes[j];

          // get title and key
          var r_title = this.getTagValue(r.childNodes, "title");
          var r_key = this.getTagValue(r.childNodes, "key");
          var r_link = this.getTagValue(r.childNodes, "link");

          // add link anchor
          var elem_link = document.createElement("a");
          elem_link.href = r_link;
          elem_link.className = "searchresult";
          elem_link.appendChild(document.createTextNode(r_title));
          elem_results.appendChild(elem_link);
          elem_results.appendChild(document.createElement("br"));
        }
      }
    }

  } catch (exception) {
    // some error occured
    window.status = "Exception caught in Searcher.processXML(): " + exception.message;
  }
}

One thing I found out is that the Microsoft XML component allows for asynchroneous fetching. This allows you to show a progress bar or similar while loading and processing in the background.

function go() {
   // set to fetch asynchroneous
   this.pXML_data.async = true;
   // set callback method
   this.pXML_data.onreadystatechange = waitForXml;
   // go fish !!
   var success = this.pXML_data.load(url);
}

function waitForXml() {
   // wait for document to be completely loaded
   if (gXML_handler.getXmlIsland().readyState == "interactive") {
      // document has been loaded - process it
      processXML();
   }
}

function processXML() {
   // do processing...
}

Compile Apache 2 with mod_ssl

When configuring Apache 2.0 before compiling (make) and you are enabling mod_ssl you sometimes get an error saying that the OpenSSL headers cannot be found. To fix this make sure to configure using the –with-ssl=<path to OpenSSL installation> parameter.

Connecting to IBM DB2 v.8.x using JDBC

Trying to configure JDBC for DB2 v.8.x I had an error messages that just wouldn’t go away:

java.lang.UnsatisfiedLinkError: no db2jdbc in java.library.path

The problem appeared to be configuration issues and the fact that DB2 v.8 ships with a new universal JDBC driver. There are a number of things that are different about the new driver:

  • New driver jar-file (db2jcc.jar)
  • Requirement for a license jar-file (in my case I guessed that the required license jar-file was db2jcc_license_cu.jar since it was the only one in /opt/IBM/db2/V8.x/java)
  • Change driver class name to
    com.ibm.db2.jcc.DB2Driver
  • Make sure the native library are in the library path if using the type 2 driver (JDBC against a local DB2 installation):

    export DB2PATH=/opt/IBM/db2/V8.1
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$DB2PATH/lib
    

The JDBC URL syntax is the same as previously.

If you forget adding the DB2 directory to the LD_LIBRARY_PATH you’ll get an error message saying:

com.ibm.db2.jcc.b.SqlException: Failure in loading T2 native library db2jcct2

Installing IBM DB2 v. 8.x on Linux without X11

To install DB2 manually follow the below steps:

  1. Download DB2 v.8.x for Linux (I downloaded v. 8.1 Express) and transfer the gzip’ed tar-file to the Linux box.
  2. Unpack the distribution to the /tmp directory:

    tar zxf DB2ExE82_Linux.tar.gz -C /tmp
    
  3. Change to root and go to the unpacked DB2 directory and start the installation:

    cd /tmp/Linux
    ./db2_install
    
  4. Select the package to install – I chose “DB2.EXP” since I was installing DB2 Express.
  5. Create an user for the DB2 instance and an user for the administration server (make sure the users belong to a valid group and that the users have a home directory):

    useradd db2inst1
    useradd db2as
    groupadd db2inst1
    groupadd db2as
    mkdir /home/db2inst1
    mkdir /home/db2as
    chown db2inst1:db2inst1 -R /home/db2inst1
    chown db2as:db2as -R /home/db2as
    <associate the group with the user e.g. using yast under SuSE or manually editing
    the /etc/groups file if you are so inclined>
    
  6. Create an instance:

    cd /opt/IBM/V8.1/instance
    ./db2icrt -u db2inst1 db2inst1
    
  7. Create the administration server:

    cd /opt/IBM/V8.1/instance
    ./dascrt -u db2as
    
  8. Set the environment for the db2inst1 user by adding the following to the users ~/.profile:

    ./sqllib/db2profile
    export DB2INSTANCE=db2inst1
    export DB2PATH=~/sqllib
    export PATH=$PATH:$DB2PATH/bin
    
  9. Log in as the db2inst1 user and check the installation by creating the SAMPLE database:

    su - db2inst1
    db2sampl
    

Sources: