Disclaimer and License

Opinions expressed here by Tim Tripcony are his own and not representative of his employer.

Creative Commons License
Tip of the Iceberg is licensed under a Creative Commons Attribution 3.0 Unported License.
Based on a work at timtripcony.com.

Unless otherwise explicitly specified, all code samples and downloads are copyright Tim Tripcony and licensed under Apache License 2.0.

Search

What the Quote?

"If you were a Boy Scout, I'd give you a merit badge for that."

Steven Rodgers

"You're the hypotenuse of my right triangle."

Nathan T Freeman

"In order to understand recursion, one must first understand recursion."

Author Unknown

« Taking the scary out of Java in XPages: Prologue | Main| Taking the scary out of Java in XPages: fixing the API »

Taking the scary out of Java in XPages: knowing the entry points

Category xpages java
Before we dive in to this first topic, I should mention Declan's series, "XPage Java Roots". Declan has been shifting more of his code to Java, so just as he did with his epic "Learning XPages" series, where he documented his initial experiences with XPages itself, he is now documenting his experience of learning how to take advantage of Java in XPage development. It's a safe bet that this series will be a very useful reference, so whether or not my own perspective on this topic proves valuable, you should definitely check out his articles on this subject.
As I previously mentioned, my goal with this series is to shed light on easy ways to incorporate Java into XPage development, and my intended approach is to try to answer the questions, "When?" "How?" and "What?". Because I believe the process of asking -- and answering -- these questions to be cyclical, I've chosen to start with a How topic: knowing the entry points for telling your XPage to run Java code.

You're already doing this.

If you've written even a single XPage, then the first two entry points are techniques I can guarantee you're already using.

The Eclipse platform includes a concept of a "builder": this is a task that runs when an Eclipse project is compiled. Designer includes several builder tasks. One of them scans all XPage and Custom Control design elements in the current application and generates a Java class that corresponds to each, then stores the compiled class file in design notes within the application. The first entry point, then, is simply writing an XPage... no matter what that page does: by creating any XPage or Custom Control, you're instructing Designer to generate Java code that will be run when your users access the page. So you're already producing Java code... you're just trusting Designer to write it for you.

The second technique uses a JSF concept known as a "resolver". The job of a resolver in XPages is essentially to parse text in real time and guess what Java code should be executed in its place. This means that we're now dealing with an interpreter instead of a compiler. If you want to get pedantic, Java is interpreted too, in the sense that it is stored in a format known as "bytecode", which must then be interpreted by the Java Virtual Machine, not executed directly by the computer itself. The JVM, however, is a layer between your code and the server's hardware that is incredibly optimized. JSF resolvers, on the other hand, are interpreters executed by that bytecode, so even if you consider Java to be an interpreted language, then we're nesting interpreters... no doubt that's enough computer science jargon to illustrate that there might be some performance implications here.

If you've ever looked at the source XML of an XPage and seen an expression like "#{currentDocument.Subject}", you've already seen a resolver instruction. This type of expression gets parsed on the fly, as your users access and interact with your XPage, and if Domino can successfully figure out what the expression "means", then it knows what Java code to run. If not, the result is an error. The portion of the XPages runtime that does the parsing is known as an EL resolver; the syntax we use for these expressions is called Unified Expression Language, or Expression Language for short... or just EL, for even shorter. In some cases, a $ is used instead of a #, but in the context of this topic, the difference that causes at runtime is irrelevant, because either way, the expression still serves as a shorthand instruction to run Java code.

The parsing of this syntax is highly efficient because there's a very small set of permutations of what it can contain. It packs a lot of power into a very limited set of operators. If you see a colon in one of these expressions, however, all bets are off. Well, some bets are off. This is because whatever precedes the colon is instructing the runtime to use an alternate EL resolver. For nearly all of us, by far the most common example of this is the "javascript:" prefix, which we typically refer to as SSJS. Any expression using this prefix tells the server to construct an abstract syntax tree based on what the text of that expression represents, according to the ECMAScript specification, then run Java code that approximates equivalent operations. This approach to parsing is still impressively efficient, but far less so than a "prefixless" expression, because there are so many more possibilities for what the expression can contain. The resolver must determine what everything means in context. So, for example, if it encounters parentheses, do they surround the definition of a new function's argument list, or arguments being passed to an already defined function? Or are they simply enforcing order of execution?

Regardless of which resolver is used, though, this is all EL: SSJS, therefore, is not an alternative to EL, it is a subset of EL. It just uses a very complex resolver instead of the default resolver.

