wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

By Date: January 2016

The quick and dirty Domino Cloudant export


Moving data out of Domino never has been hard with all the APIs available. The challenge always has been: move them where? Ignoring for a second all security considerations, the challenge is to find a target structure that matches the Domino model. Neither flat table storage nor RDBMS fit that very well.
A close contender is MongoDB which is used in one compelling Notes retirement offering. However the closest match in concept and structure is Apache CouchDB, not surprisingly due to its heritage and origin.
It is maintained by a team led by the highly skilled Jan Lehnardt and of course there are differences to Notes.
But the fit is good enough. Using the lightweight Java library Ektorp exporting a set of documents from Notes to CouchDB is a breeze. The core class is a simple mapping of a Notes document to a JSON structure:

package com.notessensei.export;

import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import lotus.domino.Document;
import lotus.domino.Item;
import lotus.domino.NotesException;

public class NotesJsonDoc {
 public static String ID_FIELD = "_id";
 public static String REV_FIELD = "_rev";
 
 private Map<String, String> content = new HashMap<String, String>();
 
 @SuppressWarnings("rawtypes")
 public NotesJsonDoc(Document source) throws NotesException {
  Vector allItems = source.getItems();
  for (Object itemObject : allItems) {
   Item item = (Item) itemObject;
   this.content.put(item.getName(), item.getText());
  }
  this.content.put(ID_FIELD, source.getUniversalID());
 }
 
 public Map<String, String> getContent() {
  return this.content;
 }
 
 public NotesJsonDoc setRevision(String revision) {
  this.content.put(REV_FIELD, revision);
  return this;
 }
 
 public String getId() {
  return this.content.get(ID_FIELD);
 }
}
##
The uniqueID used in Notes can be used as the ID in CouchDB, thus make it easy to even update documents both ways if required. In your agent you need to create a Java collection consisting of the unids of the documents you want to export. You could do that using a view or the document collection. Here you go:

package com.notessensei.export;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.Collection;
import java.util.Map;
import java.util.Vector;

import org.ektorp.AttachmentInputStream;
import org.ektorp.CouchDbConnector;
import org.ektorp.CouchDbInstance;
import org.ektorp.http.HttpClient;
import org.ektorp.http.StdHttpClient;
import org.ektorp.impl.StdCouchDbInstance;

import lotus.domino.EmbeddedObject;
import lotus.domino.NotesException;
import lotus.domino.Session;
import lotus.domino.Database;
import lotus.domino.Document;

public class DataUploader {

 private CouchDbConnector getDB(String protocol, String credentials, String targetURL, String couchDBName)
   throws MalformedURLException {
  try {
   HttpClient httpClient = new StdHttpClient.Builder().url(protocol + credentials + targetURL).build();
   CouchDbInstance dbInstance = new StdCouchDbInstance(httpClient);
   // if the second parameter is true, the database will be created if
   // it doesn't exists
   CouchDbConnector db = dbInstance.createConnector(couchDBName, true);
   return db;
  } catch (Exception e) {
   e.printStackTrace();
  }
  return null;
 }

 public void uploadData(Session s, Database nsf, Collection<String> unids, String protocol, String credentials,
   String targetURL, String couchDBName) throws MalformedURLException {
  CouchDbConnector db = this.getDB(protocol, credentials, targetURL, couchDBName);
  String dbURL = protocol + targetURL + "/" + couchDBName + "/";

  for (String unid : unids) {
   Document doc = null;
   try {
    doc = nsf.getDocumentByUNID(unid);
    this.uploadOneDocument(s, dbURL, doc, db);
   } catch (NotesException e) {
    e.printStackTrace();
   } finally {
    try {
     if (doc != null) {
      doc.recycle();
     }
    } catch (NotesException e) {
     // no action required
    }
   }
  }
 }

 @SuppressWarnings("rawtypes")
 private void uploadOneDocument(Session s, String dbURL, Document doc, CouchDbConnector db) throws NotesException {
  String unid = doc.getUniversalID();
  String docURL = dbURL + unid;
  NotesJsonDoc payLoad = new NotesJsonDoc(doc);
  // Capture the directURL to the JSON document
  payLoad.getContent().put("docURL", docURL);
  
  // Create document
  db.create(payLoad.getContent());
  
  // Handle attachments
  try {
   if (doc.hasEmbedded()) {
    Vector aNames = s.evaluate("@AttachmentNames", doc);
    if (!aNames.isEmpty()) {
     Map existingDoc = db.get(Map.class, payLoad.getId());
     String rev = existingDoc.get(NotesJsonDoc.REV_FIELD).toString();
     for (Object aNameObj : aNames) {
      this.uploadAttachment(doc, db, rev, aNameObj.toString());
     }
    }
   }
  } catch (NotesException e) {
   e.printStackTrace();
  }
 }

