.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

Everyone likes a friendly URL

As part of our BEA World strategy for this year, we’re revamping our corporate web site, http://www.bdg-online.com. You should expect an unveiling in the upcoming weeks.

While there will be some revised and some additional content, this is primarily an infrastructure upgrade, including moving to more powerful virtual hosts and upgrading the backend from ASP to ASP.NET (yes, I know, it’s about time).

One of things that really bugs me about ASP and ASP.NET is the failure to include built-in support for friendly URLs. By friendly I mean something that doesn’t end in .asp, .htm, .aspx or some other extension and naturally also doesn’t have a querystring (?foo=bar&boo=moo . . . etc.). For example, http://www.bdg-online.com/customers is a lot more friendly than http://www.bdg-online.com/customers.asp and definitely more friendly than something like http://www.bdg-online.com/content.aspx?p=/customers.

Java provides a nice facility for this in the form of servlet mappings. Since a lot of people are using MVC these days, you are probably going to set up servlet mappings anyway. Here’s an example from a sample web.xml file:

<servlet>
 <servlet-name>customers</servlet-name>
 <servlet-class>com.bdg-online.www.Customers</servlet-class>
</servlet>

<servlet-mapping>
 <servlet-name>customers</servlet-name>
 <url-pattern>/customers/*</url-pattern>
</servlet-mapping>

But what about ASP or ASP.NET? No one cares about ASP any more, so I didn’t bother to research that. But for .NET, I came up with a simple and elegant solution to the friendly URL problem. All you need to do is add the following code (or something like it) to your Global.asax.cs file:

protected void Application_BeginRequest(Object sender, EventArgs e)
{
 if (!Request.RawUrl.EndsWith("htm") &&
     !Request.RawUrl.EndsWith("css") &&
     !Request.RawUrl.EndsWith("ico") &&
     !Request.RawUrl.EndsWith("jpg") &&
     !Request.RawUrl.EndsWith("js") &&
     !Request.RawUrl.EndsWith("gif"))
 {
   if ("".Equals(Request.RawUrl) || "/".Equals(Request.RawUrl))
   {
     Context.Server.Transfer("default.aspx");
   }
   else
   {
     Context.Server.Transfer(Request.RawUrl + ".aspx");
   }
 }
}

You’ll note that I forward requests to the corresponding aspx page, as long as the request isn’t for static content (images, css, etc.).

Of course you also need to do two things:

  1. Configure an .aspx page for every friendly URL you want resolved
  2. Add a wildcard mapping in IIS for * (files without extensions) to the asp.net ISAPI filter

The process for #2 is a little involved. It’s also different for IIS 5 (XP) and IIS 6 (2003). I don’t feel like posting screen shots right now, but if anyone wants to give this a trial run and can’t figure out how to do #2, just e-mail me and I’ll walk you through it.

I just came up with this a couple of hours ago and I haven’t put it through much testing, so YMMV.

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

My take on the acquisition of Plumtree by BEA

Several colleagues, coworkers, customers and other Plumtree partners have asked me for my opinion on the buyout of Plumtree by BEA Systems. I certainly have thoughts and comments about this event, but moreover I have several open questions that I want to ask Plumtree, BEA and the community of customers and partners. Of course I have my own take on the answers, but I’m curious to hear from others in the community.

First, let me say this: I feel overwhelmingly positive about the acquisition. BEA is a great company with excellent products (Weblogic, Tuxedo, JRocket) and a solid strategic vision. Most of the articles I’ve read have said that they plan to make Plumtree its own business unit and continue to support Plumtree’s 700 customers. By purchasing Plumtree, BEA has made a strong, albeit implied, statement about the portal market. You won’t read this in any of the articles out there, but it’s a statement that I’ve been making for a long time: Plumtree is clearly the best and the only pure-play horizontal portal technology out there. All of this is good news for Plumtree and for Plumtree partners like bdg.

Now, on to my questions . . . .

Will BEA continue to support Plumtree on .NET?

According to the FAQ published on BEA’s web site, the company plans to support Plumtree on all of the existing platforms and application servers on which it runs. This is a major change of direction for BEA, which has always aligned itself more with the Java/Sun/McNealy vision that the .NET/MS/Gates vision and which ties all of its products to its own application server, Weblogic.

The problem is that IT departments in major companies have their own near-religious beliefs about platforms. Some want “pure” Microsoft stacks (Windows, SQLServer, IIS, .NET/CLR), some want “pure” Java stacks (Solaris, Oracle DB, Weblogic/Websphere/Tomcat/JBoss and Java/JVM) and some even want LAMP stacks (Linux, Apache, MySQL and PHP).

In order for a portal — the UI integration layer for the enterprise — to be successful in the heterogeneous IT world in which we live, it must run on all of those platforms and it must have a strategy for supporting integration with every platform. It’s clear from their product direction, including their recent decision to support Linux, that Plumtree has known this for a long time. I can’t speak for BEA, but the message I’ve been getting from them for the past several years is that you can solve all the world’s problems — or least all the world’s IT problems — with Java. As much as I like Java, I’ve never quite bought into that vision. The IT world is just too heterogeneous for that vision to approach reality.

I sincerely hope that BEA sticks to this new strategy of supporting the Microsoft stack for Plumtree. The good news is that Weblogic has always run on Windows. Running Java on Windows is fine in my book, but if you tell that to the approximately 500 Plumtree customers who run Plumtree on a Microsoft stack, they’re not going to be pleased. In fact, I think they’ll start looking for another solution, perhaps even Sharepoint.

What will happen to BEA’s Portal product?

The press releases are calling BEA’s portal product a transactional portal for the extranet and Plumtree’s a collaborative portal for the intranet. This is nothing more than an attempt to downplay the competitive nature of the two products. This spin isn’t working for me. bdg has built transactional extranets using Plumtree and I’m sure that enterprises have built collaborative intranets using the BEA Portal. In fact, BEA specifically pitches the collaborative features of their portal product as part of their marketing literature.

Obviously the companies need to make a statement saying that they’re going to support both camps in order to avoid massive customer hemorrhaging. (Look what happened to Epicentric’s customers when they were acquired by Vignette.) It’s good to hear that the near-term plan supports both portal products for the sake of the customers, but I hope to hear some more believable strategic direction from BEA and Plumtree about their clearly competitive portal offerings.

What would make sense to me would be a hybrid that includes most of Plumtree’s compelling out-of-the-box functionality — including collaboration, content management and usage tracking — and merge it with the compelling parts of BEA’s portal, such as the Portal Java Controls and the Portal Resources Designer. Development tools like these will greatly enhance Plumtree’s Java developer offerings to bring them up to speed with their Microsoft offerings (like the EDK’s .NET Web Controls). But there are some big architectural decisions to make. For example, is it better to integrate BEA’s Designer with the Plumtree EDK to help those of us building Java portlets, or should they take an IDE plug-in approach for Eclipse like Plumtree did with the .NET IDE?

The industry press is still beating up BEA for having a Java client portal designer instead of a web-based one just like they beat up Plumtree four years ago because of their Windows-based portal designer (called Content Manager). The answer is simple: BEA needs to webify their portal designer. But if they’re going to live by their new strategy of cross-platform support, anything they build will need to have a .NET equivalent.

This may be cynical, but I think telling all the developers who currently support Weblogic Portal that they’re going to have start thinking about portability to .NET is going to be a hard sell.

How will the merger affect the ship date for Plumtree G6?

Plumtree has set a ship date for G6, the next generation of their portal product. The product is currently in Beta, so we all know that we’re getting close.

The press releases and FAQ do not mention G6 or say anything about the next version of BEA’s portal. If any kind of tangible BEA Portal/Plumtree Portal integration attempts are squeezed into G6, I doubt that they will hit their ship date.

I think it would be a smart move to ship G6 as is — and there’s a good chance that it will happen, given the fact that Plumtree will be a separate business unit at BEA — and then shoot for integration in the next major product iteration, whenever that is.

I hope to hear some clear direction from the two companies on this soon because our customers’ rollout plans are directly affected by information like this.

Will this deal make BEA even more of an acquisition target for Oracle?

Everyone I know — myself included — had a feeling that Plumtree would be acquired some day. But the major questions were 1) when and 2) by whom? Quite some time ago and long before Plumtree had its Java strategy fleshed out, there were rumors of a Microsoft takeover. Then Siebel. Then Peoplesoft. But BEA? I never would have guessed.

I personally thought Oracle would be the suitor, especially after they acquired Oblix, PeopleSoft and J.D. Edwards. After extending its tentacles into almost every enterprise software market (and proving tremendously incapable of producing any decent software applications other than a database), Oracle snapped up ERP, HR and SSO/Identity Management in the blink of an eye. It seemed reasonable to me that a good portal product that could integrate with all those applications would be a clear next target. Oracle’s portal certainly doesn’t cut the mustard. In fact, they often offer it up for free only to be beaten out by Plumtree, which is, ahem, a far cry from free.

Now the next pressing question: is Oracle even more likely to acquire Plumtree now that they’re a part of BEA? Now they’d get an excellent application server and a cross-platform, industry-leading portal. You know it crossed Larry Ellison’s mind when he heard the news. Food for thought.

What will happen to the name Plumtree?

Back in late 1998, when BEA acquired WebLogic, Inc., they kept the company’s preexisting market share and mind share intact by transitioning the name of the company into the name of what has become BEA’s flagship product. Oracle has done the same with its recent acquisitions.

BEA would be wise to do the same with Plumtree. “BEA Plumtree Portal” may not have a ring to it right now — but mark my words — it is soon to become a household name in the world of enterprise software.

* * *

For all of you who asked, those are my thoughts on the merger. Sorry it took me almost a week to come up with a response to your questions, but if you recall from an earlier post, I was teaching a Plumtree training class all last week. Anyone who has taught training knows how exhausting that is, hence the delay in putting my thoughts on (virtual) paper.

As always, your comments are most welcome.