.NET Meet Java (and vice versa)

Here are my notes from this session, which, BTW, is the best talk I’ve seen today. (The best yesterday was Eric Whitley’s.)

Interesting stat: 80% of the Fortune 500 use .NET.

Microsoft is committed to a January 2007 release of Excel Services for the Web, which has interesting implications for ALUI. Perhaps there’s an opportunity here for building ALUI/MS Excel integration the right way. Finally.

The .NET Application Accelerator is coming in a matter of weeks. It will expose any .NET application as a WSRP service that can be consumed by ALI or WLP.

My Love Affair with ALI Taglibs

There’s been some recent activity on this very old thread in the newsgroups regarding displaying the help link in a portlet. Until G6, this could only be done with native code AFAIK. But, if you supress the portlet title bar, there really aren’t many places where you can put native code in a portlet.

Enter G6 and the extensible taglib support, a quiet little feature that (without any fanfare or marketing by BID) has seriously changed my life.

The source speaks for itself. It look 15 minutes to write. (Granted, I already had my ALUI development environment all set up.)

HelpURL.java:

package com.bdgportal.alui.taglibs;

import com.plumtree.openfoundation.util.*;
import com.plumtree.portaluiinfrastructure.tags.*;
import com.plumtree.portaluiinfrastructure.tags.metadata.*;
import com.plumtree.server.*;
import com.plumtree.xpshared.htmlelements.*;

public class HelpURL extends ATag {

public static final ITagMetaData TAG;
public static final RequiredTagAttribute PORTLET_ID;
  public static final RequiredTagAttribute ID;
  public static final OptionalTagAttribute SCOPE;

static
{
 TAG = new TagMetaData("helpurl",
   "Puts the help URL for this portlet into the variable specified by the ID attribute.");

 PORTLET_ID = new RequiredTagAttribute("portletid",
   "The portlet ID.",
   AttributeType.INT);

 ID = new RequiredTagAttribute("id",
   "The name of the variable in which the help link should be stored.",
   AttributeType.STRING);

 SCOPE = new OptionalTagAttribute("scope",
   "The scope used to store the the help link.",
   AttributeType.STRING, Scope.PORTLET_REQUEST.toString());
}

public HTMLElement DisplayTag() {
 ((IXPList)GetState().GetSharedVariable(GetTagAttributeAsString(ID),
  Scope.GetScope(GetTagAttributeAsString(SCOPE)))).Add(
     ((IPTWebService)((IPTSession)GetEnvironment().GetUserSession()).GetWebServices()
  .Open(((IPTGadget)((IPTSession)GetEnvironment().GetUserSession()).GetGadgets()
  .Open(GetTagAttributeAsInt(PORTLET_ID), false)).GetWebServiceID(), false))
  .GetProviderInfo().ReadAsString("PTC_HTTPGADGET_HELPURL"));
 return null;
}

public ATag Create() {
 return new HelpURL();
}
}

To deploy this code, see the excellent section on edocs about creating custom Adaptive Tags.

To use this code in a portlet, do the following.

myportlet.htm:

<span xmlns:pt='http://www.plumtree.com/xmlschemas/ptui/'>
	<pt:mytaglibns.helpurl pt:portletid="234" pt:id="helplink"/>
	<pt:core.html pt:tag="a" href="$helplink">Help</pt:core.html>
</span>

I didn’t test this, so YMMV. Have fun!

Comments

Comments are listed in date ascending order (oldest first)

  • That’s slick, Chris – that’ll be handy for porting between devstageprod where objectids may be different 🙂

    Posted by: ewwhitley on September 13, 2006 at 6:20 AM

  • Hi, This code makes ten database requests just to get the the IPTWebService object for given portlet. Is there any better way to do this?

    Posted by: Piotr Dudkiewicz on May 18, 2007 at 6:48 AM

  • Sorry, but there’s no better way to get the help URL out of the web service. ALUI is optimized to make calls to its database and the UI code does that everywhere — it’s a dynamic web application, so that should be expected.

    Posted by: bucchere on May 29, 2007 at 2:03 PM

  • It seems that ALUI is optimized to do as many database calls as it’s possible;) Thanks.

    Posted by: Piotr Dudkiewicz on June 1, 2007 at 2:46 AM

Washington DC Area ALUI (Plumtree) Training