But this is probably the most crucial point in this particular explanation: a key aspect of any EL resolver's behavior is called the variable resolver. The variable resolver's job is to determine what Java object every variable in your expression refers to. Once it knows what each object is, then it can determine what to do with and to those objects.

So let's take a second look at the example expression from above:

#{currentDocument.Subject}

When parsing this expression, the first thing the runtime does is look for a colon. None is present, so it uses the default resolver. The default resolver determines that the entire expression consists of two pieces: one variable named "currentDocument", and a property of that variable named "Subject". So it needs to figure out what "currentDocument" refers to.

There are a couple places it looks. One of them is the requestScope. So if you manually store something in the requestScope, the key you used will remain a valid variable for the remainder of that request. There are cases where variables automatically created by Domino are only valid for a subset of request processing... for instance, the row variable for a view panel or repeat control, or a dataContext assigned to a specific panel. But in general, an object stored in the requestScope can be accessed by key without explicitly referring to requestScope, because the variable resolver treats requestScope keys as variable names.

If everything goes as planned, the variable resolver should determine that "currentDocument" refers to an instance of a Java class called com.ibm.xsp.model.domino.wrapped.DominoDocument. Now it needs to decide what to do about the "Subject" property. For this specific example, you'd most likely create this expression to assign as the "value" attribute of a data component, such as an Edit Box. If this is the case, then the same expression can mean one of two things; either the page is being "rendered" (sent to the browser), which means this is a read operation, or data is being posted to the server, so this is a write operation. Once the server has determined which, it needs to decide what methods to run to handle the property.

The DominoDocument Java class is an instance of a Java interface called com.ibm.xsp.model.DataObject. LotusScript supports the definition of classes, but does not have a notion of interfaces, so this is a new concept we can take advantage of in Java. An interface is basically a promise: specifically, that any class claiming to "implement" it will have all of the methods that interface defines. However, it may choose to implement those methods very differently from how other classes that implement the same interface implement its methods. In the case of DataObject, the two methods you'll most want to be aware of are getValue() and setValue(). Because these are part of the DataObject interface, that means that any data source in XPages has these two methods. What these methods do behind the scenes will typically be wildly different based on which type of data source you're dealing with. In the case of DominoDocument, they handle getting data to and from the "back end" document.

So, in a read operation, #{currentDocument.Subject} ends up running as something approximately equivalent to the following Java code:

VariableResolver resolver = ApplicationEx.getInstance().getVariableResolver();
FacesContext context = FacesContext.getCurrentInstance();
DominoDocument doc = (DominoDocument) resolver.resolveVariable("currentDocument", context);
return currentDocument.getValue("Subject");


In a write operation, however, the last line will be slightly different:

doc.setValue("Subject", newValue);

This is one of the handy benefits of EL: not only is it very efficient, it allows us to use a single expression for a property that is read/write, and just let the platform figure out what to do. But this is what is really happening in XPages... all of our EL expressions tell Domino to run Java code on our behalf. It's just not necessarily our Java code.

Stop guessing.

When you call methods on any object in SSJS, therefore, you're already using Java... you're just using a bizarre IDE to do so. Every reference to session or database or context.getUser().getRoles().contains("[Admin]") is causing Java to execute... Domino just has to temporarily interpret all that code as JavaScript in order to guess what Java code to execute.

Similarly, when you define your own objects in SSJS, the variable resolver turns them into instances of Java classes that approximate the behavior of JavaScript objects in a browser. These objects have characteristics in common with portions of the Java Collections Framework but include some extra overhead. There are also classes in that framework that do a lot of useful things for you automatically without you having to tell them to, like ensure that members of a list are unique and / or automatically sorted. Paul Withers found a handy cheatsheet that tersely describes what each is useful for.

So one of the easiest ways to start getting additional benefits from Java code is to occasionally allow Domino to stop guessing what you want, just for an individual object, by explicitly stating that it should be an instance of a specific Java class.

This is done by using the full canonical name of the class. Just like Domino user names have a common format (e.g. "John Doe") and a canonical format (e.g. "CN=John Doe/O=ACME"), so do Java classes. The canonical name of the HashMap class, for example, is java.util.HashMap. The portion prior to the common name (e.g. java.util) is known as its "package". Java packages are just sets of related classes, similar to how we tend to bundle related LotusScript code into a Script Library.

So if you were planning to write the following code:

var userInfo = {}; // because you already know never to use 'new Object()'
userInfo.firstName = userDoc.getItemValueString("firstName");


