Specified Content-Type not always honored Notes 6.5.x

I normally use a simple document for stylesheets and JavaScript libraries in Notes. The form has a field for the “filename” (e.g. my_lib.js) and a field for the data. Until now I had been using a richtext field called HTML for the contents and changed the Content-Type on the form properties as appropriate (text/javascript, text/css etc.). This was working nicely until I created a page using the Strict XHTML DOC-TYPE.

It became an issue because Firefox requires that the Content-Type is text/css for stylesheets when rendering in Strict mode. Otherwise the stylesheet wont be used. Using the Live HTTP Headers extension for Firefox I saw that Domino kept setting the Content-Type to text/html even though the form properties was set to text/css.

Changing the richtext field name from HTML to Body solved the problem. Apparently Domino will not honor the specified Content-Type when the form has a field called HTML.

One of those things in Domino…

Blog split and lessons about mod_rewrite in Apache

I split the blog using the multi-user feature of Pebble (the blogging software I use). The actual split was quite easy and took about an hour, the main part of the time spent to separate the posts into the blogs where they belonged.

After the split each blog reside in their own directory web-wise, as opposed to being in the root (/) of the server. This changes the URL you use to access the main blog (the one you are reading now). My main concern was therefore to keep existing links working. This was accomplished using mod_rewrite on Apache to make this blog reside at the root as well as in the new directory.

Having never really delt with mod_rewrite I had figure that out first but it turned out to be simpler than what I had expected. I just added a couple of RewriteRules to the virtual host and reloaded Apache. Simple enough (apart from the trial-and-error process of figuring out the quirks with the regular expressions and mod_rewrite flags).

I now use the following RewriteRules:

RewriteEngine    on
RewriteLog       /usr/local/apache2/logs/rewrite.log
RewriteLogLevel  0
RewriteRule      ^/index.jsp                              /index.jsp          [L]
RewriteRule      ^/(.*)(themes/default|.css|.js)        /$1$2               [PT]
RewriteRule      ^/training$                              /training/          [R,L]
RewriteRule      ^/training/(.*)                          /training/$1        [L]
RewriteCond      %{REQUEST_URI}                           !(^/lekkimworld/.*)
RewriteCond      %{REQUEST_URI}                           !(^/(.*).css)
RewriteRule      ^/(.*)                                   /lekkimworld/$1     [PT]

Line 4 is used to make sure I access the overview page for the blogs. Line 8-10 is used to make sure that any request for a page not being explicitly for the /lekkimworld sub-blog is redirected to the lekkimworld blog. There is no need to handle the /training blog as well since RewriteRules are processed in a top-to-bottom approach so any requests for that blog has already been handled.

The mod_rewrite flags on the right [xxx] is used to signal how the RewriteRule should be processed. If no flag is specified the processing will continue down the chain. By adding a [L] flag to a rule, that rule will be the last one processed with the RegExp matches the request. [R] makes Apache send a HTTP code 302 back to the browser for a redirect. [PT] is used to pass the request through to the content handler without any further processing.

LotusScript.doc is being well received

Based on the feedback I have received in regards to the annoucement of LotusScript.doc I think it is safe to say that this is a tool developers has been missing. I have received a number of e-mails from people who would like to enroll as “beta-testers” so that’s very nice.

I plan to start the beta-program on Monday and then target a release to the community the coming week (15 August). I just need to finish up the user documentation before sending the application to the beta-testers.

Stay tuned…

Introducing LotusScript.doc

This blog post is to introduce LotusScript.doc to the Notes developer community. LotusScript.doc is a tool I wrote that aims to be for LotusScript what javadoc is for Java.

Why LotusScript.doc?

Notes applications are very hard and time consuming to document. When we think about documenting Notes applications we normally only document the actual application (the user manual) since there isn’t a standard way of documenting the LotusScript code – until now…

LotusScript.doc is a Notes application you can use to generate HTML documentation for LotusScript code in Notes databases. The produced format is the same as for javadoc.

To find out more head over to the project website at www.lsdoc.org.

Using JavaScript to generate table of contents for a webpage

Combing the DOM (Document Object Model) with JavaScript makes it easy to dynamically build the TOC. Now I only have to add extra sections and they are automatically added to the TOC. Clicking the item in the TOC will go to the section on the page.

An easy solution. Nice.

Code