bdg plans to offer a 5-day AquaLogic (Plumtree) Training Course in the Washington DC Metro Area starting on October 16th. We’re combining our two most popular courses into one jam-packed week. The first three days will cover Plumtree Administration followed by two days of Portlet Development with the IDK. The course will be given in Herndon, VA about 10 minutes from Dulles Airport. All course materials have been updated for the G6 version of ALUI.

If you’re interested, you may download the complete syllabi from our web site.

There are no pre-requisites for the Plumtree Administration course other than a basic understanding of Web applications. The Portlet Development course requires strong programming skills in either Java or .NET including Web programming concepts such as posting forms, retrieving parameters from the querystring and working with basic Javascript for form validation.

The price for this course is $500/day. You may attend the 3-day segment, the 2-day segment or the entire week.

There is a limit of 10 participants. You must sign up by Friday, October 6th and bdg must receive payment by Friday, October 13th. We now accept VISA/MC payments in addition to corporate checks and money orders.

To sign up, please send an e-mail to [email protected]. If you have any questions, please send us an e-mail or call us at 703 234 7910.

ALUI Portlet Pagination Cookbook

A coworker asked me how to write Plumtree portlet pagination (i.e. showing records in a UI and allowing the user to move from page to page, n records at a time) the other day and the ensuing discussion made me rethink how I’ve always done this and consider some new options. In this post, I attempt to shed some light on a very simple concept that turns out to be quite interesting in terms of implementation.

First, let’s consider some of the ways developers typically add pagination to standard web applications, forgetting about portals and portlets for a moment. Let’s call n your page size, i your page number and t the total number of records. On the first page, you might see n records laid out with alternating row colors and a 1-n of t marker to show you were you are, e.g. Now Showing Records 1-5 of 45. There’s probably also a next button and a back button (grayed out for now), a first page button (also grayed out), a last page button and maybe even a “fast-forward” button to move forward several pages at a time.

A very easy way to implement this in a standard (non-portal) MVC Java/J2EE Web application would be to carry some state, say i (the page index), on the querystring. For example, say you have a record viewer servlet called “RecordView” running on a Java-enabled Web application container such as Tomcat. You could have something like http://bdg-plumtree:8080/bdg-plumtree-context/RecordView as your URL. Your servlet code snippet might look something like this:

import javax.servlet.*;
import javax.servlet.http.*;

...

private Model model;

protected void doPost(HttpServletRequest request,
                      HttpServletResponse response)
                        throws IOException {

    //if you didn’t specify a page argument, display the
    //first page (pageIndex = 0)
    int pageIndex = request.getParameter("i") == null ? 0 :
      Integer.parseInt((String)request.getParameter("i"));

    //make a call into the data model to get the i-th page
    //and put the results in the request
    request.setAttribute("results", model.getResults(pageIndex));

    //forward to your view (JSP)
    request.getRequestDispatcher("view.jsp").forward(request, response);
}

In the view, you would simply print out the results, line-by-line, perhaps alternating row colors to make it easier to read. Then you need to display the little marker that tells you what page you’re on and the buttons to get next/back/to the end/etc. The logic to figure out what buttons to display and which buttons to gray out is a little involved, but it’s mundane enough that I don’t think I need to cover it here. The important part for this discussion is what’s in the links that actually take you forward and (soon) back. The answer there is simple enough — just create links that append the appropriate querystring and you’re all set. Here’s an example:

<a href="http://bdg-plumtree:8080/bdg-plumtree-context/RecordView?i=<%=i + n%>">Next</a>

So far so good.

The problem is that when you try to do this in an ALUI portlet, you don’t have direct access to the querystring, so you can’t use this approach. You need to store the variable i using some kind of state mechanism. Here are your options:

  1. The HTTP session
  2. A portlet setting
  3. A session setting (G6 and up only)

There are tradeoffs between #1 and #2 but #3 offers a good compromise. Let me explain.

If you use the HTTP session, your users’ page setting (i), will only persist for the life of the session, which is probably desirable. But, if you’re using the Plumtree caching model for portlets (such as expires or last-modified/etag caching), you can’t cache this portlet at all, which is definitely not desirable. The reason is that every page, regardless of the value of i, will have the same cache key.

To implement session-based pagination, you only need to change two lines of code:

request.getParameter("i")
becomes request.getSession().getAttribute("i") and your anchors that control the moving from page to page now need to point to a different controller servlet. (Remember, you don’t have control over the query string any more in portletland).

<a href="http://bdg-plumtree:8080/bdg-plumtree-context/ChangePage?i=<%=i%>">Next</a>

The ChangePage servlet simply sets the session attribute and then calls return to portal as shown here:

