Drobo Outage

Well bugger me, it would appear that Drobo is on the ball when it comes to support, I received the following message from them within 48 hours of posting this blog (I did not contact them):

“Hi Mark
Thank you for your feedback ( I happened to come across your blog). I have created a case for you for the purpose of forwarding your feedback to our product development team for review. No need to respond, just wanted to thank you and let you know what we were doing.

Kind Regards,
Drobo – Tier 3 Support Engineer”

10 out of 10 again Lads!

End Update

I had a strange Hiccup from one of my Drobo FS’s during the weekend, a brief ‘weerrr-clunk’ from one of the drives and connection was lost, after some unsuccessful attempts to reconnect a re-boot was performed after this I was treated to the happy message

Mount Failed – Internal Error

Lovely!!! thankfully Drobo have posted the solution on their Web Site

You have to press Ctrl – Shift – M then type in “Proceed”, it will then trundle off and run a full check disk and afterwards request a reboot, which worked perfectly, OK boys 10 of 10 for good functionality but -100 for UI, if this is the correct (and only) response to an ‘internal Error’ message, why is there not a bloody great big button marked “Don’t panic! run check disk now” under the error!!

Oh and while I’m at it, the new dash board is not a step forward!, its runs at half the speed, occupies twice the screen space (so big in fact it is unusable on a netbook) and as far as I can tell does not add any new functionality (if fact it takes it away as it does not show the space available pie charts in the task bar and does not support droboshares), so I will be sticking to the old one, so dear Drobo, you are nice an big and successful now, but your original engineers still knock the spots off the new ui people you have hired,

finally a bit of info, as a long time owner of Drobos (V1 Classic, V2 Classic, Drobo share, Drobo FS) I can confirm that they have got faster with each generation (as advertised), this seems to me to be keeping in line with growing drive sizes, so a near fully loaded* consumer/pro level Drobo will always take about 24 hours to get over an outage or drive change, patience people patience, it will get there (i have never lost any data off a Drobo)

  • by fully loaded I mean all bays filled with the largest supported drives and about 80%-85% full.

ITunes and Adobe air

Using adobe Air on multiple different clients brings its own challenges particularly if your interacting with the client machines underlying File system and other programs that make their home there, case in point, on an AIR application LDC wrote, once an file is download it is checked to see if it is “all present and Correct” via details provided from the source server,

It would appear after some investigation that the ITunes monitoring of its “Automatically add to iTunes” folder is fast, really fast, it consistently beats an action script complete event to getting a new file handle, as with 14 days of lessons: lesson 2: Adobe Air and AV its a sod to catch nicely, thankfully the folder name is consistent so we are left with