You might choose to tell Domino that you don't need the extra overhead of a JavaScript object (like support for maintaining closures) by writing this code instead:

var userInfo = new java.util.HashMap();
userInfo.put("firstName", userDoc.getItemValueString("firstName"));


So instead of getting an object that is like a HashMap, but with features you know you're not going to use anyway, you get an object that is a HashMap.

If you know you're going to be creating a bunch of objects from the same package, you can save a few characters on each line by using the importPackage() statement:

importPackage(java.util);
var firstMap = new HashMap(); // common name is now enough
var anotherMap = new HashMap();
var sortedMap = new TreeMap(); // different class, but same package


At last count, the XPages runtime includes over 1,500 packages that just ship with the platform. Not 1,500 classes... 1,500 packages. For a lot of what you want to do in an app, Domino already makes code available to you that will do it for you instead of you having to reinvent the wheel (or call a LotusScript agent because you have old code that does the same thing). Using only the syntax above, you can skip writing (or maintaining) your own custom code for doing common things like parsing or generating JSON and XML or encoding and decoding Base64. Stuff like that is just built in to the platform now.

The only tricky part here is actually finding out that what you want to do has already been done for you, so it's very tempting to ignore that all this code is just right here waiting (with apologies to Richard Marx). But if you do choose to ignore it, either you are less productive (because your code takes longer to write) or your users are (because your code takes longer to execute)... or both. So I encourage you to discover what is already available to you. The difficulty is that there's no quick and easy way to do this. The most comprehensive list I've found is the Extensibility API Javadoc, which is already a bit outdated, because it's for 8.5.2, and they don't seem to have published an updated Javadoc for 8.5.3. Additionally, this only covers the couple dozen pacakges that are considered part of the Extensibility API... so who knows what's in the other 1,500 packages and how to interact with them.

So at present the best -- and far from ideal -- way to get a feel for what is already available to us without having to install anything else or bundle additional libraries into our applications is to switch to the Package Explorer, and underneath the root for any open application, expand the section labeled "Plug-in Dependencies". Every .jar file listed there is a collection of packages, each of which is a collection of classes, each of which is a collection of methods. I feel comfortable guaranteeing that, if you're still calling LotusScript agents to avoid having to rewrite all that code, at least some of it has already been rewritten for you in those bundled packages.

And the good news is that even if you only learn about one class that allows you to skip running a LotusScript agent against an in-memory document, your app will perform better than it otherwise would, and you have less code to maintain because you're now calling platform code instead of a custom equivalent. The platform API now includes so many features that none of us will ever discover, much less master, every one of them, but don't let that become an excuse for never learning any of it.

To be continued...

This post is already longer than I'd planned, so I'm going to wander off and pick this up again later. Note that so far we have neither written any custom Java code ourselves, nor have we added anything to the platform from external sources, such as open-source Java libraries or a commercial product API. We've just been removing a tiny bit of the ambiguity around which type of Java objects our XPages will use.

In the next post I'll cover how to allow the exact same pattern to add capabilities to the platform that don't ship with it... again, without writing any actual Java yourself. That can wait for later.

Comments

Gravatar Image1 - Forgive me for being pedantic, but I have one small note on this excellent explanation...

"...When parsing this expression, the first thing the runtime does is look for a colon. None is present, so it uses the default resolver."

The determination of how an expression is going to be resolved is done by the Builder process. The "javascript:" prefix causes the builder to use a different version of the ValueBinding class. (JavascriptValueBinding, surprise, surprise!) The prefix is even dropped from the String literal in the generated Java code.

Great post, Tim.

Gravatar Image2 - The builder does? I thought it was done by the evaluator.createValueBinding(...) or evaluator.getBindingValue(...) line, which should look for registered language factories, in case any were snuck in at runtime.

Gravatar Image3 - Ack! I stand corrected! You and Tim are absolutely correct.

I guess I was just remembering the way I always did it when constructing component trees myself! Emoticon

Gravatar Image4 - Incredibly useful info, Tim! Keep writing it, Professor T., and I'll keep showing up for class in the front row.Emoticon - thanks

Gravatar Image5 - Good post Tim. Your explanations are very helpful in making the transition to Java. I look forward to your future posts.
Thanks!Emoticon

Gravatar Image6 - should this line...
      return currentDocument.getValue("Subject");
...read instead as...
      return doc.getValue("Subject");
...?

Post A Comment

:-D:-o:-p:-x:-(:-):-\:angry::cool::cry::emb::grin::huh::laugh::lips::rolleyes:;-)