request.getSession().setAttribute("i",
    request.getParameter("i"));
PortletContextFactory.createPortletContext(request,
    response).getResponse().returnToPortal();

The only way to unique-ify the cache key, therefore caching your portlet appropriately, is to go with approach #2, the portlet setting. Now, when users advance i, they will be creating a new cache entry for each value of i (since settings are always part of the cache key). The drawback is that the users’ page settings (i) will persist longer than the life of the session. In other words, they could be browsing page 5, then they could leave the portal for several days, come back, and still be on page 5!

For your view:

PortletContextFactory.createPortletContext(request,
  response).getRequest().getSettingValue(SettingType.Portlet, "i");

And for your ChangePage servlet:

PortletContextFactory.createPortletContext(request,
  response).getResponse().setSettingValue(SettingType.Portlet, "i", request.getParameter("i"));
PortletContextFactory.createPortletContext(request, response).getResponse().returnToPortal();

G6 offers a nice compromise: the session setting. (If I had to guess, I would say the session setting was designed expressly for pagination.) With a session setting, you get the best of both worlds: a page setting that lasts only for the duration of the session but also a unique cache key so that you can effectively cache your portlet.

For your view:

PortletContextFactory.createPortletContext(request,
  response).getRequest().getSettingValue(SettingType.Session, "i");

And finally, for your ChangePage servlet:

PortletContextFactory.createPortletContext(request,
  response).getResponse().setSettingValue(SettingType.Session, "i", request.getParameter("i"));
PortletContextFactory.createPortletContext(request,response).getResponse().returnToPortal();

All of these methods have one drawback — they refresh the entire page on every portlet pagination click. So . . . stay tuned for an upcoming post on AJAX-based portlet pagination.

Comments

dev2dev comments are listed in date ascending order (oldest first)

  • This is a good start for everyone on Java. If you are like me and are a .NET developer at heart, you will be glad to know the .NET Web Control Consumer supports the use of a MS data grid control. All that is needed is for the developer to drag one of these objects onto the form, hook it up to a data source, and presto, you get pagination, storability, edit, and any other thing you always wanted from a table.

    Andrew Morris – [email protected]

    Posted by: drews_94580 on August 15, 2006 at 2:03 PM

  • It’s true — in many ways, .NET is way ahead of Java. I have only a little experience with the .NET Web Controls and the Control Consumer, but from what I’ve seen, there’s a lot of power and flexibility there. My post was making the “I want to roll my own pagination in Java” assumption. 🙂

    Posted by: bucchere on August 16, 2006 at 8:30 PM

Caveat Emptor: Using Varpacks in Pluggable Navigation

I got burned by this today, so I thought I would share it with you all. I had a perfectly good and working pluggable navigation that loaded a remote portlet in the left navigation pane. The only problem was that I had hardcoded the portlet ID and I wanted to make it configurable. So naturally I put the portlet ID and some other settings in a varpack, which is a reasonable thing to do. I then called my varpack from the constructor of my pluggable navigation class that implements IView.

Then I spent the next hour banging my head against the keyboard.

I kept getting a nasty Invocation Target Exception coming out of the reflection methods used to pre-load pluggable navigations. Eventually, using the handy space=MemoryDebug trick, I was able to ascertain that my varpack XML syntax was wrong and my varpack was looking empty to ALUI. So I fixed that and still, I got an ITE.

Then I looked at a PTSpy log and I discovered that the loading order of objects in ALUI was to blame. The portal loads built-in varpacks first, then it loads pluggable navigations, then it loads custom varpacks. So you can’t use the varpack from the IView constructor. I moved it to the Display method and everything started working again. Phew. 😐

So, shame on me for trying to use a custom varpack before it was loaded. Make sure not to make the same mistake!

Passing information between Plumtree portlets

This matter has been a subject of a great deal of debate among many of our customers, so I thought I would share my thoughts on the topic. What better place to do it than here on bdg’s Plumtree blog? 😉

This post expounds on the many methods you can use to pass information from one portlet to another or, in the more simple case, just store information temporarily for use later by a single portlet. There are at least three approaches I’ve found that accomplish this: the Plumtree Settings approach, the PCC/Adaptive Portlet approach and the backend-system approach. As you’ll see, there are also hybrids of these approaches that may work best for you, depending on your environment. I’m going to describe each of these in detail, but first, allow me back up a bit and explain the problem.