[<ul id="toc"></ul>

function buildTOC() {
   // get bullet list holder
   var toc = document.getElementById("toc");

   // get all anchors on the page
   var anchors = document.anchors;
   for (var i=0; i]
[<a name="section1" class="no_style">]Section 1[</a>]
Ut dolore veniam ullamcorper iriure molestie in commodo aliquip sed in enim accumsan iriure iusto. Dolore dolore eu tation ut hendrerit tincidunt vulputate nulla. Exerci, vulputate velit, quis dignissim minim, blandit nonummy facilisis at blandit augue nostrud magna nonummy, wisi nulla consequat. Enim odio, in nostrud zzril ut consequat accumsan delenit feugait suscipit volutpat ut et at. Augue nisl praesent molestie qui ut dolore eum minim vel dolor luptatum vero magna iusto et.

[<a name="section2" class="no_style">]Section 2[</a>]
Consequat exerci feugiat eum augue accumsan feugiat blandit odio. Tation te, vel dolore duis te commodo elit qui at in feugait iriure vero, ullamcorper nostrud lorem ipsum feugiat, erat ex. Eros vel nonummy zzril wisi quis consequat in autem ut suscipit amet, amet erat at duis minim in molestie.

[<a name="section3" class="no_style">]Section 3[</a>]
Eu esse at et feugait elit, lorem, zzril, blandit eum facilisi feugiat, commodo nulla ut vel tation euismod accumsan, qui. Duis, in amet vel esse ad, ad vero vel facilisis minim in nisl laoreet iriure tation eros molestie dolor. Odio vel quis nostrud autem dolor et exerci vulputate augue erat in nisl iusto dolore vulputate, aliquam, laoreet ullamcorper dignissim dolor adipiscing ad qui. Hendrerit te at diam ea quis, aliquip molestie wisi erat praesent, luptatum adipiscing nonummy nostrud zzril nisl consequat et euismod. Enim ullamcorper consequat lobortis, minim wisi augue nisl diam, esse suscipit duis ex eum odio dolore, ut ut. Delenit ex iusto et ex, ex tincidunt illum luptatum eu te in in feugiat minim augue, sit dolore blandit iusto, consequat duis vel nulla duis at.

Result

Writing a custom Tomcat login module (Realm)

In the project I am working on we needed to move the users from LDAP to a database table due to a switch from JBoss to Tomcat as the application server. It was a little complicated since the passwords was hashed with SHA1 in LDAP and we needed the cleartext passwords since we needed to encrypt it in another way. To avoid having users switch their password I wrote a custom login module, called a Realm, in Tomcat. It was really simple.

When logging in the login module will do the following:

  1. Check whether the username is registered in the database. If it is the user is authenticated against the database.
  2. If no username/password is found in the database we go to the LDAP server and try to authenticate the user there.
  3. If the user was authenticated using LDAP we update the database with the username and password supplied before returning a Principal.
  4. If we still haven’t authenticated we return null to indicate that we could not authenticate the user.

Simple and effective.

Writing a custom Realm

A realm is defined by the org.apache.catalina.Realm interface. I opted to extend the org.apache.catalina.realm.RealmBase class instead of implementing the interface since it meant I only had to override one method:

public class LdapDbRealm extends RealmBase {

   // accessors for realm configuration (database) *******************
   public String getDbUsername() {
      return this.pDbUsername;
   }
   public void setDbUsername(String dbUsername) {
      this.pDbUsername = dbUsername;
   }
   ...
   ...

    // accessors for realm configuration (ldap) *******************
    public String getLdapPassword() {
      return this.pLdapPassword;
   }
   public void setLdapPassword(String ldapPassword) {
      this.pLdapPassword = ldapPassword;
   }
   ...
   ...

    // methods from super class ***************************************

   /**
    * This method is the work-horse of the class. The implementation goes
    * through the follow steps:
    * <ol>
    * <li>Checks whether the username is registered in the database. If it is the
    * user is authenticated against the database.</li>
    * <li>Next we go to the LDAP server and checks whether the user can be authenticated
    * there.</li>
    * <li>If the user was authenticated using LDAP we update the database with the
    * username and password supplied before returning a Principal.</li>
    * </ol>
    *
    * @see org.apache.catalina.realm.RealmBase#authenticate(java.lang.String, java.lang.String)
    */
   public Principal authenticate(String username, String credentials) {
      ...
      ...
   }

}

The accessor methods are used to set information from the server.xml document where the Realm is configured for the Context:

<Context path="/guide" docBase="guide.war" debug="0" reloadable="true" crossContext="true">
   <Logger className="org.apache.catalina.logger.FileLogger"
      prefix="hgguide_log." suffix=".txt" verbosity="4" timestamp="true"/>

   <!-- configure login realm -->
   <Realm className="dk.horisontnet.guide.login.LdapDbRealm"
      dbDriverName="COM.ibm.db2.jdbc.app.DB2Driver"
      dbUrl="jdbc:db2:guide"
      dbUsername="db_user"
      dbPassword="password"
      ldapUsername="cn=Manager, o=Example"
      ldapPassword="password"
      ldapSearchBase="ou=Users, ou=Guide, o=Example"
      ldapUrl="ldap://localhost"
   />

   ...
   ...

</Context>

All the attributes to the <Realm /> tag should have a matching accessor pair in the Realm implementation so they can be set by Tomcat at startup. A caveat is that you need to define the Realm implementation in a MBean descriptor and add the descriptor to the ServerLifecycleListener at the top of the server.xml:

<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener"
            debug="0"
            descriptors="/com/example/realm/mbean-descriptors.xml" />

The descriptor file looks like this:

<?xml version="1.0"?>
<!DOCTYPE mbeans-descriptors PUBLIC
 "-//Apache Software Foundation//DTD Model MBeans Configuration File"
 "http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
<mbeans-descriptors>
   <mbean name="LdapDbRealm" className="org.apache.catalina.mbeans.ClassNameMBean"
      description="Implementation of custom Realm"
      domain="Catalina"
      group="Realm"
      type="com.example.realm.LdapDbRealm">

      <attribute name="debug"
         description="The debugging detail level for this component"
         type="int"/>

      <attribute   name="dbDriverName"
         description="The JNDI named JDBC DataSource for your database"
         type="java.lang.String"/>

      <attribute   name="dbDsName"
         description="The column in the user role table that names a role"
         type="java.lang.String"/>

      ...
      ...
   </mbean>

</mbean-descriptors>


Installation

The realm class and the descriptor is installed in Tomcat by adding it to the server.xml as described above and by copying the class files to the %CATALINA_HOME%serverclasses directory together with the descriptor file. The path in the Listener tag (see above) should match the path of the descriptor.

Custom Principal and ClassCastException problems

I created my own java.security.Principal implementation that I return from the authenticate method of Realm implementation since I need some additional information about the user. I then tought that I simply could cast the java.lang.Principal returned by the HttpServletRequest.getUserPrincipal() method to my implementation class but i kept getting java.lang.ClassCastExceptions. 🙁

The solution could be found in the Tomcat wiki and was to use reflection. This is due to the fact that the class is loaded by different classloaders and hence are different as seen by Tomcat.

Beware of runtime dependencies (added 8/Feb/2006)

I have just upgraded the Tomcat instance the Realm is used in from Tomcat 4.1.x to 5.5.x which proved difficult since the RealmBase class being extended has changed. Please see here for more information. The lesson is to make sure to compile the Realm against the actual version of Tomcat you are deploying on.

Deploying Sametime

We started by dividing the users into groups:

  • Group 1: IT department and support staff
  • Group 2: Management and super users
  • Group 3: Office personnel
  • Group 4: All the rest (incl. mobile users)

We roll Sametime out in a stepwise fashion starting with group 1 and once they have been running it for 2 weeks we proceed to group 2 and so on. This is done to lessen the effect on the helpdesk and to make sure there are some internal ambassodors for the product before releasing it to the wild crowd.

To roll it out we simply created an explicit policy called /Sametime. The policy only has a Desktop Setting and it used to maintain the Sametime server name. We assigned the policy to the chosen users using the Domino Administrator instead of having to manually do it.

We have been using the IM_DISABLED notes.ini setting set to 1 to disable Sametime from loading in Notes 6.5.x. To configure Sametime without having to visit every user we created a welcome e-mail with a button users could click. Once the button was clicked and Sametime configured the documentation was shown for users to print. This had more users click the button.

The button does the following:

  • Removes the IM_DISABLED notes.ini setting (to enable the Sametime functionality)
  • Sets the IM_SHOW_STATUS notes.ini setting (show online status on names)
  • Sets the IM_ENABLE_SSO notes.ini setting (enable Sametime Single-Sign-On)
  • Sets the Sametime server in the active location document
  • Prompts the user to restart Notes

Once Notes is restarted Notes prompts the user for the HTTP password and the user is laughing. Sweet and simple.

We set the Sametime server in the active location document to make sure Sametime will kick in once Notes is restarted. To set it we utilized my LotusScript LocationDocument class:

Dim ld As New LocationDocument("")
Set ld.SametimeServer = New NotesName("sametime1/Example")
Call ld.Save()

So far we are rolling out to Group 2 and most users are happy. We have a few being concerned about the “big brother watching you” effect but that is probably just a matter of time until they see the benefits. Of cause they can also turn of Sametime, though we don’t encourge it.

The only problem we have had is with the HTTP password for users not using iNotes. This is a major headache and it is difficult to explain the customer that “Single-Sign-On” doesn’t really mean exactly that. I can understand them though. It would be really nice if Notes could just “handle” the authentication to Sametime. I mean the user has already been authenticated using the certificate in his id-file, which is way stronger than plain text passwords.

I hope Lotus is thinking about this as they continue to bundle Notes and Sametime.