 /*
  * Uploads attachments to the Cloudant database
  */
 private void uploadAttachment(Document doc, CouchDbConnector db, String rev, String attName) {
  try {
   EmbeddedObject embObj = doc.getAttachment(attName);
   String attMime = this.getMimeType(attName);
   InputStream embeddedInput = embObj.getInputStream();
   AttachmentInputStream attStream = new AttachmentInputStream(attName, embeddedInput, attMime);
   db.createAttachment(doc.getUniversalID(), rev, attStream);
   embeddedInput.close();
   embObj.recycle();
  } catch (NotesException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }

 /*
  * Returns the mime type based on the extension only since we don't have a
  *file on the file system
  */
 private String getMimeType(String attName) {
  String ext = attName.substring(attName.lastIndexOf(".") + 1);
  return MimeTypes.getMimeType(ext);
 }
}

The quick and dirty part: no handling of MIME, duplicate attachment names, embedded objects etc. So - as usual YMMV

Posted by on 21 January 2016 | Comments (1) | categories: Bluemix

The 3C of leader - leader: Clarity, Competence, Control


An organisation failing their objectives has a reflexive reaction: implement more controls. Rumor has it, some sales organisations perform daily cadence calls towards the end of a period to ensure deals close (as if time away from the customer helps).
L. David Marquet, in his book "Turn the Ship Around!" paints a different story. Like a plane needs 3 points to be defined, functional organisations need 3C to operate at peak performance: Clarity, Competence & Control.
The Leader Leader principles
I wrote a short review on Amazon, but prefer the comfort of my blog to unfold a few thoughts.
In his story David is in an interesting position. In the pecking order of the Navy he is middle management, having at least 3-4 layers of management above him (until you reach POTUS), however on his ship he is in ultimate command. On high sea that is obvious. I wonder how many division heads, country leaders, section heads. etc. can related with that and see themselves in the same location and opportunity.
The killer question he asks is about processes (Page 159): " Have your processes become the master rather than the servant?". This is a brilliant question to irritate process champions - but essential to become a high performance operation.
The stance on "guiding principles" sum it up nicely: "The guiding principles needed to do just that: provide guidance on decisions". If they lack clarity, they can't do that. Some people use role models (contemporary, historical or spiritual): "What would [insert-name-here] do", but a clearly formulated set of principles (like "We the people ....") help to stay focused.
To that extend I like IBM's core values (and the mission: "Become the most relevant company on the planet"): Dedication to every client's success, Innovation that matters - for our company and for the world, Trust and personal responsibility in all relationships. Using them to decide if in doubt helps to preserve personal integrity.
Of course you could listen to Immanuel Kant and follow the categorical imperative:" Act only according to that maxim whereby you can, at the same time, will that it should become a universal law." Or you take the shorter version as promoted by Lama Marut :" Be nice".

Enjoy your read. Makes a good present to manager too,

Posted by on 09 January 2016 | Comments (0) | categories: Business Learning

Mess with the Bluemix Colors


The Bluemix designers consider their color scheme: robust, decent and unobstrusive. However not everybody likes the dark colors ( some do). Stylish to the rescue. It comes in Firefox and Chrome flavours. It requires a custom style sheet and it might take you a while to figure things out. So use this for starters:

 .dashboardArtifactCreationSection .tile,
 .cloudOESpaceLabel,
 .tile-container .tile,
 .bluemix-global-header,
 .bluemix-global-header .bluemix-nav-list,
 .NavTree2,
 .cloudOEActionBarDockedNavArea,
 .cloudOEActionBarSelector,
 .cloudOEAppDetails .cloudOEActionBarDockedNavArea,
 .inner,
 .cloudOEAppDetails .cloudOEActionBarNavigationTreeNode,
 .d-category-section .category-header,
 .dijitInputContainer,
 .cloudOEActionBarContentArea .cloudOEFilterBar .cloudOESearchBox .dijitInputField,
 .cloudOEActionBarContentArea .cloudOEFilterBar .cloudOESearchBox input,
 .cloudOEActionBarContentArea .cloudOEFilterBar .cloudOESearchBox .dijitTextBox,
 .d-docked-nav-area .d-nav-container,
 .nav-category,
 .cloudOEAppActivity,
 .appDetailsOverview_Health .tile-segmented,
 .appDetailsOverview_EstimateCost {
    background-color: #3366ff;
    color: #fff;
}
  body {
  background-color: white;
  }
  header {
    background-color: #6677CC;
  }
  
  .cloudOEStoreFront,
  .catalog-message-pane,
  .catalog-container {
    background-color:  #65C4FF;
    color: black;
  }
  
   .cloudOEDockedOpenNav {
    background-color:  #4dc4ff;
     color: black;
   }
Now go and pick nice colors.
As usual YMMV

Posted by on 08 January 2016 | Comments (0) | categories: Bluemix