You’re building what Plumtree now calls an IAM (Integrated Activity Management) application. If you don’t think you are, check out this example (requires Flash). If you thought you were building an SOA (Service Oriented Architecture) application, well, a rose by any other name is still a rose, right? Basically, IAM, SOA and Composite Applications all mean the same thing to me, more or less. The way I define them is: you’re building an application that allows your end-users to achieve a business goal, say, an employee enrolling in his or her company’s health benefit plan. In order to pull this off, you need to build integration with several different corporate systems including perhaps getting an employee record from the HR system, deducting pre-tax payments from the employee in the company’s payroll system, and then registering the employee with an external site’s web services-based API to enroll him or her with the third-party benefits provider.

In order to pull this off in Plumtree, you’re going to need to design and build several portlets and perhaps a couple community pages or even multiple communities. Exactly how many portlets and whether to use one page, more than one page or one or more communities is one of the many decisions you’ll need to make in designing your application. All your decisions, BTW, should depend on how you want to structure your navigation and your screens to make your application usable. These decisions should not be ruled by what Plumtree is or isn’t capabile of doing but instead by how you’re going to make it easy (and even fun) for your employee to enroll in health benefits.

So, all that business aside, you’ve designed your application and it uses more than one portlet on, say, different communities. In keeping with our example, let’s say the first portlet allows the employee to confirm or update all his or her personal information and the second portlet asks the employee to pick amounts to be deducted from his or her paycheck for premiums and flexible spending. Now you’ve created at least two technical problems for yourself: 1) how do you get from the Employee Information community to the Payroll Management community and 2) how you get information (or state) from the Personal Information portlet to the Flex Spending portlet?

The answer to the first question is easy – use the pt:openerLink markup tag to create an URL that takes you from the Employee Information community to the Payroll Management community.

The answer to the second question depends on what type of state you need to pass, what form it’s in and how big it is. I’ll keep those things in mind as I discuss the three approaches to passing state.

Plumtree Settings approach

This approach works well if you have small bits of state that can be represented as strings and if you don’t mind having the page refresh (which it’s going to do anyway as you move from one community to another). To pull this off, you’ll need to use a User Setting, a type of setting that is unique for every user but that can be read (and set) by any portlet. First, you’ll need to settle on a name for your User Setting and then configure your Plumtree Web Service to “listen” for that setting. To do this, you need to open your Web Service editor, go to the Preferences page and then under where it says “Add specific preferences that you would like sent to this Web Service,” enter the name of your User Setting. You’ll need to do this in all the Web Services that need access to this information.

In our benefit enrollment application, the most likely thing we’ll need to pass is the employee ID, which shouldn’t be hard to represent as a string. Here’s what the code might look like in the Personal Information portlet:


PorltetContextFactory.createPortletContext(request, response).getResponse().setSettingValue(SettingType.User, "EmployeeID", employeeID);

And here’s the code for your Flex Spending porltet:


String employeeID = PorltetContextFactory.createPortletContext(request, response).getRequest().getSettingValue(SettingType.User, "EmployeeID");

PCC/Adaptive approach

The Plumtree Settings approach works well in this case, but what if you wanted to do this without a page refresh? Then I might suggest looking at two different adaptive portlet patterns: the Master-Detail pattern and the Broadcast-Listener pattern. These patterns do exactly the same thing, but with one major difference. The Master-Detail pattern assumes that you have no more than two portlets and that your two portlets are on the same page and the Broadcast-Listener allows you to send information to portlets on different pages and to broadcast from one portlet to many listener portlets.

Here’s how the PCC/Adaptive approach fits into our example: when the employee finishes updating his or her personal information in the Personal Information portlet, he or she is expected to click a button or link to indicate completion. The button or link needs to be tied to a javascript method that passes the employee’s ID to a PTHTTPGETRequest for the content (or a segment of the content) of the Flex Spending portlet using a querystring argument. When setting up a PTHTTPGETRequest, you’ll notice that the second argument is essentially a function pointer that allows this object to execute a callback when the response completes. All that callback function needs to do is place the content of the Flex Spending portlet into a div tag and you’re done.

I think Plumtree does a great job with all their Adaptive Portlet examples – you can literally copy and paste them into your portlet code and they just work. However, if you get stuck and need more help, feel free to post here or on portal.plumtree.com.

Backend System approach

So we’ve talked about how to pass a small piece of state from one portlet to another, covering the with-page-refresh and without-page-refresh cases in addition to the single-community/page and multiple-community/page case. But what about the case where your state is not quite as simple as a employee ID?

