Attachments and Java Beans

Up until this point, I must admit that I have been lazy. Even though most of the XPages I have created in the last two years have made extensive use of Java Beans, I have left the attachments to the XspDocument and the typical upload and download controls. I did not want to open that can of worms and just wanted to stick with what I know works. Well, that is dumb. It is fine for one or two minor applications that are never going to be used anyway, but when it comes down to it, I want it to be correct. Today, I started that adventure and, with all new things, a google search was performed for anything that could help me and point me in the correct direction. (Honestly, what did you old folks do without search engines? I’d be lost! Or at the very least spending 10 hours a day at the public library!) What I did not find was a document that contained upload and download information. Since I do not want to loose what I found today, I decided to write a quick post. Thank you to everyone that I am stealing from to write this…. ūüėõ

The first thing that I noticed was that my concept was faulty. At least I think it was. I wanted to have a single file in my Java Bean that I could upload and download at will and access and save in my DAO layer. Of course I could be mistaken, but it does not seem to work that way. Uploading documents and downloading them again needs to be performed in two different actions, and in two different ways, and with different objects. Futhermore, I do not even offer both functions on the same page, though both are possible with the same bean.

First off, my test bean is very simple. If I were to extract an interface (just to get a quick look at what the bean contains, it would hold the following information

public interface IFileTest {

/*
* This function contains the information to save the document. Normally, I do this in a seperate DAO layer Object.
* The example that I used had the majority of the information in one single class.
* I did not experiment as I wanted to keep everything as simple as possible. Such experiments are further on my to-do list.
*/
public abstract void save() throws Exception;

/*
* This function contains the logic to download the attachement. It is performed in a separate XPage containing only this function call.
*/
public abstract void downloadAttachment() throws Exception;

/*
* This function returns a string that points to the xpages with the download attachment function call.
*/
public abstract String getDownloadURL();

/*
* This function will read the parameters from the URL in order to initialize the data for the viewscoped bean.
* Normally with the Beans I create, this function will access the DAO and set the data in an object contained by this Bean.
* This Bean is a controller in the MVC design pattern.
*/
public abstract void init();

/*
* I just find this helpful.
*/
public abstract String getUnid();

/*
* com.ibm.xsp.component.UIFileuploadEx.UploadedFile. This object is used ONLY in the upload core XPage control.
*/
public abstract UploadedFile getFile();

public abstract void setFile(UploadedFile file);

}

 

As i said, this test is done with as simple a construct as possible.

After this was completed, I worked on uploading a document. It seemed the most logical starting point. My primary source for this was a StackOverflow question posted by David Leedy so, note that the following code is primarily coming from Mark Leusink, the accepted answerer of Mr. Leedy’s question. The first part is the most simple. I have a property in my Bean that is of type com.ibm.xsp.component.UIFileuploadEx.UploadedFile . I have a corresponding getter/setter pair. I use EL to link the core control for uploading data to the bean. The real magic happens in the save logic.

public void save() throws Exception{
    
    /* I use the openNTF Domino API (ODA) for nearly all of my
     * applications. If this was not the case, we would have to worry about
     * proper recycling. Keep in mind that this is also just a test. Normally
     * my routines have much cleaner error handling.
     * 
     * The following statement uses a utility that I built that helps me get
     * key objects. I am assuming here that you know how to get a handle on the current
     * document. 
     */
    Document doc = ODASessionHelper.getCurrentDatabase().createDocument();
    doc.replaceItemValue(FIELD_FORM, FORM_NAME);
    
    // file is and instance of UploadedFIle. This is the Property that is bound to the core FileUpload control
    if(file != null){ 
      IUploadedFile fl = file.getUploadedFile();
      
      //File is the standard java.io variant.
      File file = fl.getServerFile();
      
      String fileName = fl.getClientFileName();
      // this gave me ONLY the name of the file without any path information.
      System.out.println(String.format("clientFileName: '%s'", fileName)); 
      
      // on my system, this gave the character ";"
      System.out.println(String.format("seperator is '%s'", File.pathSeparator));
      
      // This gives you the location of the file that was uploaded by the control on the server.
      File realNameFile = new File(file.getAbsoluteFile() + File.pathSeparator + fileName);
      System.out.println(String.format("realFile name: '%s'", realNameFile.getAbsoluteFile()));
      
      boolean renamedFile = file.renameTo(realNameFile);
      if(renamedFile){
        //typical code to attach a file to a document.
        RichTextItem body = doc.createRichTextItem(FIELD_BODY);
        body.embedObject(EmbeddedObject.EMBED_ATTACHMENT, "", realNameFile.getAbsolutePath(), null);
      } else {
        throw new Exception("file could not be renamed");
      }
      doc.save();
      
      /*
       * Normally at this stage, I save the UNID so that I get that document again
       * to prevent a bunch of new documents being created.  This is just me being 
       * lazy and wanting to get a test out ASAP.
       */
    } else {
      throw new NullPointerException("file was null");
    }
  }

The only issue that I have with the above code is that the new name of the attachment is a bit messed up. I do not know if it is because the operating system is windows, or if it is because of the domino version, but the attachment name is changed to “_<strangenumbers>tmp;realAttachmentName.txt” . This is because File.pathSeperator is a semicolon. I have a workaround for this in my download function, but a workaround is still only a workaround.

As I previously said, I did not find a post with both upload and download functionality explained. I did find an awesome article on openNTF regarding downloading attachments programmatically. So, here is a quick shout-out to Naveen Maurya who posted the XSnippet. In the example provided, an XPage was built which called a server-side JavaScript function which got a handle on the FacesContext and the server response to download all files in a zip file. I just edited this to be run in my Bean and not in JavaScript.

/*
   * same disclaimer. I wanted to this quickly. Normally my error handling is 
   * significantly better. I just want the theory here.
   */
  public void downloadAttachment() throws Exception{
    Database db = null;
    Document doc = null;
    
    //java.io.OutputStream;
    OutputStream stream = null;
    
    // java.util.zip.ZipOutputStream;
    ZipOutputStream out = null;
    
    // java.io.BufferedInputStream;
    BufferedInputStream in = null;
    
    try{
      if(StringHelper.isNullOrEmpty(getUnid())) throw new IllegalStateException("Unid is null");
      
      // again, I am using ODA, and this is just a way to get the current database.
      db = ODASessionHelper.getCurrentDatabase();
      
      /*
       * I normally do this in multiple steps.
       * 1. try to get the document with the UNID
       * 2. try to get the document with the noteID
       */
      doc = db.getDocumentByUNID(getUnid());
      if(!doc.hasItem(FIELD_BODY)){
        throw new IllegalStateException("body not located");
      } else {
        Item item = doc.getFirstItem(FIELD_BODY);
        if(!(item instanceof RichTextItem)){
          // I would assume that I would have to come up with a MIME variant as well.
          throw new IllegalStateException("item is not of type richtext");
        } else {
          // normally I ask if item is instanceof RichTextItem
          RichTextItem body = (RichTextItem)item;
          
          Vector objs = body.getEmbeddedObjects();
          if(objs.isEmpty()){
            throw new IllegalStateException("body has no objects to download");
          } else {
            
            ExternalContext extContext = FacesContext.getCurrentInstance().getExternalContext();
            // javax.servlet.http.HttpServletResponse;
            HttpServletResponse response = (HttpServletResponse)extContext.getResponse();
            
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expires", -1);
            response.setContentType("application/zip"); // change this for different types.
            // I gave a static name to my zip file, but the original code was dynamic
            response.setHeader("Content-Disposition", "attachment; filename=Attachments.zip");
            
            stream = response.getOutputStream();
            out = new ZipOutputStream(stream);
            
            for(EmbeddedObject att : objs){
              in = new BufferedInputStream(att.getInputStream());
              int length = in.available();
              byte[] data = new byte[length];
              in.read(data, 0, length);
              String nm = att.getName();
              
              /*
               * This is my workaround for the file names. Although they are saved in the document
               * with the incorrect name, I could at least download them again with the proper name.
               */
              ZipEntry entry = new ZipEntry(nm.contains(";") ? StringHelper.rightSubstring(nm, ";") : nm);
              out.putNextEntry(entry);
              out.write(data);
              in.close();
            }
          }
          // cleanup should be done properly.  this is a 'do as I say, not as I do' moment.....
          out.flush();
          out.close();
          stream.flush();
          stream.flush();
          FacesContext.getCurrentInstance().responseComplete();
        }
      }
    } catch(Exception e){
      // very nasty error handling....
      e.printStackTrace();
      throw e;
    }
    
  }

In conclusion, I have a test XPage application with one form, and with two xpages. The one xpage allows saving attachments. It has the File Upload control available by the XPages core and a save button. The second XPage is only used for downloading the attachments. It holds no content, but gets the file to download via the HTTPServletResponse in the beforeRenderResponse XPage action. The UNID of the document is passed with the URL.

Although not implemented in an xpage, I also built the logic to open the URL in a new window using client side javascript:

window.open("#{javascript:FileTest.getDownloadURL()}", "_blank");

FileTest in the above example is the name of the bean as configured in the FacesConfig.xml file.

My next steps would be

  • to build a view with which I could display the file names and other typical information available for file downloads
  • export files without being compressed into a zip file
  • it goes without saying that I would have to refine the above functions and build in proper cleanup and error handling

Happy Programming!!




Java Beans and funny stuff

I have such a backlog of posts that I have written and have neglected to post, it is not even funny.  I have a few videos that still need to be edited or redone and it is nuts. So first off, I am sorry for the long breaks in posts, but life tends to get in the way.

Today I was working with an apprentice and getting him involved with XPages.  He seems to enjoy it so far, and it is a help for me because I give some of the more mundane things to him.  He has zero Notes/Domino experience and has focused primarily on .NET development.  If you go to derultimativeligatipp.de, you can see a bit of his handy work.  Of course he worked with another apprentice of ours to build that site, and I must say together they built one awesome application.  But I digress.  Of course I think our apprentices are amazing, but we want to quickly discuss our adventures of the day with Beans and XPages.

As readers may or may not be aware, I have spent a great deal of time developing a standard java library that I use in all of my XPage applications for use both internally and for customers.  It includes custom controls, standard beans, and now a configuration.  But a simple configuration was not good enough for me.  Let me quickly get into my use case.

Up until now, I have been using a file saved under the Resources/Files tab of the project. ¬†I wanted to get around needing profiles which can be a cached pain in the rear, I originally did not want to have a view to look up a specific document, and I did not want to touch the xsp.config xml file. Of course there are some wonderful snippets available from Paul Withers in case you would prefer that approach. ¬†I wanted to save values that differ from database instance to database instance, as well as from dev version to QA version to productive version. ¬†As far as I am aware, performing a design refresh also rewrites the config.xml file. ¬†Really the best way to get the functionality I wanted was a good old fashioned NotesDocument, a good old fashioned view, and the wonderful ODA view method “getFirstDocumentByKey()”. #ODAOnceMoreForTheWin .

This brought on interesting points that I could discuss with the apprentice: abstract classes, Beans, and expression language.   I wanted to build the following structure:

AbstractConfig
-contains the view alias for lookups which will be used for all XPage configurations
-abstract init() method
-abstract getLookupKey() method
-a few other methods that are sensible for our use case but may not be needed for all.

AbstractDashboardConfiguration => AbstractConfig (just an example)
-implements init() to fetch the needed data
-protected final Strings for each field in the NotesDocument
-private members for each field value and the appropriate getter/setters

DashboardConfigurationDocument => AbstractDashboardConfiguration (just an example)
-save() implementation
-specific XPage rendering helper functions
-is set up as a viewScope variable

DashboardConfiguration => AbstractDashboardConfiguration (just an example)
-Specific methods to use a re-init interface that I built so that all of my applicationScoped beans can be reinitialized with a click of a button by an admin
-obviously applicationScoped

As you can see, the build up is pretty complex, and this is the easiest of examples. ¬†There are probably a few “WTF?” questions that I should touch upon, so let me get to them first.

First off, I am sure the reason for an AbstractConfig class is clear.  When all configuration documents are already being saved in the same view, then why not.  Certain fields need to be set in order to guarantee that they are displayed in the correct view. Also, why should the name of the view be set in each configuration class?  It just makes sense to have a single abstractconfig java class.  But, the question that probably comes to mind is, why is there a need for another abstract class before the real implementation?

The answer is pretty simple: I hate StackOverflowExceptions. ¬†I started to create two classes to handle configuration information. ¬†One bean would be responsible for saving and editing the information (DashboardConfigurationDocument), and the other would be responsible for caching the information in the applicationScope (DashboardConfiguration). ¬†Without the abstract class I am left with the following¬†conundrum…..

It is clear that DashboardConfigurationDocument should get it’s information from the document… I mean…. ¬†it is sort of implied in the name. ¬†It should also save itself. It then needs to inform the applicationScoped DashboardConfiguration bean that it should refresh its data. This data¬†could be read from DashboardConfigurationDocument to get around needing to write the int() function twice. ¬†Right there we have a problem because¬†we have two classes that call each other. ¬†It just makes the most sense that both of these classes have the same key functions and members in the abstract version, and the rest of the key implementation in the concrete classes. ¬†It makes for a much cleaner implementation at the cost of hereditary chaos. ¬†ūüôā ¬† Truth be told I find it awesome.

The second major question that I should directly address is, why do I just not save the DashBoardConfigurationDocument bean in the application scope? Basically I am a control freak wanting to do crazy things. ¬†No…. ¬†I assure you that I have a reason. ¬†Let’s look at lazy admin A and multi-tasker admin B. ¬†Admin A makes a single change, directly in the appScoped bean before going for coffee, and admin B gets a phone call after doing the same. ¬†Neither are finished with their changes, neither of them had to save the changes explicitly, yet both of the have a change that is already potentially putting the application in an unstable state. ¬†Baaaaaaaddddd ¬†vooodooo…. ¬†baaaaaaaadddddd. ¬†For this reason, I also like to separate my editing logic from my global configuration logic. Additionally, I can have XPage UI specific logic in the viewScoped class without feeling guilty about caching stupid spam members in the appScope bean.

I can use this pattern as often as I want, and I can be sure that I do not forget anything.  All of my field names are saved as final strings and I can use them in other sub-classes if I need to.  I can even decide later that I want to override my save function in another bean to get SQL support or whatever. It is just clean, and I like clean.

After taking some time to explain a lot of this to the apprentice, we dove into Expression Language and getting some good old binding done. ¬†It worked like a charm…. almost.

This goes into another crazy use case.  I only wanted one configuration XPage.  I have an enum where I have specific configuration keys saved.  These values are then presented to the user in a combobox where a specific configuration key can be selected, and the document is opened, and the information is displayed.  We did this with Expression Language.  The ComboBox held string values representing each of the values in the enum, and the bean had the necessary getter and setter to take the string and set the enum value for that document.  This setter also triggered a process whereby the configuration was fetched and the values were refreshed.  It was a thing of beauty until we realized that the values were not being refreshed on the XPage although the values in the bean were being refreshed with the contents of the NotesDocument.  It took us two hours to figure this issue out.  The correct getters/setters were being called, the init() function was being called, the correct document was being retrieved, and the values were correct in the bean.  Why were they not refreshed on the XPage?

First off, I thought it was a simple refresh problem.  The errorMessages control displayed no errors, and I thought that it was just a simple case of needing to either perform a full refresh, a refresh directly on the component, or some other crazy thing.  We messed around without any success.  In the end, this was one instance where a simple EL expression just was not enough.  We saw that the EL expression was calling the correct methods;  the onChange partial refresh on the page was working correctly.  My suspicion is that the partial refresh was being performed faster than the methods called by the EL expression.  We took out the EL data binding and instead implemented a SSJS function calling the setter method for the configuration key.  When we did this, everything worked as planned.  We also now have one page that can be used for multiple similar configuration documents that can easily be extended without changes in the design.

Lesson learned:  Java is awesome, EL is cool, but ssjs still has to be used for certain things.

XPage dev Why Java

I recently received a comment that I would like to address:

Hey Greg. I watched the tutorial and followed it quite easily. My biggest block to Java is why should I use it? I am having difficulty understanding what the great benefits are, other than getting skilled in a new field.
I also am perplexed as to why, for example, would I need a session helper when I have session info with my standard XPage. Another thing, having an object of a Domino document and using the object to read and write fields back to my source data seems rather tedious seeing that I can read and write straight to my source data?!?
I’m missing something for sure because everyone seems to be wanting to go the Java route.
If I had 1000 users open an XPage application and view a document then why create 1000 objects, one for each document and session helpers. Does that not take needless memory?

But I am looking forward to the next tutorial!!

There are a few questions in here that I find good to ask and that can cause some debate in the professional world.  I will start off my answering my part to the question why.


Why use Java instead of JavaScript?

Some people will say, “Use Java! ¬†Why? ¬†Because ..¬†white noise………”

I am sure that all of you have heard the “Because it is faster”. ¬†Faster for whom? ¬†The computer? ¬†The difference is negligible. ¬†In a test done by a colleague, iterating over a view with 75,000 documents, touching every one completed only about two seconds faster when done in Java as opposed to JavaScript (14 seconds rather than 16 seconds). ¬†I say, if you are doing something in the real world that needs to touch that many documents and the user is waiting on you, then you are doing something wrong! ¬†I do not care if that is done in JavaScript or Java. ¬†Now, keep in mind that he was doing this for a test and to see if there was a measurable difference. ¬†There was. ¬† We thank him for doing his tests and for sharing the results with us! But to that end, lets make our first point:

Use the correct tool for the correct job!

First off let me say that I find this debate stupid.  It reminds me of the debates that I had to listen my friends engage in regarding their preferences to C# vs Java and Windows vs Linux or Mac.  Every task has its goals, and every goal has its best practice and optimized structure and technology.  If you are working on a project that will be small, used by few, and only requires a few pieces of JavaScript code, than maybe it is best to use pure JavaScript in your implementation.  Likewise, if you are going to be building an application that has code that focuses on customizing the controls, then that too might be best to be done in a pure JavaScript environment.  JavaScript simply is the tool for those jobs.  If you are going to be working on code that needs to be well documented, used by others, maintained by others, or shared between applications or one day may be shared by applications, then Java is the way to go, no doubt about it.  It just is the tool for that job!  Likewise, if you have to build code that handles jobs and working with many documents, then the code does not belong in either of them, but rather run in [scheduled] Agents.*

I know that the separation of Business Logic and UI Logic is another big push the separation of JavaScript and Java.  To that I say bullocks.  Because of the JSF infrastructure and the ability to change existing HTML generation code into actual controls, Java might indeed be the best place to store your code rather than using JavaScript to generate the code for <ComputedField>s set to HTML.  Granted customizing those controls must still be done using JavaScript, but depending on the use case, you might even have some of that logic kept in a Java Object.

Yeah, but, Why Java

Maintainability, Readability, Usability.  There is no better way to put it.

Think back to another Blog Series I did when I discussed the issues I had with debugging JavaScript.  I mentioned that the stupid error

if(apples = oranges) doSomething();

as opposed to:

if(apples == oranges) doSomething();

and that this was an error that should be caught, but never is.  This is my number one reason for pushing for java.  The stupid errors that can cost a great deal of debugging time are caught by the compiler and are easy to fix.  Another case, you have a larger function.  You are dealing with many different kinds of objects, and you accidentally try to set a document object to a documentcollection object.  In JavaScript, you will not discover this error until you run the program and you get an error that states that the desired function is not available, or you end up debugging because it manifested itself in some other way.  In java, a variable declared as a document can only be used for a document.  The error will never happen.  My first reason for pushing for Java in XPages is Usability.  You are not going to have as much code that contain errors and his hidden or uncaught by a compiler.

Another reason why I say usability is improved with java is the tooltips/inteli-sense or whatever else you want to call this feature.  If the javadoc for the API you are using is connected in the build path, you are able to look at detailed information provided by the developers, assuming that they maintained their javadoc well.  This also goes for the javacode that you write.  Of course you have the ability to write and look up comments in SSJS, but they do not show up like they do in Java.  If I am wrong about that please tell me, but I do not believe I am.  In Java, you have the ability to simply use shortcuts to import packages and classes using the inteli-sense, keyboard shortcuts, and see what members and functions are available in those classes whether they are known to designer or not.**  You write a class, that class is known by the inteli-sense. It is that simple.  It may not seem like a lot, but when you can quickly see what code does what, import, and comment all in one place, then why would you not want to?

Readability is also highly improved.  Lets say you have your classes all defined and you are using them.  In JavaScript, you do not have the ability to click on the method being called and have that method opened up in designer, at least not when those functions are defined in two different files.  With Java, this is not an issue at all.  Click on the call, file opens right to the spot in question, time is saved, finish.  The reader is just put into a position where it is efficient to go through your code and figure out what to do.  Another thing: in that same series I linked before, I mentioned that embedded JavaScript function calls are not listed in the outline view.  In Java, this is not the case.  All members, functions and embedded classes and their members are listed in the outline view thus adding to the readability of your code.

Because you are writing your code in a place that makes it readable and because it is in a very usable format that will not require a lot of learning about your code (ie reading your script files to learn how to use it and instead getting the comments via the javadoc and inteli-sense), you are saving time of future programmers who are using your code.  You are also saving yourself time when you need to change something 16 months later and do not remember what you did any longer.  You are also creating something that can be added to later!  Let me explain this.  By saying that, I am not referring to adding some code at some point down the line.  If you want to do that, than JavaScript is completely reasonable.  I am talking about finding out that there are these few classes that you use over and over and over again.  You have to port them into every database you own.  By having these classes in java already, you are in a position where it is not that expensive to port them over into OSGi Libraries that can be put on the server as globally available beans.  If you have them in JavaScript, you also need to translate them.  Furthermore, if you have an HTML generation function that you use for a computed field that displays html code, then it is not so far off to take that code one day and make a real JSF control out of it.  Likewise if you decide to make that code into a control, you can share that control via an OSGi extension.  Overkill?  Nope.  You are simply writing your application in a way that allows for future sustainability.

Why Java?  Not for the negligible speed increase, but for Maintainability, Readability, Usability.


Why a SessionHelper object?

This question refers to a video that I posted as a part of my Java XPage tutorial. ¬†I created a Java object in order to access the session. ¬†First off, I just would like to mention that this is primarily a class to assist other Java classes and not JavaScript. ¬†I most likely would not put this into the faces-config file and register it as a bean, although I have done so in the past. ¬†As the object stands right now, it is merely a list for static methods. ¬†This is a poor candidate for a bean as an instance of the class is not necessary to use its functionality. ¬†Get it when you need it, otherwise leave it. ¬†The only functionality in this particular SessionHelper class that might¬†be helpful down the line in JavaScript is the getODASession(). ¬†Otherwise, if I remember correctly, ODA creates a bean called “openSession” that allows you to access the ODA session object simply in JavaScript without any sort of self made utility¬†class. ¬†Even then, you could always access the ODA Factory utility.

There will be some who will say that my approach to getting a session in an external class is a bad idea.  I can only say to that that I have not had any issues getting the session or the database from other helper objects and methods.  An absolute no-go would be getting the objects under the database object.  Your cleanup would either be messy, or give you NotesExceptions depending on how you went about it.


 Java and Object Retention

Beans and the scoped maps are there for a reason. ¬†Lets assume we have an external database that holds language information: labels, keyword lists and so on. ¬†A typical approach from a few applications that I have taken over require that each form retrieve its own values using @DbLookup to fetch them from the source DB. ¬†In XPages, this is dumb. ¬†Likewise, this would be an asinine thing to do for a computed field or hiddenText field, it would be a horrible idea to put it into the viewScope, a dumb thing for sessionScope, and perfect for an applicationScope Bean which maintains a ¬†HashMap of document labels and keywords based on some key and correctly retrieved by reading out the sessionScope bean that contains the preferred language information for the current user as read from his user document. ¬†By putting it into the applicationScope in a bean you are creating a way to cache your expensive values and provide a way to reset those values at will. ¬†Of course you can do similar things with JavaScript, but you cannot put a JavaScript “object” or function into the scoped maps. ¬†We will be getting into such patterns later on in the tutorial.


Conclusion

As Scotty said in Star Trek V, “How many times do I have to tell you, the right tool for the right job!” ¬†There are times when Java is the right tool, there are times when JavaScript is the right tool. ¬†Do not let yourself be put into the position of thinking that only one option is correct, but keep an¬†open mind and remember that future sustainability is¬†directly influenced on your programming technique and tool decision. ¬†Don’t get caught up in the stupid debate of which is better. ¬†Do not listen to the “java is faster” crap. ¬†The real reasons go much deeper than any of that, and if you are having performance problems, then there are much more likely causes than the language choice.

Java is also the only place where you can define classes for beans to be used with scoped maps.  Otherwise, you are just saving raw variables into your map and cluttering it up.  Use Java to correctly store and access your information so that it can be readily accessed and executed.  I am a strong supporter of Java use in XPage applications.  I hope that this article has helped explain why.

 

* This is really a case by case decision and must be decided upon specifically when designing the application.
** By “known to designer”, I am referring to basically hard coding the inteli-sense. ¬†I actually do not know how this works and am merely guessing. ¬†Does not matter though.

A few Years of XPage Developement – Part 5

Fixing River ‚Äď opentNTF Domino API

Introducing ‚ÄúopenNTF Domino API‚ÄĚ

This jewel has been created by a few wonderful developers with tier 5 status, XPage God.¬† (Yes, I skipped Tier 4 for now).¬† Developers with XPage God status are those that can create content that no developer can live without.¬† That content can be pure backend, controls, or both.¬† Up until now, I have only scratched the surface of what this beast can do.¬† Its main draw for me was that recycling is a thing of the past.¬† This was something that should have been done a long time ago by IBM itself.¬† If we do not have to clean up our objects in Lotusscript, why should we have to do so in Java?¬† But that is only the first of many improvements.¬† Iterating through documents in a DocumentCollection has become much simpler.¬† No longer must you use a while loop that quickly turns into an infinite loop that eventually crashes the HTTP task of the server.¬† Additionally, error handling is improved.¬† When an error occurs, the information is saved in files on the server, and if an openLog database is located on the server, the error information is saved there too.¬† No longer must you play with the NotesException class (a horrible invention to say the least, being the only Exception that I know of where the ‚ÄúgetMessage()‚ÄĚ method always returns null and that introduced the .Text public property).¬† The uses for this API are too many to list here.

Since the openNTF Domino API, or ODA as I like to call it, is so important, I have since made it a requirement for hxu version 3.  I have also updated all of my internal databases to incorporate that API.  Once I figured out how to get started with it, using the API is the simplest thing to do.  The methods are almost the same as the IBM Domino API.  The biggest advantages, however, are what to do if you have an issue.  The people who wrote the API are active members of StackOverflow.  If something does not work, you are unsure how to get started, or you get some strange error, write to the original authors via those forums, openNTF, or directly and get help.   Ask for such service from IBM, I dare you.

Future Outlooks

This brings us to the current day and the advent of a Tier 4 XPage developer, ‚ÄúXpage Sith Lord‚ÄĚ.¬† To Understand Lord status, let‚Äôs take a look at the current problem produced by all of my internal databases.¬† So far, I have about 6 internal active XPage applications.¬† This is not including development and QA copies.¬† Each of these applications relies on the hxu jar.¬† Furthermore, key properties are stored in a file under Resources/Files in the database itself.¬† Certain controls and libraries are being copied into each database instance.¬† When hxu changes, all applications implementing hxu must be changed individually.¬† This is starting to become a bit of a hassle.¬† Those with Lord Status no longer write stuff for a single database, but rather create OSGI plug-ins that can be easily installed on the server level affecting all applications which have those marked as required.¬† Controls and libraries need no longer be copied and pasted into new projects, properties can be saved in the xsp.properties file or in the domino notes.ini, and everything can be centrally controlled.¬† I have a long way to go before I can say that I have reached tier 4, but I can say that I recognize the next steps in my progress as a Notes XPage developer.