if (currentFilePath.toLowerCase().indexOf("automatically add to itunes") > -1 ) {
    //bloody iTunes will have grabbed the sodding thing by now, cope with it!!

I feel slightly dirty and its not exactly MENSA code, but it works and the client’s users are happy 🙂

As normal hope it spares someone some pain

PDF generation AGAIN

That perpetual nut of PDF generation rose again recently with a new client, this time with a twist, they did not want to worry about any licences and had taken particular umbrage at the new iText licence,

This lead to a search for a good library that i could distribute to client apps, simply, without nasty yearly surprises, also I wanted the generation its self to be quick and make sense for any future developer who has to maintain.

Had i been wanting to convert existing good looking documents to PDF and the client being amenable to a licence, I would have chosen http://pd4ml.com/ which even has a plug in agent for notes (a rarity), but the royalty free requirement lead me to http://pdfjet.com/

This turns out to be very simple to use and nice and powerful, but best of all when you buy a licence (~$290) its a developer licence rather than a client licence meaning I can reuse it multiple times vastly increasing its worth to me (it should be noted that there is a free version , and once you are sure you want to use the paid version, its just a case of swapping out a jar file.

There are lots of nice examples for the using this lib at http://pdfjet.com/java/by-example.html but here is a Domino one as it has its own quirks, this is just simple agent with the PDFjet.jar imported as an archive, an image (logo.jpg) imported as a resource and the security settings to be able to write to the file system.

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Vector;
import com.pdfjet.*;
import lotus.domino.*;
public class JavaAgent extends AgentBase {
    public String createdFileName;
    public String createdFileNameAndLocation;
    public Session session;
    public void NotesMain() {
        try {
            session = getSession();
            AgentContext agentContext = session.getAgentContext();
            Document sourcedoc = agentContext.getDocumentContext();         
            //generate the file name for the pdf [
            createdFileName = "demo.pdf";
            // ] generate the file name for the pdf
            // get a safe place to save the temp file from the notes ini file 
            // this includes handling escape charters [ 
            String writeDirectory = session.getEnvironmentString("Directory", true);
            createdFileNameAndLocation = writeDirectory.replaceAll("\\", "\\\\") + "\" + createdFileName;
            FileOutputStream fos = new FileOutputStream(createdFileNameAndLocation);
            // ] get a safe place to save the temp file from the notes ini file
            // create the pdf and generate its content based on its template [
            PDF pdf = new PDF(fos);
            pdf = TemplateDemo(pdf, sourcedoc);
            // ] create the pdf and generate its content based on its template
            //Save the pdf back to the document [ 
            RichTextItem bodyAttachments = null;
            if (sourcedoc.hasItem("Attachments")) {
                bodyAttachments =(RichTextItem)sourcedoc.getFirstItem("Attachments"); 
            } else {
                bodyAttachments = sourcedoc.createRichTextItem("Attachments");                
            bodyAttachments.embedObject(EmbeddedObject.EMBED_ATTACHMENT, null, createdFileNameAndLocation, createdFileName);
            if (sourcedoc.save()) {
                System.out.println("attachment saved back to document");
            // ] Save the pdf back to the document
            //delete the temp file [
            File f = new File(createdFileNameAndLocation);
            //] delete the temp file
        } catch (Exception e) {
    //i prefer to move the layout into different functions to keep the code as clean as possible
    public PDF TemplateDemo(PDF pdf, Document sourcedoc) throws Exception {
        String Title = sourcedoc.getItemValueString("Title");
        pdf.setAuthor("London Developer Coop");
        Font f1 = new Font(pdf, "Helvetica");
        Font f2 = new Font(pdf, "Helvetica-Bold");
        Font f3 = new Font(pdf, "Helvetica-Bold");
        //this picks up an image from a file stored in side the Domino Java agent [
        InputStream logoStream = getClass().getClassLoader().getResourceAsStream("Logo.jpg");
        if (logoStream == null) {
            System.out.println("stream empty");
        Image logoImage = new com.pdfjet.Image(pdf, logoStream, 1); // the '1' parmeter means its expecting a jpg  
        // ] see below the code for other methods
        Page page = new Page(pdf, Letter.PORTRAIT);
        //3 item header 'text,text,image' Header [
        logoImage.setPosition(502, 40);
        Paragraph t1p1 = new Paragraph();
        t1p1.add(new TextLine(f2, "I am a test document"));
        TextColumn columnt1 = new TextColumn(f1);
        columnt1.setPosition(0, 50);
        columnt1.setSize(150, 100);       
        Paragraph t2p1 = new Paragraph();
        t2p1.add(new TextLine(f2, Title));
        TextColumn columnt2 = new TextColumn(f1);
        columnt2.setPosition(151, 50);
        columnt2.setSize(300, 100);       
        // ] Header
        //chunk of text[
        //I personally prefer getting data values for display via evaluate as they are easy to handle and return nice strings
        Vector v = null;
        v = session.evaluate("CollaborationDate", sourcedoc);
        Paragraph parm1b = new Paragraph();
        parm1b.add(new TextLine(f1, v.firstElement().toString() ));
        TextColumn columnm1b = new TextColumn(f1);
        columnm1b.setPosition(130, 100);
        columnm1b.setSize(450, 100);       
        // ] chuck of text
        return pdf;

In the code above I’m getting the image from an image file included as a resource in the agent itself, for those brave enough to need to get it from the notes design and not allowed to pick it up as a URL, I point you to this article from LEkkimWorld

Anyway. yell if ive missed anything.

Old Comments
##### Mark Myers(14/06/2011 00:24:02 GDT)
@Mike good link!! i read the full article that @sean linked to which describes the reason for the licence change (all perfectly normal reasons), thankfully as the pdfjet licence is a developer one, and i have made the investment I can keep using it and keep up to date

has anyone else had issues with different generators/versions being barred (one of my main client officially does not allow ANY open source on their site (dispute having Apache all over the place))
##### Rotary Club Jersey(13/06/2011 22:28:23 GDT)
You could always use an older version of iText, back in the days before they changed the license?
##### Sean Cull(13/06/2011 19:39:37 GDT)
they liked your article so much they raised the price to USD 297 !
{ Link }
##### sean cull(13/06/2011 20:00:39 GDT)
more on the itext changes including comments from the creator of itext. :
{ Link }
##### Stephan H. Wissel(13/06/2011 20:27:05 GDT)
Did you try Apache PDFBox?
##### Mike Brown(14/06/2011 00:03:06 GDT)
You can get older versions of iText from the OpenLogic Exchange site. iText 2.1.7 appears to be the last one before the licence was changed:

{ Link }

That one is covered under the LGPL v2 and the MPL 1.1, which means you’re pretty well free to use them without paying fees or disclosing the source code for your app… I think! (I’m no expert.)

Incidentally, this is the version that you *have* to use if you’re still on Domino 7 anyway.

##### Mark Myers(13/06/2011 23:42:16 GDT)
@sean , thanks for the correction Emoticon , and the link to the info on the licence change

@Stephen, I had a look at PDFBox (Apache is always a good place to start for anything) but decided against it on the grounds of PDFjet’s simplicity, the fact that it did EVERYTHING I wanted (with 14 good examples) and the nice documentation package that I could attach to the db for future devs, also PDFBox had a long period of near stagnation (which it is now making up for) while PDFJet as a commercial application is updated regularly (such as support for QR code barcodes, PNG images with alpha transparency etc etc which i already have a use for for another client)

@Rotary Club Jersey, yeah, the clients no itext was very flat so i did not think that was a good idea.

Thank you

On Friday I got an amazon parcel, it turned out to be “the Ultimate Rice Cooker Cookbook” and was sent by Andrew Magerman , its appearance has quite made my week!!, my first gift from a friend i have made from the lotus community and related to a blog post Tooo!, awesome!
after some though this turns out to be not quite true:

  • There was the Lotusphere tickets from Elguji software (Bruce and Gale , Matt ).
  • Thank you presents from speaking at various LUGs (Warren and Kitty , Paul Mooney).
  • Being involved in great new projects (Mark Barton , Keith Brooks).
  • The Hangovers (Bill Buchan , Steve McDonagh, and various lotus people i can only just remember).
  • Constant assistance mixed with abuse from LDC (Ben Poole,Julian Woodward etc al).
  • and never ending help for my gross stupidity from various member of the community at large.I spend time with various members of different tech community’s (Java,flex,NoSQL), but the Lotus one is the friendliest and the best and i don’t care who knows it.now that I have purged the niceness from my soul i can go back to being an arse and picking on Ben Poole

Old Comments

Keith Brooks(12/06/2011 04:30:07 GDT)

That’s what friends are for.

Andrew Magerman(15/06/2011 12:02:02 GDT)

You’re welcome!

Select an agent and run it on a server

I did not write this (I hardly write any lotusScript any more), it was written by Antony Cooper, but he has kindly given permission to post it here as it is soo useful, a very nice little “Run from Menu” agent/function to enable you to run other agents directly on the server, making them much much faster, we use it for running migration and administration updates that need to change a lot of existing data, hope its as useful to you as it is to me.

    Agent Select and Run agent on server
    Created by Antony Cooper
    Description: Simple agent to build a list of agent in a database and then run the selected agent on the server
Option Public
Option Declare
Sub Initialize()
    Dim ws As New NotesUIWorkspace
    Dim session As New NotesSession
    Dim db As NotesDatabase
    Dim nid As String, nextid As String, agName As String, msg As string
    Dim i As Integer, flag As Integer
    Dim doc As NotesDocument
    Dim arrName As Variant
    Set db = session.CurrentDatabase
    REM Create note collection
    Dim nc As NotesNoteCollection
    Set nc = db.CreateNoteCollection(False)
    Call nc.SelectAllCodeElements(false)
    nc.SelectAgents = true
    Call nc.BuildCollection
    arrName = ""
    'Build array of agent names
    nid = nc.GetFirstNoteId
    For i = 1 To nc.Count
        'get the next note ID before removing any notes
        nextid = nc.GetNextNoteId(nid)
        Set doc = db.GetDocumentByID(nid)
        If IsArray(arrName) Then
            arrName = ArrayAppend(arrName, doc.Getfirstitem("$title").Values )
            arrName = doc.Getfirstitem("$title").Values
        End If
        nid = nextid
    arrName = QuickSort(arrName, LBound(arrName), UBound(arrName))
    agName = ws.Prompt(PROMPT_OKCANCELCOMBO, "List of Database Agents", "Select the agent to run on this databases server", "", arrName)
    If agName = "" Then 
        MsgBox "No agent has been selected, action cancelled.", 64, "User information"
        Exit sub
    End If
    If agName <> "" Then
        msg = "The agent you selected is '" & agName & ''
Do you want to run this agent on the server now?'
        flag = MsgBox (msg, 32 + 4, "User information")
        If flag <> 6 Then Exit Sub
    End If
    Dim agent As NotesAgent
    Set db = session.CurrentDatabase
    Set agent = db.GetAgent( agName )
    If agent.RunOnServer = 0 Then
        MessageBox "Agent ran", 32, "Success"
        MessageBox "Agent did not run", 64, "Failure"
    End If
End Sub
' Function Quick sort.
' Sorts array
Public Function QuickSort( anArray As Variant, indexLo As Long, indexHi As Long) As Variant
Dim lo As Long
Dim hi As Long
Dim midValue As String
Dim tmpValue As String
lo = indexLo
hi = indexHi
If ( indexHi > indexLo) Then
'get the middle element
    midValue = anArray( (indexLo + indexHi) /2)
    While ( lo <= hi )
    'find first element greater than middle
        While (lo < indexHi) And (anArray(lo) < midValue )
            lo = lo+1
        'find first element smaller than middle
        While ( hi > indexLo ) And ( anArray(hi) > midValue )
            hi = hi - 1
        'if the indexes have not crossed, swap
        If ( lo <= hi ) Then
            tmpValue = anArray(lo)
            anArray(lo) = anArray(hi)
            anArray(hi) = tmpValue
            lo = lo+1
            hi = hi -1
        End If
    ' If the right index has not reached the left side of array, sort it again
    If( indexLo < hi ) Then
        Call QuickSort( anArray, indexLo, hi )
    End If
    'If the left index has not reached the right side of array, sort it again
    If( lo < indexHi ) Then
        Call QuickSort( anArray, lo, indexHi )
    End If
End If
QuickSort = anArray
End Function

Old Comments
##### Hora_ce(10/06/2011 10:22:24 GDT)
I’ve created something similar to this one… but in addition, the user can choose where to run the agent – on local or on server.
##### Mark Myers(09/06/2011 21:04:27 GDT)
you are correct, the time out is not applied for this method (one of its very reasons for creation as we have a very strict admin, who wont let agents run more that 10 mins which is often too short for a migration agent)
##### Sean Cull(09/06/2011 21:01:48 GDT)
just watch out, I am not sure that the server time outs protecting against infinite loops etc are enacted via this method – I once had one go for “some time” with no apparent way to stop it.
##### Mark Myers(10/06/2011 22:10:29 GDT)
cool, I take it the run local option is for scheduled agents rather than selecting them manually from the action menu? where is it posted?

More Amazon Web Services lessons

At LDC towers we use AWS a great deal and have a habit of trying every variation of their services as we can, on the basis of “better we learn lessons on our servers, rather than clients”, one of our dev servers was built on windows 2008 64bit, and unfortunately it turns out that you can only have small and medium instances on 32bit instances not on 64bit (this applies to the Linux instances as well),



This means that unless you are willing to pay about ~$200 per month, stick to 32bit (unless you can live in under 613meg of ram)

Thankfully it was very easy to swap back thanks to the lessons we have already learnt:

  • Use Elastic IP (to have a static IP address that you can simply assign to any instance is worth its weight in gold)
  • Keep all data possible on Persistent volumesSo we could simply stop the instance, create a new one (32bit), reassign our Elastic IP and Persistent Volumes to the new instance, and just install the core programs dependancies (all our data,config and installers are kept on the volumes),

    Total time to swap ~15 mins (10 of this was the windows build)….Sweet

    oh a quick side note, you might suffer some rights issue on reassigned volumes (regarding both the ownership and rights of files), if your a Linux boy this will be no issue as you will long be used to the Chown and Chmod commands, but if your doing this on windows and a complete none admin or GUI person, you might find it a bit frustrating (the Windows GUI tools for rights always seem to throw up spurious errors and run soooo slow), however Windows 2008 finally provides fast command line tools in the shape of

    takeown /f d:directory*.* /d y

    icacls d:directory*.* /grant administrators:F

    hope that helps.