The first question I have to ask is, why are you trying to pass a lot of state? Isn’t the employee ID sufficient to go into the backend system and get whatever data you need? If the answer is no, then what form is your state in? Do you have a piece of text or an object? If you have a small piece of text, you can use either of the first two approaches: set it as a User Setting or URL-encode it and pass it around using Master-Detail or Broadcast-Listener. If you have a small object, you can serialize it and then Base-64 or UUEncode it and then use one of the first two approaches. But you need to be very careful. There are limits to the amount of data you can put on a querystring and limits to amount of data you can put in a header. These limits are not enforced by the HTTP spec, but the spec warns you about artificial limits on URL length based on web server implementations. Mark Nottingham, Senior Principal Technologist at BEA Systems, cautions against using headers larger than 2048 bytes in this post. Remember, all Plumtree Settings are passed back and forth between the portal and portlets as HTTP headers.

So you’re stuck with a somewhat bulky object, let’s say in our example, the entire employee’s record in a DTO, and so you’ve ruled out the first two approaches. Now what?

If you’re a traditional web programmer who’s new to Plumtree, you might have been thinking this all along – what about the HTTP session? Isn’t storing and passing state from page to page what sessions are for?

Sessions are great place for things like an employee record DTO, but in Plumtree, each portlet has its own session on the remote server. This means that you can put whatever you want in the session, but you’re not going to be able to share it with other portlets. Using the session also has interesting ramifications if you’re using Plumtree caching, but that’s the subject of another post.

So, what about the HTTP application? If you have a single remote server and all your portlets are using it, you can store objects, like the employee record DTO in the application, using a key consisting of the employee ID as follows (inside a Java servlet):


synchronized(this) {
this.getServletContext().setAttribute("com.bdgportals.iam.example.EmployeeRecordDTO" + employeeID, employeeRecordDTO);
}

Different portlets can now access this attribute, but you need to make sure that all your code that writes to this attribute is threadsafe.

Of course, this assumes that you’re using the same application server for all your portlets, which is not necessarily true, even for a single application (because you may be load-balancing your porltets across different application servers).

* * *

If there are other ways you’ve found to pass state between portlets, I’d love to hear about them via comments on this post.

Washington DC Plumtree Training

bdg plans to offer a 5-day Plumtree Training Course in the Washington DC Metro Area starting on August 22nd. We’re combining our two most popular courses into one jam-packed week. The first three days will cover Plumtree Administration followed by two days of Portlet Development with the EDK. The course will be given in Herndon, VA about 10 minutes from Dulles Airport.

If you’re interested, you may download the complete syllabi from our Web site.

There are no pre-requisites for the Plumtree Administration course other than a basic understanding of Web applications. The Portlet Development course requires strong programming skills in either Java or .NET including Web programming concepts such as posting forms, retrieving parameters from the querystring and working with basic Javascript for form validation.

The price for this course is $500/day. You may attend the 3-day segment, the 2-day segment or the entire week.

There is a limit of 10 participants. You must sign up by Friday, August 12th and bdg must receive payment by Friday, August 19th. We now accept VISA/MC payments in addition to corporate checks and money orders.

To sign up, please send an e-mail to [email protected]. If you have any questions, please send us an e-mail or call us at 703 234 7910.

bdg launches the PHP EDK 5.1

I am very pleased to announce that on Friday, 8 July 2005, bdg released the PHP EDK 5.1 to the Plumtree Code Share. Largely due to a Herculean effort on the part of Rich Weinhold, our resident PHP and Plumtree Guru, we were able to take the com.plumtree.remote.portlet.* package from zero to released in just three weeks.

We are offering this code up for redistribution and use under the BSD License, which is the standard for the Plumtree Code Share.

In order to access the code (for now), you’ll need to create a login on portal.plumtree.com. We are currently considering other distribution methods, such as SourceForge, and we’ll make an announcement should we choose to go down that path.

We look forward to seeing some PHP portlets start to emerge for the Plumtree platform. If you’re interested in developing a PHP portlet for Plumtree, let us know — we’d love to hear from you.

The future of JSR-168

There is some interesting speculation about the future of JSR-168 going on at the Portlets Yahoo! Group. IMO (which is a redundant thing to say because this whole blog is My Opinion), I don’t think the spec is going away by any means. But at the same time, it seems that the entire industry has come to recognize it as the “lowest common denominator” of portlet functionality. In terms of features, it fails to specify portlet-to-portlet communication APIs among several other things. But worst of all, it’s designed with the presumption that the portlets will always run inside the portal container. Humph.