Ruby for XPages Programmers, Part 1

Wed May 30 22:54:00 EDT 2012

Tags: ruby xpages
  1. Ruby for XPages Programmers, Part 1

Since I'm always gabbing on about Ruby, I figured it'd be useful to give an overview of the language and, in future posts, why it's worth using for XPages scripting. I don't plan to write an exhaustive tutorial for the language (they're aplenty on the web), but I'll go over the basic concepts and some of how it compares to EL, Server JavaScript, and Java.

Ruby is a dynamic, object-oriented language with functional-programming features and syntax designed for programmer-friendliness. In terms of other languages, it's sort of like a combination of Perl's general syntax, Smalltalk's object model, and (some of) Lisp's functional and metaprogramming abilities. Additionally, Ruby has its own unique features and conventions, such as optional parentheses, property-style getters and setters, and yield statements.

Maybe the best way to provide a gentle introduction to Ruby is to show a relatively simple but contrived code example. Here is a "Point" class meant to represent a point in two-dimensional space where the X coordinate must always be positive:

class Point < Object                                           # 1
    attr_accessor :y                                           # 2
    attr_reader :x
	
    def initialize(x=0, y=0)                                   # 3
        @y = y                                                 # 4
        self.x = x                                             # 5
    end
	
    def x=(x)                                                  # 6
        raise Exception.new("x must be >= 0") if x < 0         # 7
        @x = x
    end
	
    def to_s; "[#{@x}, #{@y}]"; end                            # 8
end

There are a lot of Ruby-isms going on, but hopefully the bulk of the code will be readable without knowing the language. Starting from the top, with a focus on the differences from Java:

  1. Classes are created with just class, with no public modifier, and must begin with a capital letter, which is the language-enforced convention for class names and constants. By unenforced convention, classes and constants are CamelCase. The "<" is equivalent to Java's extends. As in Java, extending Object is not needed, but is done here for demonstration purposes. Finally, # is Ruby's single-line comment delimiter, like // in Java or ' in LotusScript.
  2. attr_accessor and attr_reader are built-in Ruby class methods that generate getters+setters and getters only, respectively. The ":"s in :x and :y indicate that those are Symbols. Symbols are interesting beasts, but in this case think of them as meaning "the things named 'x' and 'y'".
  3. The initialize method is equivalent to Java's constructors and LotusScript's New. This one takes two parameters, but provides default values in case one or both are left out. While Ruby objects are strongly typed in that they all have a class, Ruby variables are typeless and not statically checked.
  4. The "@" symbol is used to denote instance variables, which, like all Ruby variables, don't need to be declared before they're used the first time. In this case, the line is equivalent to "this.y = y;" in Java.
  5. "self.x = x" is used here to indicate that the first "x" is calling the current object's x= method as opposed to setting the local variable "x" to itself. This line is equivalent to "this.setX(x);" in Java.
  6. Ruby, like C++, allows operator overloading, including this special type to define setters. This allows setting of the object's "x" parameter with code like "some_point.x = 3".
  7. This line is jam-packed!
    • Ruby's exceptions are essentially like Java's, except they use raise and begin/rescue instead of throw and try/catch.
    • Objects in Ruby are constructed by calling the new method on the class itself, rather than having new be a language keyword.
    • Ruby allows for conditional and looping statements at the end of single lines as syntactic sugar.
    • Ruby allows parentheses to be dropped when the resultant code is unambiguous.
  8. This line is pretty packed as well:
    • to_s is Ruby's equivalent to Java's toString().
    • Ruby, like JavaScript, uses optional semicolons. Here, they're used to pack the method definition into one line.
    • When a method takes no parameters, the () after the name is optional, both in definition and in use.
    • Ruby strings allow for code interpolation via #{...}. It's like value bindings in XPages or the equivalent feature in PHP or Perl - it allows Ruby code to be embedded in a string instead of +-based concatenation.
    • Like Server JavaScript and Formula Language, the last line in a Ruby method (except in initialize and assignment methods) is an implied return.
    • By convention, Ruby uses all-lowercase underscore_separated_words for variable and method names.

That ended up being a bit more complicated than I had expected, but hopefully it's a reasonable start (if not, there are definitely better Ruby tutorials on the web). In the next post in this thread, I'll describe some other unique or unusual Ruby features and concepts, and later I'll go into some more applicable examples to explain why Ruby is worth using in XPages generally.

XPages MVC: Experiment II, Part 3

Mon May 28 20:00:00 EDT 2012

Tags: xpages mvc
  1. XPages MVC: Experiment I
  2. XPages MVC: Experiment II, Part 1
  3. XPages MVC: Experiment II, Part 2
  4. XPages MVC: Experiment II, Part 3
  5. XPages MVC: Experiment II, Part 4

Continuing on from my last post, I'd like to go over a couple specifics about how I handle fetching appropriate collections of objects from a view and a couple areas where I saved myself some programming hassle.

As I mentioned before, both the "manager" and "collection" classes inherit from abstract classes that handle a lot of the dirty work. The AbstractCollectionManager class is by far the smaller of the two, containing mostly convenience methods and a couple overloaded methods for generating the collection objects. The central one is pretty straightforward:

protected AbstractDominoList<E> createCollection( String sortBy, Object restrictTo, String searchQuery, int preCache, boolean singleEntry) { AbstractDominoList<E> collection = this.createCollection(); collection.setSortBy(sortBy); collection.setRestrictTo(restrictTo); collection.setSearchQuery(searchQuery); collection.setSingleEntry(singleEntry); collection.preCache(preCache); return collection; }

The first three properties actually determine the nature of the collection, while the last two set some optimization bits when the code knows ahead of time about how many elements it expects (such as doing a by-id lookup or a "recent news" list that will only ever display 5 entries). The createCollection call at the start is overridden method in each class that returns a basic collection object to avoid Java visibility issues.

The AbstractDominoList class is much longer; I won't go into too much detail, but a couple parts are pertinent. As I've mentioned, it implements the List interface (via extending AbstractList), which means it just needs to provide get and size methods. The way these work is by checking to see if it houses a valid cached ViewEntryCollection and, if it does, translating the method call to get the indexed entry as an object or the size. If the collection hasn't been fetched yet or if it's no longer valid (say, if it was recycled), it uses the view-location information from the implementing class and any parameters on the object to construct another one.

First, it fetches and configures the view properly:

Database database = this.getDatabase(); View view = database.getView(this.getViewName()); view.refresh(); String sortColumn = this.sortBy == null ? null : this.sortBy.toLowerCase().endsWith("-desc") ? this.sortBy.substring(0, this.sortBy.length()-5) : this.sortBy; boolean sortAscending = this.sortBy == null || !this.sortBy.toLowerCase().endsWith("-desc"); if(this.searchQuery != null) { view.FTSearchSorted(this.searchQuery, 0, sortColumn, sortAscending, false, false, false); } else if(this.sortBy != null && !this.sortBy.equals(this.getDefaultSort())) { view.resortView(sortColumn, sortAscending); } else { view.resortView(); }

Then, it uses code similar to this to store the collection in its cache (I say "similar to" because there are additional branches and methods, but this is the idea):

if(this.restrictTo == null) { ViewEntryCollection collection = view.getAllEntries(); this.cachedEntryCollection = new DominoEntryCollectionWrapper(collection, view); } else { ViewEntryCollection collection = view.getAllEntriesByKey(this.restrictTo, true); this.cachedEntryCollection = new DominoEntryCollectionWrapper(collection, view); }

The other part of the get method is translating the ViewEntry into an object, which is done via a method similar to this monster:

protected E createObjectFromViewEntry(ViewEntry entry) throws Exception { Class currentClass = this.getClass(); Class modelClass = Class.forName(JSFUtil.strLeftBack(currentClass.getName(), ".") + "." + JSFUtil.strLeftBack(currentClass.getSimpleName(), "List")); // Create an instance of our model object E object = (E)modelClass.newInstance(); object.setUniversalId(entry.getUniversalID()); object.setDocExists(true); // Loop through the declared columns and extract the values from the Entry Method[] methods = modelClass.getMethods(); String[] columns = this.getColumnFields(); Vector values = entry.getColumnValues(); for(Method method : methods) { for(int i = 0; i < columns.length; i++) { if(values != null && i < values.size() && columns[i].length() > 0 && method.getName().equals("set" + columns[i].substring(0, 1).toUpperCase() + columns[i].substring(1))) { String fieldType = method.getGenericParameterTypes()[0].toString(); if(fieldType.equals("int")) { if(values.get(i) instanceof Double) { method.invoke(object, ((Double)values.get(i)).intValue()); } } else if(fieldType.equals("class java.lang.String")) { method.invoke(object, values.get(i).toString()); } else if(fieldType.equals("java.util.List<java.lang.String>")) { method.invoke(object, JSFUtil.toStringList(values.get(i))); } else if(fieldType.equals("class java.util.Date")) { if(values.get(i) instanceof DateTime) { method.invoke(object, ((DateTime)values.get(i)).toJavaDate()); } } else if(fieldType.equals("boolean")) { if(values.get(i) instanceof Double) { method.invoke(object, ((Double)values.get(i)).intValue() == 1); } else { method.invoke(object, values.get(i).toString().equals("Yes")); } } else if(fieldType.equals("double")) { if(values.get(i) instanceof Double) { method.invoke(object, (Double)values.get(i)); } } else if(fieldType.equals("java.util.List<java.lang.Integer>")) { method.invoke(object, JSFUtil.toIntegerList(values.get(i))); } else { System.out.println("!! unknown type " + fieldType); } } } } return object; }

Yes, I am aware that it's O(m*n). Yes, I am aware I could move values != null outside the loop. Yes, I am aware I do the same string manipulation on each column name many times. Shut up.

Anyway, that code finds the model class and loops through its methods, looking for set* methods for each column (the column list being specified in the implementation class). When it finds one that matches, it checks the data type of the property, and invokes the method with an appropriately-cast or -converted column value. The result is that the implementation class only needs to provide the code needed for fetching any data that isn't in the view, such as rich text. At one point, I think I had it so that it read the view column titles and used those as field names, but I stopped doing that for some reason... maybe it was too slow, even for this method.

In any event, next time, I'll go over some of the disadvantages that I've found with this method that don't involve big-O notation.

XPages MVC: Experiment II, Part 2

Thu May 24 20:43:00 EDT 2012

Tags: xpages mvc
  1. XPages MVC: Experiment I
  2. XPages MVC: Experiment II, Part 1
  3. XPages MVC: Experiment II, Part 2
  4. XPages MVC: Experiment II, Part 3
  5. XPages MVC: Experiment II, Part 4

Continuing on from my last post, I'd like to go over the collection and model structures I used in my guild-forums app.

I set up the data in the Notes DB in a very SQL-ish way (in part because I considered not using Domino initially). Each Form has a single equivalent view (for the most part), which is uncategorized and sorted by default by an ID column. There is one column for each column that I want to sort by and, in the case of simple documents, one for each applicable field, containing only the field name; additionally, I created $-named "utility" columns for when I wanted to sort by a given bit of computed data or have a second secondary sort column for a given column. For the most part, the data is normalized, but I had to give in to reality in a few situations, for things like sorting Topics by the their latest Post dates.

For collection classes in Java, each collection class file contains one public class for the "manager" and one list implementation class, both of which extend from Abstract versions, so the actual implementations may have very little code. For example, here is the "Groups" Java file (minus some Serialization details):

public class Groups extends AbstractCollectionManager<Group> { protected AbstractDominoList<Group> createCollection() { return new GroupList(); } } class GroupList extends AbstractDominoList<Group> { public static final String BY_ID = "$GroupID"; public static final String BY_NAME = "$ListName"; public static final String BY_CATEGORY = "ListCategory"; public static final String DEFAULT_SORT = GroupList.BY_ID; protected String[] getColumnFields() { return new String[] { "id", "name", "category", "description" }; } protected String getViewName() { return "Player Groups"; } protected String getDatabasePath() { return "wownames.nsf"; } protected Group createObjectFromViewEntry(DominoEntryWrapper entry) throws Exception { Group group = super.createObjectFromViewEntry(entry); Document groupDoc = entry.getDocument(); group.setMemberNames((List>String<)groupDoc.getItemValue("Members")); return group; } }

The actual heavy lifting is done by the parent class, letting the specific classes be composed almost entirely of descriptions of where to find the view, what columns map to model fields, and any extra code that is involved with that object mapping. Other classes are larger, but follow the same pattern. For example, here's a snippet from the manager for Posts:

public class Posts extends AbstractCollectionManager<Post> { // [-snip-] public List<Post> getPostsForTag(String tag) throws Exception { return this.search("[Tags]=" + tag); } public List<Post> getPostsForUser(String user) throws Exception { return this.getCollection(PostList.BY_CREATED_BY, user); } public List<Post> getScreenshotPosts() throws Exception { return this.search("[ScreenshotURL] is present", PostList.BY_DATETIME_DESC); } }

The parent abstract class provides utility methods like getCollection and search that take appropriate arguments for sorting, searching (I love you, FTSearchSorted), and keys.

As for the individual model objects, they start out as your typical bean: a bunch of fields with simple data types and straightforward getters and setters. Additionally, they contain "relational" methods to fetch associated other objects, such as the Forum that contains the Topic, or a list of the Posts within it. Finally, the model class is responsible for actually saving back to the Domino database, as well as taking care of any business rules, like making sure that the Topic document is updated with a new Post's date/time.

The result is that a new object type is easy (-ish) to set up, with all the code being written contributing specifically towards the task at hand. And, since the nuts and bolts of XPages are Lists and Maps, working with these objects is smooth, taking advantage of Expression Language's cleanliness and Server JavaScript's bean-notation support in the UI and Java's clunky-but-still-useful collections features.

Next time, I'll go over some of the specifics of how I deal with fetching collections and individual records, use reflection to save me some coding hassle, and other tidbits from the giant messes of code that make up my abstract classes. After that, I think I'll make a fourth post to detail some down sides to my approach as implemented.

XPages MVC: Experiment II, Part 1

Wed May 23 15:38:00 EDT 2012

Tags: xpages mvc
  1. XPages MVC: Experiment I
  2. XPages MVC: Experiment II, Part 1
  3. XPages MVC: Experiment II, Part 2
  4. XPages MVC: Experiment II, Part 3
  5. XPages MVC: Experiment II, Part 4

As I've mentioned a couple times, my largest XPages app to date is the site I did for my guild's forums. The forum part itself isn't particularly amazing, considering it's almost harder to NOT write a forum in Domino than it is to write one, but it gave me a chance to try my hand at abstracting data access away from the XPage.

I put "Part 1" in the title because I figure it will be best to break the topic up into at least three parts: the overall idea and the result, the structure of the model and collection classes, and some nitty-gritty bits about how I get the data out of the back-end classes.

My first draft of this site was straightforward, from an XPages perspective: I mapped XPages to the corresponding Forms and used a bit of Server JavaScript here and there to pull in bits of support information. This worked pretty well at first, but quickly ran into code and speed scalability issues. Just looking at a "Topic" page, the following data needs to be looked up from outside the Topic document itself:

  • The name and ID of the parent Forum document
  • The name and ID of the parent Forum's parent Category document
  • Whether or not the topic is marked as a "favorite" for the current user, which is stored in a prefs doc for the user
  • The collection of Post documents for the Topic
  • For each Post document:
    • Summary and rich text data from the document
    • The display name, title, and signature of the posting user, which are stored in their Person document
    • The number of posts from the posting user that are visible to the current user, updated live (heh)
    • Whether or not the current user has rights to edit the Post document
  • Any surrounding page elements, such as the count of unread topics from the front page, the "who's online" list, etc.

As you can imagine, writing each one of those as inline SSJS turned into a hairy, slow mess. And that's just for the Topic page! That's leaving out the other pages, such as the forum index, which has to show a tree hierarchy of Categories and Forums with up-to-date Topic and Post counts.

I tried wrapping these things up into SSJS functions in a script library, which helped a LITTLE, but it still sucked. The clear solution was to wrap these all in proper model objects and, after a number of revisions, I came up with a pretty good solution.

Conceptually, the back-end structure consists of:

  • Collection Managers. These are analogous to Views, in that there's one per document type and they communicate with a similarly-named uncategorized View with many sortable columns. I write methods in these managers to allow for returning an appropriate collection for the circumstance (e.g. getPostsForForumId(...) and getPostsForTag(...)), as well as generic methods for getting arbitrary collections for a given key, sort column, and FT search query.
  • Collections. These are analogous (and often wrap) ViewEntryCollections. They implement the List interface to allow for use in xp:repeats and handle efficiently getting the needed model objects and re-fetching the View and Collection if they're recycled away.
  • Model Objects. These are what you'd think: Post, Topic, etc.. These provide getters/setters for the fields of the document, getters for associated collections (e.g. topic.getPosts()), and handle saving the document back to the DB.

The net result is that pages went from having giant blocks of lookup code (or SSJS function calls that pointed to giant block functions) to nice little EL expressions like #{category.forums} and #{forum.latestPost.createdBy}. As importantly, this allowed for a lot of caching to make these relational operations speedy when dealing with live data in a Domino DB.

Next time, I'll go into some specifics about how I structured the collection and model classes.

Optimizing for the Wrong Thing

Wed May 23 08:37:00 EDT 2012

Tags: off-topic

My apartment has a core logistical issue: it has two floors and the connection is a spiral staircase. This is generally fine for normal use, but it's a giant hassle when moving in or out: the building has to hire some guys to essentially dismantle the stairs to turn each step to one side so they can hoist large objects like bookshelves and beds up and down.

In order to make this mildly more practical, the building decided to replace one of the upstairs windows in each apartment with a model that makes it easier to move things in an out through it. Nothing to complain about there - a window's a window, and now was a pretty good time to make the switch, since our section of the building has been lightly populated but is picking up now.

The actual installation was problematic in the way you'd expect that kind of thing to be: longer than the estimated hour, several trips, and the powerful aroma of new drywall and paint for several days. I had to call the front desk a couple times to get them to "remind" the maintenance guy that he was supposed to stop by several times but kept forgetting. Still, that's within the expected range.

The problematic part of this window is that, while it is indeed better at the specific task of moving furniture in and out, it's at the significant expense of daily window use. I had assumed that the new window would be basically the same as the old one, but made to be easy to remove when needed. That, however, is not the case. Replacing the two-pane window we used to have, the new one is a single giant pane with bars glued to it, I guess to make it look like the others. It can't be opened from the bottom like the school-style window we used to have or slid up like normal human windows, but instead there's a large handle on the left: turn it 90° and you can swing the entire window inwards, which is what you'd do when moving; give it another quarter turn and you can kind of lean it inwards from the top about three inches to let some fresh air in. Note that this means that it's mildly easier to move the entire window than it is to open it just a bit.

Additionally, they added a "lock" to the window, presumably for policy or regulatory reasons, to make it so that we can't easily swing open the whole thing. I say "lock" because that's how the building guy described it to me. A more accurate description would be "a metal stick screwed into the window frame". It's literally a bar of metal placed a couple inches from the window, so the window will smack into it like it's a doorstop. It's also colored a dark green, which would have matched the color of the old window frame; the new frame, however, is white. Fortunately, it appears easy to remove, so I may do that.

To accommodate the inward-swinging top and this "lock", the maintenance guy had to move the windowblinds several inches, so that they're now placed right in the middle of the previously-deep frame. We used to have a couple plants and a bed for the cats up there; I guess we still could, but then we'd have to choose between never quite lowering the blinds all the way, bonking the cat on the head when lowering them, or manually angling them back behind the obstacles every time.

And the kicker: there's no screen, so we can't actually open the window unless we want a bunch of bugs in here anyway.

So yes, this new window is indeed good at the task of moving furniture in and out through it. Fantastically good, in fact! Unfortunately, that's something that happens only twice per tenant, and in theory tenants should not be coming and going particularly frequently. Certainly, less frequently than they might do things like, say, let some fresh air in, raise or lower blinds, and place things on the windowsill... tasks that are all significantly worse now.

Overall, I feel like my computer room's window was mugged.

XPages MVC: Experiment I

Tue May 22 17:28:00 EDT 2012

Tags: xpages mvc
  1. XPages MVC: Experiment I
  2. XPages MVC: Experiment II, Part 1
  3. XPages MVC: Experiment II, Part 2
  4. XPages MVC: Experiment II, Part 3
  5. XPages MVC: Experiment II, Part 4

Now that I've had a bit of time, I've started trying out some ideas for new ways to do XPage development. Specifically, I'm trying out the "XPages as the Controller" setup I pondered last time. The general goal is this:

  • There would be one XPage for each zone of concern (I don't know the right terminology): Posts.xsp, Users.xsp, etc.
  • The XPage itself would have almost nothing on it - it would exist as a trigger for the server to load an appropriate Controller class, which would in turn handle the output, usually by picking a custom control by name.
  • The custom control will handle all the appearance logic... and ONLY the appearance logic. The Controller will feed in any variables and data sources that are needed, and then the custom control will just reference those values entirely via EL.
  • The Controller will marshal will either create Domino View data sources directly or will use some wrapper Model class, in order to allow the XSP environment to handle efficiency and serialization.

So far, I've made some interesting progress. I originally set out to use a ViewHandler class to find the action and feed in the variables, but I ran into trouble getting to the viewScope or an equivalent in time for on-page-load value bindings to access them - either I was too early (before createView) or too late (after createView). What I really wanted was the beforePageLoad event, so I did just that: I created a view-scoped managed bean called "controller" that has some code to check what XPage is being loaded and to find a Controller class of the equivalent name (e.g. "Posts.xsp" → "xsp.controller.Posts"). Then, I do beforePageLoad="#{controller.control}" (I apologize for the names - I may change it to "router"), and it works nicely. The controller manager thing loads the class, asks it to figure out the current action ("list", "tag", etc.), and then tries to call a method by that name via reflection.

It sounds a bit weird, and the implementation is sort of half-baked, but the result is that you end up with a class with a structure like this (with actual implementation stuff removed):

public class Posts extends AbstractController { public void list() { ... } public void show() { ... } public void tag() { ... } }

Each of those methods corresponds to an action that can either be implied (like "list" being the default for Posts.xsp, established via other code) or derived from the URL, like "/Posts.xsp/show/1" to show the post with ID "1". The code in the method would fetch the appropriate object or collection and attach it to the XPage (as a dynamically-added data source, for example). Then, the XPage would load up an appropriate custom control for the action (for example, "pages_list" or "pages_show"), which would know what values to look for in the view scope.

I'm going to keep working on this, but I can think of a couple potential muddy areas:

  • I'm not sure how real data-backed custom controls (e.g. a list of posts by month in the sidebar) work into this. You can add beforePageLoad code in a custom control, but the view root is still the main page you're loading, so it's not as clean.
  • I'm not sure if it's worth trying to shuttle data source actions through the controller (like "save" for a document) or best to reference the methods in the model from the XPage directly. Since the environment is already "impure", I don't know how much bending-over-backwards is warranted.
  • The whole thing may be an exercise in cramming Rails-isms where they don't fit, like when someone familiar with SQL first starts programming for Domino. Just because something works doesn't mean it's correct for the platform.

I'm sure there will be more sticking points, too. Overall, though, this feels mostly good, or at least conceptually better than the other development paradigms I've tried with XPages. As the post title indicates, though, I expect this to be experiment number 1 of many.

More Musing About Controllers

Sun May 13 08:45:00 EDT 2012

Tags: xpages mvc

I've been thinking more about this MVC thing, thanks to re-learning Rails. I'm fairly convinced that moving as much code as possible out of the XPage and into wrapper objects is a big improvement (convinced enough that it feels silly that wasn't doing it already), but it still feels not quite right.

Two parts of the MVC trinity are straightforward in modern Domino development: Forms and wrapper objects are the Model and XPages are the View. The Controller is where things get muddy. Off the top of my head, routing and controlling are handled by:

  • Domino's built-in URL handling
  • "Display XPage Instead" on Forms and Views
  • Web rules in the Directory
  • navigationRules on the XPage itself
  • The XPage's names themselves, as used in URLs
  • Anything fancy you do with document mode switching and multi-page XPages

It's kind of a mess, and very few of these are really programmable. You can do a lot with web rules, but the application itself doesn't know about them, so you end up with code that's coupled with a specific server configuration - very unsafe. Domino's built-in URLs work well enough for basic operations - show a view, open a document, edit a document - but don't understand more complex concepts, like a shopping cart "Checkout" (yes, I just re-did the depot tutorial from the Rails book).

Honestly, it might be enough to move the action code out of the XPage and into the wrapper classes. That's a big step in readability, maintainability, flexibility, and error prevention, and most sites get along just fine with a lot less. Still, it doesn't feel right.

I don't really have a better option, though. This morning, I've been kicking the idea around that you could treat the XPage itself as the Controller, sacrificing URL and code cleanliness for some power. In this setup, a main XPage would correspond to an aspect of your application (say, "AdminSite") or management tools for a data type (say, "Posts"). You would construct your URLs by chaining path info past the ".xsp" part and then use the main page (or, ideally, a shared library across all pages) to figure out what the real request is. You'd end up with an extra level to your URLs, like "Posts.xsp/search?q=foo". It's horrifyingly ugly, especially right after "whatever.nsf", but it makes a sort of sense: ":application/:controller/:action?:parameters". The alternative now is to have multiple XPages that each deal with very similar things and, if you have a large app, name them in a structured way anyway, like "Posts_List.xsp" and "Posts_Search.xsp?q=foo".

You'd have to put the individual action code in custom controls with loaded parameters to match the action - this would add yet another type of element to the custom controls list ("subforms", actual custom controls, and now the View component of each action), but it may be a necessary evil. The Extension Library has something like this, though not exact, in its "Core_DynamicPage" demo.

The whole idea seems a bit half-baked at the moment but worth investigation, so I'll have to tinker with it a bit when I have some time.

The Language of the Platform

Thu May 10 19:34:00 EDT 2012

Tags: programming

The most recent episodes of Hypercritical and Build and Analyze discuss a new third-party iOS development environment called RubyMotion. Essentially, RubyMotion is an iOS version of MacRuby: an implementation of Ruby on top of the native Objective-C runtime, allowing programmers to write native apps in Ruby. This is similar in nature to JRuby and the like, in that the Ruby objects you deal with are real objects in the underlying platform, and you manipulate them in the same way, at near-native speed, but with a different syntax.

Most of their discussion involves around the point that it's unwise to use an environment like this as a way to avoid learning Objective-C. This is absolutely true: for whatever environment you're using, you should learn the native language, because it should be the best-supported and least quirky way to write programs. In the case of OS X and iOS, this means Objective-C + Cocoa, for classic Domino development, it's Formula and LotusScript, and for newer Domino development, it's Java and JavaScript. Even when the language is really terrible, like LotusScript, you're best served by using it and letting the platform be the platform, particularly when you're learning. In classic Notes/Domino, you can use Java for agents and JavaScript-in-the-client for one or two things, but you can tell very quickly that they're not natural fits - a lot of things aren't supported, you have to do weird stuff like .recycle() every Java object, and things just don't feel "right".

So, in short: don't use a non-native language to avoid learning how program in the "real" one, particularly when that secondary language is unsupported by the vendor. It won't save you much time to begin with, since learning the library is the bigger part by far, and it's likely to cause you hassle down the line.

However, I think there's still a place for this kind of thing — as well I should, considering how much time I spend trying to cram Ruby into Domino to avoid having to write in Java and JavaScript. I think that a wrapper environment like this can be good when:

  1. You are well-versed in the native language and the API, such that you wouldn't have any problem accomplishing the task you're setting out to do in it and can readily deal with platform-specific errors.
  2. Similarly, you're willing and able to re-write whatever you're doing in the native language should your wrapper become unsupported and incompatible.
  3. The wrapper environment/language is significantly better than the native one.

That last one is actually a point against RubyMotion for me (though I'm not a Mac/iOS developer, so I can't speak with authority), because Objective-C, particularly in its latest incarnation, is a very good language with a lot of very powerful features. Ruby is still more succinct and powerful, but I'm not sure it's SO MUCH more powerful that it's worth giving up the benefits of using the native environment and the inherent bonuses of using a strictly-C-compatible language.

Naturally, I find the differences more compelling in the case of Domino. Though Java and Server JavaScript make for a better programming environment than LotusScript did, I still find them distasteful, especially Java's endless verbosity. In that case, expending a significant amount of effort towards the goal of replacing them with a language like Ruby is very worthwhile. Lengthy Ruby programs are pretty much always going to be significantly smaller and easier to read than equivalent Java programs and Ruby supports numerous invaluable programming features that are either not present in Java or are so arcane that they may as well not be (closures & anonymous functions, class extensions & metaprogramming, symbols, easy hash literals, and duck typing, to name a few).

So as long as you go into it with eyes open and can weigh the pros and cons of adding an alien language to your platform, it can be a useful choice, but you should still pay your dues by mastering the "real" way to do it first.

Separation of Concerns

Wed May 09 19:53:00 EDT 2012

Tags: xpages

A while back, I wondered about the right way to write XPages. Things have changed a bit since then - Domino has gotten a bit better, the Extension Library exists and is great, and I'm a bit more adept with the environment. The forum app, which I should probably write a post explaining one day, came together kind of like how I mentioned there - Java classes to wrap all of the Domino access, which dramatically reduced the amount of code in the XPages themselves.

Still, I'm not sure I'm doing it right most of the time.

I've never done extensive work in a proper MVC environment, but I've gotten a bit of a taste for it on the occasions that I've tinkered with Rails. I feel like the components are there in XPages, but none of the official documentation encourages using any of them, nor does Designer encourage programming that way. You can do it in bits, though - for event handlers, you can point the action to a Java method via EL (like "#{someObject.doSomething}", if I recall correctly), and the "Next page" things in the XPage's properties panel use this underlying xp:navigationRule architecture, which is probably a mix of View and Controller, but it's an interesting idea.

I may have been on to something with the forum app, but I had to create a very aggressive caching system to make it at all practical. The end result was good - very little code in the XPages themselves - but there was more "framework" necessary than I think I'm comfortable with. Still, that discomfort isn't as bad as seeing a lot of business logic in an XPage (yes, I am aware that my main side project is a way to make it all the easier to do this).

I feel like the "right" way to do it would be to really restrict the XPage itself to be a page-layout engine, concerned only with declaring widgets and connecting them to actions on data. However, you can't get very far with just the components you're given, since you're eventually going to want to, say, set a couple values on a document programmatically. You can sort of stumble along with some of the built-in actions, but that's not necessarily any cleaner... just more XML-y.

Maybe the correct thing to do would be to make it easier to expand on Controllers or Models (my MVC knowledge gets shaky here) in code in Designer so that you could, for example, define a publish action for a blog post document that handles setting the published date, changing the status, and saving it. That way, the XPage would be referencing the actual job to be done - publishing the post - while the back-end code would handle the implementation details of replaceItemValue and so forth. Maybe it'd be best to treat Forms like an object definition - a combination of instance members (fields) and actions to be performed. They're already on their way to being mostly structural support for XPages apps.

I may try to structure my next XPage like this, perhaps writing wrapper objects that take Documents or DominoDocuments in their constructor and providing all the actions that I'd want to perform on them. I could set that up as a dataContext perhaps, and then use its methods instead of adding inline code on the XPage. It's worth some experimenting, I think.

Swapping Between JRuby Embed Methods

Tue May 08 10:23:00 EDT 2012

Tags: jruby xpages

When going about making Ruby in XPages work, I had to figure out which of the three JRuby embed methods to use: Core, JSR 223, and BSF. For my purposes, it seems like the need for BSF was obviated by JSR 223 (there are some differences, but I'm not sure they matter to me), so it's really a choice between the first two. I've tried both, and I keep swapping between them (the next beta, if things stay stable, will likely switch back to Core) and between various configurations inside them, due to various problems I've run into on a couple points:

  • Performance. This one is pretty straightforward - I want pages to load as fast as possible, ideally with no discernible difference between a JavaScript-only XPage and a Ruby-laden one.
  • Classloading. Ruby should be able to access the same class library as Server JavaScript, such as the javax.* classes and any custom classes in the NSF. If I just go the default route for each embedding method, Ruby can't access those classes, but setting the classloader may or may not work with a given configuration.
  • Clean Termination. This one's tough to tell. The "Core" route has a .terminate() method, but I'm not sure if this is strictly necessary or if it's just a good idea to do it when you're going to be spinning up a lot of runtimes, but otherwise it's fine to let the garbage collector do its thing. This should give me an excuse to try out some of that heap dump stuff.
  • Independence. By this I mean that, ideally, each page load and its associated script libraries will be completely independent of future calls - for example, a class declared in a script library should only be available when that library is loaded for the page and not otherwise. This conflicts with performance heavily - the fastest way to do it is to just have a single runtime, either for the JVM (via a singleton) or for the application, but this could lead to lingering methods and classes.

On all these metrics, it seems like Core is more flexible, but JSR 223 is the "right" way to do embedded scripting, being as it is the overarching framework for scripting languages in Java now.

Regardless of which of the two I choose, I have four different types of runtimes to choose from: threadsafe, concurrent, single-thread, and singleton. Those are more or less in order of "most independent" towards "fastest". However, it's not as simple as just choosing how fast or safe I want the code to be - I've also run into weird problems with some setups regarding the classloader and OutOfMemoryErrors. When I use the singleton and single-thread routes, everything is speedy, but class loading works unpredictably - it seems to SOMETIMES use the JSF runtime classloader, sometimes not, and sometimes it's different between my dev and production servers. I'm not entirely sure what causes the memory problems, but I think I've seen them on the concurrent and threadsafe routes.

For now, I have it set to use Core with the single-thread context and calling .terminate() at the end of each request. It SEEMS to be stable, but the home page in particular takes painfully long to load. I'll have to fiddle with it some more before I can safely release another beta.

A Quick-and-Dirty "$$ViewTemplateDefault" (-ish) for XPages

Thu May 03 20:16:00 EDT 2012

Tags: xpages

8.5.3 brought with it the very-handy "Display XPage Instead" property for views. It's great! That way, you can keep more of your existing URLs in old apps or just generally use cleaner ones in new apps - XPages are awesome, but ".xsp" in the URL is not.

In a full-blown app, you're probably going to point each view to its own XPage containing the hand-crafted fancified version. Sometimes, though, you just want to toss a xe:dynamicViewPanel on a page and that's good enough. However, unlike with the equivalent property for Forms, you can't just put a xp:dominoView data source on the page and have it pick up the view the XPage is replacing. With a Form replacement, the server translates a URL like "/view/document" to "/page.xsp?documentId=whatever&action=openDocument" for you; for a View, however, all you get is "/page.xsp". This stymied me when I just wanted to make a generic "View.xsp" to use as scaffolding for new views until I make a real page for each one.

Fortunately, though context.url (context.getUrl() in SSJS) shows the XPage path, you can use a longer property facesContext.externalContext.request.requestURI to get the original request's path info (the part after the server name - the other components are also available in that object). You can use that to fetch what's to the right of the database name (you could use ".nsf/", but facesContext.externalContext.requestContextPath + "/" would probably be technically safer in edge cases, assuming you can use ".nsf" in a folder name on a Domino server) to get the view name (extra line breaks for display purposes):

<xp:dominoView var="dbView">
	<xp:this.viewName><![CDATA[${javascript:
		java.net.URLDecoder.decode(@Right(
			facesContext.externalContext.request.requestURI,
			facesContext.externalContext.requestContextPath
			+ "/"), "UTF-8")
	}]]></xp:this.viewName>
</xp:dominoView>

Not beautiful, but it works. With that and a xe:dynamicViewPanel, you can get a sort of $$ViewTemplateDefault replacement, though you still have to specify it for each view.