Categories
Featured Posts Personal

Nobody’s Gonna Read This (and Why That Makes Me Happy)

Boy do I love the fact that no one reads this blog. And to the few people who are exceptions to that general rule — thank you for being so supportive!

I just hit two or three web pages in a row (TechCrunch, Digg and the Meebo blog) wherein each post I read had 80+ comments that reminded me why I rarely ever actually read comments.

Haters, trolls, flamers, spammers — whatever you want to call them, the internet is ridden with people who are filled with spite and rage. The funny thing is that in no other forum (except for perhaps while driving) are people this cruel to one another. It’s just not socially acceptable.

I realize that e-hate isn’t a new problem: in fact, it dates back to the early days of UseNet, Netiquette and the ol’ “do we allow AOLer’s on the internet” debate. While doing some fact-checking on wikipedia, I was really amused to read about Godwin’s Law, which sums up what I’m talking about better than I ever could: “As a Usenet discussion grows longer, the probability of a comparison involving Nazis or Hitler approaches one.”

We all know the Kathy Sierra story. I’m glad she had a thick enough skin to re-emerge in the blogging world and on Twitter because the world is a better place with her contributions than it is without them.

We all remember The Great Sarah Lacy Twitter Massacre of SXSW 2008. I recently met Sarah at a tech event in DC and, believe it or not, she doesn’t have horns, literally or figuratively.

Jason Calacanis recently “retired” from blogging. When I read his post, I immediately thought that it was just a PR stunt, but I’m beginning to realize that I can sympathize with his viewpoint. I really don’t want to ever be an A-list blogger or “internet famous” because it’s just like painting a big target on your own ass.

I love my family and close friends, I love the physical neighborhood in which I live and I love the virtual networks that have developed around my career and my passions for the past 15 years or so that I’ve been using the internet.

But honestly, a big part of me doesn’t want anyone else to read this. Not because I don’t take criticism well. (I don’t, but then again nobody does.) I just wish some of the same general rules that apply to social interactions — at say, a cocktail party, a baseball game or at the supermarket — would apply to the internet.

Comments welcome. Just be nice, ok?

Categories
bdg dev2dev Featured Posts Plumtree • BEA AquaLogic Interaction • Oracle WebCenter Interaction

Write an ALUI IDS in Under 15 Lines Using Ruby on Rails

Not only is it possible to write an ALUI Identity Service in Ruby on Rails, it’s remarkably easy. I was able to do the entire authentication part in fewer than 15 lines of code! However, I ran into problems on the synchronization side and ended up writing that part in Java. Read on for all the gory details.

As part of building the suite of social applications for BEA Participate 2008, we’re designing a social application framework in Ruby on Rails and integrating it with ALI 6.5. Not being a big fan of LDAP, I decided to put the users of the social application framework in the database (which is MySQL). Now, when we integrate with ALI, we need to sync this user repository (just as many enterprises do with Active Directory or LDAP).

So I set out to build an IDS to pull in users, groups and memberships in Ruby on Rails.

It’s pretty obvious that Ruby on Rails favors REST over SOAP for their web service support. However, they still support SOAP for interoperability and it mostly works. I did have to make one patch to Ruby’s core XML processing libraries to get things humming along. I haven’t submitted the patch back to Ruby yet, but at some point I will. Basically, the problem was that the parser didn’t recognize the UTF-8 encoding if it was enclosed in quotes (“UTF-8”). This patch suggestion guided me in the right direction, but I ended up doing something a little different because the suggested patch didn’t work.

I changed line 27 of lib/ruby/1.8/rexml/encoding.rb as follows:

 enc = enc.nil? ? nil : enc.upcase.gsub('"','') #that's a double quote inside single quotes

Now that Ruby’s XML parser recognized UTF-8 as a valid format, it decided that it didn’t support UTF-8! To work around this, I installed iconv, which is available for Windows and *nix and works seamlessly with Ruby. In fact, after installation, all the XML parsing issues went bye-bye.

Now, on to the IDS code. From your rails project, type:

ruby script/generate web_service Authenticate

This creates app/apis/authenticate_api.rb. In that file, place the following lines of code:

class AuthenticateApi < ActionWebService::API::Base
 api_method :Authenticate, :expects => [{:Username =>
:string}, {:Password =>
:string}, {:NameValuePairs =>
[:string]}], :returns =>
[:string]
end

All you’re doing here is extending ActionWebService and declaring the input/output params for your web service. Now type the following command:

ruby script/generate controller Authenticate

This creates the controller, where, if you stick with direct dispatching (which I recommend), you’ll be doing all the heavy lifting. (And there isn’t much.) This file should contain the following:

class AuthenticateController < ApplicationController
 web_service_dispatching_mode :direct
 wsdl_service_name 'Authenticate'
 web_service_scaffold :invoke

 def Authenticate(username, password, nameValuePairs)
   if User.authenticate(username, password)
     return ""
   else
     raise "-102" #generic username/password failure code
   end
 end
end

Replace User.authenticate with whatever mechanism you’re using to authenticate your users. (I’m using the login_generator gem.) That’s all there is to it! Just point your AWS to http://localhost:3000/authenticate/api and you’re off to the races.

Now, if you want to do some functional testing (independently of the portal), rails sets up a nice web service scaffold UI to let you invoke your web service and examine the result. Just visit http://localhost:3000/authenticate/invoke to see all of that tasty goodness.

There you have it — a Ruby on Rails-based IDS for ALUI in fewer than 15 lines of code!

The synchronization side of the IDS was almost just as simple to write, but after countless hours of debugging, I gave up on it and re-wrote it in Java using the supported ALUI IDK. Although I never could quite put my finger on it, it seemed the problem had something to do with some subtleties about how BEA’s XML parser was handing UTF-8 newlines. I’ll post the code here just in case anyone has an interest in trying to get it to work. Caveat: this code is untested and currently it fails on the call to GetGroups because of the aforementioned problems.

In app/apis/synchronize_api.rb:

class SynchronizeApi < ActionWebService::API::Base
 api_method :Initialize, :expects =>
[{:NameValuePairs =>
[:string]}], :returns =>
[:integer]
 api_method :GetGroups, :returns =>
[[:string]]
 api_method :GetUsers, :returns =>
[[:string]]
 api_method :GetMembers, :expects =>
[{:GroupID => :string}], :returns =>
[[:string]]
 api_method :Shutdown
end

In app/controllers/synchronize_controller.rb:

class SynchronizeController < ApplicationController
  web_service_dispatching_mode :direct
  wsdl_service_name 'Synchronize'
  web_service_scaffold :invoke

  def Initialize(nameValuePairs)
    session['initialized'] = true
    return 2
  end

  def GetGroups()
    if session['initialized']
      session['initialized'] = false
      groups = Group.find_all
      
      groupNames = Array.new
      for group in groups
        groupNames << "<SecureObject Name=\"#{group.name}\" AuthName=\"#{group.name}\" UniqueName=\"#{group.id}\"/>" 
      end 
      return groupNames
    else
      return nil
    end
  end
  
  def GetUsers()
    if session['initialized']
      session['initialized'] = false
      users = User.find_all
      
      userNames = Array.new
      for user in users
        userNames << "<SecureObject Name=\"#{user.login}\" AuthName=\"#{user.login}\" UniqueName=\"#{user.id}\"/>" 
      end
      
      return userNames
    else
      return nil
    end
  end

  def Shutdown()
    return nil
  end
end

Comments

Comments are listed in date ascending order (oldest first)

  • Nice post, Chris. This is the first time I’ve seen this done!

    Posted by: dmeyer on January 20, 2008 at 4:16 PM

  • Thank you, David.I just noticed that part of my sync code was chomped off in the blog post because WordPress was assuming that was actually an opening HTML/XML tag. I made the correction so the above code now accurately reflects what I was testing.

    Posted by: bucchere on January 21, 2008 at 1:16 PM

Categories
bdg dev2dev Featured Posts Plumtree • BEA AquaLogic Interaction • Oracle WebCenter Interaction

The Enterprise Relevance of Web 2.0

Register Now!

The concepts behind Web 2.0, social networks, and collaboration are now poised to transform your enterprise, providing solutions such as collaborative mashups, expertise discovery and social search to enhance your existing portal.

According to Gartner, Web 2.0 will have a major impact on a broad range of traditional enterprises. Gartner states that “positive business model change will result in unexpected ways, and enterprises must prepare for this transition.”

Register to attend this exciting seminar on Wednesday, November 14th, 6:00 pm and hear how BEA’s three new products will “two-dot-oh” your company’s Web along with other topics that include:

  • How Web 2.0 can bring true value to your business
  • How to differentiate between Web 2.0 and Enterprise 2.0
  • How to implement new Web 2.0 concepts like blogging, wikis, tagging and social networking into your business and allow IT governance and control
  • How to enhance your existing portal infrastructure

Enjoy free hors d’oeuvres and an open bar along with presentations that define Web 2.0 and show how BEA’s new social computing products Pages, Ensemble and Pathways can deliver true business value from Web 2.0 and bdg’s newest products that bridge the gap between Web 2.0 and the enterprise.

Attendance is limited, so please take a moment to register now. I look forward to meeting you at the event.

Date: Wednesday, November 14th
Time: 6:00 p.m. – 8:00 p.m.
Location: Marriott Tyson’s Corner

8028 Leesburg Pike
Vienna, VA, 22182
(703) 734-3200

Directions

Register Now!

Comments

Comments are listed in date ascending order (oldest first)

  • I’m sorry I missed this! If you have a notification list for events like these please include me, I’d love to hear about future events you guys sponsor. [email protected] Thanks!

    Posted by: geoffgarcia on January 17, 2008 at 1:33 PM

  • Hi Geoff! The event was down here in Tyson’s Corner, VA, so we focused on local attendees. I’ll make sure to include you next time, even though if my memory serves me correctly, you’re up in NY.

    Posted by: bucchere on January 17, 2008 at 6:47 PM

  • Oh, I almost forgot. If I can find the time, I’ll put together a video podcast of the event. I have the footage; I just haven’t had the time to do the editing. 🙁

    Posted by: bucchere on January 17, 2008 at 6:48 PM

Categories
bdg Business dev2dev Featured Posts Plumtree • BEA AquaLogic Interaction • Oracle WebCenter Interaction

Predictions: Will Oracle Acquire BEA?

There’s been a lot of speculation in response to some press releases from Oracle that an all-cash buyout of BEA may be immanent. More than two years ago, I made an entry on my company’s blog that said, effectively, that by acquiring Plumtree, BEA painted a target on itself to be acquired by Oracle. Here’s the snippet from my other blog dated August 28, 2005:

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.

I also said that BEA would keep the name Plumtree and lo-and-behold, they changed it to AquaLogic. So I wasn’t 100% right, but at least I can say that I called this one.

Comments

Comments are listed in date ascending order (oldest first)

  • Someone just walked into my office and said, “Hey, since BEA already has a dual portal strategy (ALI and WLP), what will happen if they get acquired by Oracle, which already has their own portal product?”

    Two years ago, I predicted a merging of WLP and ALI, with the result being much like ALI with the great developer tools you get from WLP and workshop tacked on to it. Obviously that’s not exactly how things played out.

    So my prediction this time is that all three portals will “seamlessly” co-exist under one roof, giving consumers plenty of ways to portalize all under the Oracle name. We’ll call it the Portal Trifecta — w00t!

    Posted by: bucchere on October 12, 2007 at 10:40 AM

  • Oracle is going to support SqlServer 2000 & 2005 for Aqualogic? And support .NET? Interesting if they would sell the Aqualogic piece of to to Microsoft. Give MOS a better external portal….?

    Posted by: vivekvp on October 12, 2007 at 11:37 AM

  • Great question, Vivek. I was surprised to see BEA pledge support for ALUI on .NET and SQL Server. I’ll be even more surprised to see that happen over at Oracle. Remember though, Oracle runs on Windows!

    Posted by: bucchere on October 12, 2007 at 12:08 PM

  • Chris, don’t you mean 4 portal products; ALUI, WLP, Oracle Portal, and WebCenter? The merger makes a lot of sense from my view point, but in all seriousness the one area which will need a lot of help is Portal. IBM has only one WebSphere Portal code base.

    Posted by: Dr. BEA Good on October 16, 2007 at 9:33 PM

  • It’s hard to image that a company maintains three or four full-featured portal products, even a giant like IBM, Oracle or MS.

    Posted by: caiwenliang on October 17, 2007 at 5:16 AM

  • Four portals? Yikes! I just don’t want confused consumers to go off and buy Sharepoint or WebSphere portal when I think ALUI and WLP are superior products.

    Posted by: bucchere on October 18, 2007 at 2:11 PM

Categories
Featured Posts Software Development

Say hello world to comet

A couple of weekends ago I inflicted upon myself a quest to discover what all the buzz was about regarding Comet. What I discovered is that there is quite a bit of code out there to help you get started but the documentation around that code, and about Comet in general, is severely lacking. All I really wanted to find was a Comet-based Hello World, which as any developer knows, is the cornerstone of any programming language or methodology.

Since I couldn’t find one on Google, I ascertained that no Hello World exists for Comet and therefore I took it upon myself to write one.

For those of you who are new to Comet, the first thing you should do is read Alex Russell’s seminal blog post on the topic. At its core, Comet is really just a message bus for browser clients. In one browser, you can subscribe to a message and in another you can publish a message. When a message gets published, every browser that’s subscribed (almost) instantaneously receives it.

What? I thought clients (browsers) had to initiate communication per the HTTP spec. How does this work?

Under the covers, Comet implementations use a little-known feature of some web server implementations called continuations (or hanging gets). I won’t go into details here, but at a high level, a continuation initiates from the browser (as all HTTP requests must do) and then, when it’s received by the server, the thread handling it basically goes to sleep until it gets a message or times out. When it times out, it wakes up and sends a response back to the browser asking for a new request. When the thread on the server receives a message, it wakes up and sends the message payload sent back to the browser (which also implies that it’s time to send a new request). Via this mechanism, HTTP is more or less “inverted” so that the server is essentially messaging the client instead of vice-versa.

A few questions immediately pop into mind, so let’s just deal with them right now:

Why is this better than Ajax alone?

It boils down to latency and users’ tolerance for it. In the worst case, traditional web applications force entire page refreshes. Ajax applications are a little better, because they can refresh smaller parts of a page in response to users’ actions, but the upshot is that the users are still waiting for responses, right? A Comet-driven application has essentially removed the user from the picture. Instead of the user asking for fresh data, the server just sends it along as soon as it changes, given the application more of a “realtime” feel and removing virtually all perceived latency.

So are we back to client server again?

Sort of. Comet gives you the benefit of server-to-client messaging without the deployment issues associated with fat clients.

Can’t applets do this?

Of course they can. But who wants to download an applet when some lightweight Javascript will do the trick?

Why the name Comet?

Well, clearly it’s a pun on Ajax. But it’s not the only name for this sort of technology. There’s something out there called pushlets which claims to do the same thing as Comet, but which didn’t seem to catch on, I guess.

Back to the whole point of this post: my hello world. I pieced this example together using dojo.io.cometd and a recent version of Tomcat that into which I dropped the relevant parts of Jetty to provide support for continuations.

It’s finally time to say “hello world” to my hello world.

First off, download one of the more recent dojo builds that contains support for dojo.io.cometd. Drop dojo.js on your Java-based web/application server. (I used Tomcat, but you can use JBoss, Jetty, Weblogic, Websphere or any other web server with support for servlets.) Add this page in the root of your application:

<script src="js/dojo.js" type="text/javascript"></script>
<script type="text/javascript">//<![CDATA[
  dojo.require("dojo.io.cometd");
  cometd.init({}, "cometd");
  cometd.subscribe("/hello/world", false, "publishHandler");
  publishHandler = function(msg) { alert(msg.data.test); }
// ]]></script>
<input type="button" value="Click Me!" />

Without a cometd-enabled web server behind it, the above page does absolutely nothing.

So, to make this work, I needed to find a Java-based web/application server with support for continuations. I’m sure there are many ways to skin this cat, but I picked Jetty. You can get Jetty source and binaries if you’d like to follow along. Since all of our customers who embrace open source are lightyears more comfortable with Tomcat than they are with any other open source web/application server (ahem . . . Jetty), I decided to embed Jetty in Tomcat rather than run everything on Jetty alone. It’s all just Java, right?

Here I ran into a few snags. The maven build file for Jetty didn’t work for me, so I dropped everything in org.mortbay.cometd and org.mortbay.cometd.filter into my Eclipse project and just integrated it with the ant build.xml I was already using to build my web application. Here’s the relevant portion of my build.xml:

<javac srcdir="${srcdir}" destdir="${classdir}" debug="true" debuglevel="lines,vars,source">
<classpath>
<pathelement location="${jetty.home}/lib/jetty-util-6.0.1.jar"/>
<pathelement location="${jetty.home}/lib/servlet-api-2.5-6.0.1.jar"/>
</classpath>
</javac>

Once Jetty was essentially hacked into Tomcat, the rest was smooth sailing. I just wrote a JSP that dropped a “goodbye world” message onto the same old queue that I used in the last example, but I did so using server-side code. Here’s the JSP:

<%@page import="org.mortbay.cometd.*"%>
<%@page import="java.util.*"%>
<%
Bayeux b = (Bayeux)getServletContext().getAttribute(CometdServlet.ORG_MORTBAY_BAYEUX);
Channel c = b.getChannel("/hello/world");
Map message = new HashMap();
message.put("test", "goodbye world");
c.publish(message, b.newClient());
%>

This page does not produce any output of its own; rather, it just drops the “goodbye world” message on the queue. When you hit this page in a browser, any other browser listening to the /hello/world queue will get the message. The above JSP, along with the dojo page you created in the first step, should be enough to wire together two different flavors of Comet messaging: browser to server to browser and just plain old server to browser.

I’m curious 1) if this was helpful and 2) if you’d like to share what you’re doing with Comet with me (and please don’t say cleaning your kitchen).

Categories
bdg Featured Posts Plumtree • BEA AquaLogic Interaction • Oracle WebCenter Interaction

BEAWorld 2006 Speaking Engagement

This just in: my talk on ALUI Taglibs begins at 1:50 PM on Monday the 18th at the ALUI Developer User Group Meeting, which will be held in one of the rooms on the 120 block of Moscone Center. See you there! (Be sure to come up and introduce yourself.)

Categories
bdg dev2dev Featured Posts Plumtree • BEA AquaLogic Interaction • Oracle WebCenter Interaction

UUID Object Opener, The Coolest ALI Taglib Yet

Anyone who’s ever done a major Plumtree/ALUI deployment knows of this problem: You create a portlet or community (or any other object) in Dev and then you migrate it to Test and on to Production. The problem is that you’ve also written some code in your navigation portlet or in another portlet that depends on an ObjectID (e.g. you’ve used a pt:standard:opener tag) and now, in each environment, your ObjectID has changed and you’re basically hosed.

Pre-G6, I came up with a solution described (somewhat hastily) in this post, but it requires a lot of leg work and — worse yet — manual configuration in each environment.

Enter G6 and the magic of taglibs. (Am I beginning to sound like a broken record? Yes, I know, you can’t fix every problem with a taglib, just 95% of them, right?) With this new taglib I wrote today, I extend AOpenerLinkTag and simply convert a UUID to an ObjectID and ClassID so that you can use the same taglib invocation in every environment. I don’t want to toot my own horn too much here, but honestly, this is pretty much the most useful taglib I’ve ever encountered, and once again, it took under 30 minutes to write.

Before I dive into the source, let me back up and say that I had to bend the rules a bit. OOTB, there are two subclasses of ATagAttribute: RequiredTagAttribute and OptionalTagAttribute. I added a third: MutableTagAttribute. It looks and smells like a tag attribute, but under the covers it’s not. Instead of grabbing its value out of the tag invocation, it allows you to set/change the value at runtime inside the taglib code. Granted, this is a little weird, but it’s what I needed to do in order to subclass AOpenerLinkTag and keep it happy dappy.

MutableTagAttribute.java:

package com.bdgportal.alui.taglibs;

import com.plumtree.portaluiinfrastructure.tags.metadata.*;

public class MutableTagAttribute extends ATagAttribute {

  private String value;
  
  public MutableTagAttribute(String name, String desc, AttributeType type) {
    super(name, desc, type);
  }
  
  public String GetDefaultValue() {
    return value;
  }

  public void SetDefaultValue(String value) {
    this.value = value;
  }
  
  public boolean GetIsRequired() {
    return false;
  }
}

Now that we have a tag attribute that we can change on-the-fly, writing the taglib was a snap.

UUIDObjectOpener.java:

package com.bdgportal.alui.taglibs;

import com.plumtree.portaluiinfrastructure.tags.*;
import com.plumtree.portaluiinfrastructure.tags.metadata.*;
import com.plumtree.xpshared.htmlelements.*;
import com.plumtree.taglib.standard.basetags.*;
import com.plumtree.server.*;

public class UUIDObjectOpener extends AOpenerLinkTag
{
  public static final RequiredTagAttribute UUID;
  private MutableTagAttribute OBJECT_ID;
  private MutableTagAttribute CLASS_ID;


  public UUIDObjectOpener() {
    OBJECT_ID = new MutableTagAttribute("objectid", "Not used -- do not set a value for this!", AttributeType.INT);
    CLASS_ID = new MutableTagAttribute("classid", "Not used -- do not set a value for this!", AttributeType.INT);
  }

  public ATagAttribute GetObjectIDAttribute()
  {
    return OBJECT_ID;
  }

  public ATagAttribute GetClassIDAttribute()
  {
    return CLASS_ID;
  }

  public static final ITagMetaData TAG;

  static
  {
    TAG = new TagMetaData("uuidobjectopener", "Opens an object based on its UUID.");
    UUID = new RequiredTagAttribute("uuid", "The UUID for the object you want to open.", AttributeType.STRING);
  }

  public HTMLElement DisplayTag()
  {
    Object[] objectAndClassId = ((IPTMigrationManager)(((IPTSession)GetEnvironment().GetUserSession()).OpenGlobalObject(PT_GLOBALOBJECTS.PT_GLOBAL_MIGRATION_MANAGER,
          false))).UUIDToObjectID(GetTagAttributeAsString(UUID));
  
OBJECT_ID.SetDefaultValue(objectAndClassId[PT_MIGRATION_OBJECT_COLS.PT_MOC_OBJECTID].toString());
    CLASS_ID.SetDefaultValue(objectAndClassId[PT_MIGRATION_OBJECT_COLS.PT_MOC_CLASSID].toString());
    return super.DisplayTag();
  }

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

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.uuidobjectopener pt:uuid="{00000-0000-0000-000000}" pt:mode="2">Open My
   Object</pt:mytablibns.uuidobjectopener>
</span>

I did actually test this taglib and it worked swimmingly. Of course you need to substitute a real UUID for all those Os.

In closing, here’s a little shameless plug: I’ve been asked by BEA to give a short, 20-minute talk at BEA World on my favorite subject (duh, taglibs) at the ALUI Developer User Group on Monday, September 18th in Moscone Center, San Francisco. It will happen some time between 1 and 5:30 PM. The ALUI User Groups are free for conference attendees. I hope to see you there or at the bdg booth. Please come on up and introduce yourself — I always like to meet members of this great community in person.

Enjoy!

Comments

Comments are listed in date ascending order (oldest first)

  • Will there be any performance issues using this tag as it involves additional operations of getting Object ID and Class ID from the UUID?

    Posted by: psudhir_it on February 6, 2007 at 10:15 PM

  • From what I can tell, the tag makes a single SQL query (something like select objectid, classid from ptmigration where uuid = ?) which should be a pretty darn fast query, especially since there’s probably an index on uuid.

    The portal is making database calls left and right when you’re displaying a portal page, so making one more database call to generate an opener link shouldn’t really be a performance factor. Nonetheless, it’s definitely something to think about and I’m glad you brought it up.

    Posted by: bucchere on February 7, 2007 at 5:53 PM

  • Hi Chris! Am attempting to move this over to .NET; can you tell me which reference I need to add to resolve com.plumtree.taglib.standard and the AOpenerLinkTag? I’m not sure how to convert this Java fragment, which appears to have two seperate definitions of TAG: public static final ITagMetaData TAG; static { TAG = new TagMetaData(“uuidobjectopener”, “… UUID.”); …can you tell me what it means, and any tips on converting to C# ? Should have an opportunity to throw some load at this later on; will post my results here. My customer is already sensitive to performance problems caused by header portlets making DB calls; so I will also be looking into the caching possibilities. Cheers, Rob

    Posted by: rwagner on October 10, 2007 at 11:04 AM

  • Here is another option. The little known server.pt?uuID={XYZ-UUID} syntax. We use this in our public site which is not gatewayed to deep link into portal content without the need for an adaptive tag. We also use this to establish fqdns in apache that redirect to portal pages. For example in apache setup a fqdn of docs.bea.com which points to portal.bea.com/portal/server.pt?uuID={XYZ-UUID}.

    Posted by: ryanyoder on February 11, 2008 at 6:18 AM

  • Wow, very cool! I totally didn’t know that syntax even existed. If it’s supported, it ought to be documented, because it’s quite handy.

    One gotcha is that you need to pass mode=2 if you want to open the object in view mode because the default is edit mode, e.g.: /portal/server.pt?uuID={46514C0F-0187-4340-AA24-84E41C00C60F}&mode=2

    Posted by: bucchere on February 11, 2008 at 6:31 AM

Categories
bdg dev2dev Featured Posts Plumtree • BEA AquaLogic Interaction • Oracle WebCenter Interaction

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

Categories
dev2dev Featured Posts Plumtree • BEA AquaLogic Interaction • Oracle WebCenter Interaction

A Slick Alternative to pt:standard.openerlink

For one reason or another your bosses (we all have more than one, don’t we?) have told you that the look-n-feel of the common object opener in ALUI just doesn’t cut it. Even though it’s powerful, scalable and pretty nice-looking and it includes a myriad of options (e.g search, browse, single vs. multi-select, set previously selected, etc.), they just want something different. Perhaps they don’t want a pop-up window. Perhaps they don’t like how many clicks it takes to get down to an object. Perhaps they’re just being difficult.

Regardless, you’ve been asked to come up with a clean, fast, in-place object selector that still shows a hierarchical view. (For the purposes of this discussion, I’m going to use the example of communities from here on out instead of just talking about “objects.”) So naturally, as a portlet developer, you turn to the IDK. Unfortunately, if you want to get portal metadata, you have to use the PRC/SOAP server. There goes fast. So maybe you can write it in native code or using database calls. There goes clean.

Your best bet here — and really the only good way to accomplish this — is to develop a custom taglib. Custom taglibs are quickly becoming my favorite new feature in G6. (BTW, if you aren’t on G6, upgrade ASAP — it’s worth the effort.) So, for your benefit, I decided to try my hand at writing a taglib to present a nice hierarchy of communities. Here’s what I discovered.

First off, let’s talk about the HTML I want to display in my custom tag for a moment. Whoever came up with the concept of select boxes and optgroup elements was a complete goofball. Why develop something that’s naturally suited for a hierarchy and then limit the hierarchy to a depth of one?

Here’s an example:

So I had to throw my initial idea of using nested option elements out the window simply because you can’t nest an optgroup within an option. Bummer.

So here’s the display I settled on:

There’s still a hierarchy here, it’s just flattened and there’s essentially a “breadcrumb” for each community. In the example I have, bdg is a top level community and services is a subcommunity of bdg. Consulting, development, integration and training are all subcommunities of services.

Alrighty then, so how to you construct this nice select box? And BTW, make it easy, clean and fast. Here you go:

package com.bdgportal.alui.taglib

import com.plumtree.openlog.OpenLogService;
import com.plumtree.openlog.OpenLogger;
import com.plumtree.portaluiinfrastructure.tags.*;
import com.plumtree.portaluiinfrastructure.tags.metadata.*;
import com.plumtree.xpshared.htmlelements.*;
import com.plumtree.server.*;

public class CommunitySelector extends ATag
{
   private static OpenLogger log = OpenLogService.GetLogger(
    OpenLogService.GetComponent("UI_Infrastructure"),
    "com.bdgportal.alui.taglib.CommunitySelector");

 public static final ITagMetaData TAG;
 public static final RequiredTagAttribute SELECT_ID;
 public static final RequiredTagAttribute SELECT_NAME;
 public static final OptionalTagAttribute SELECT_CLASS;
 public static final RequiredTagAttribute ROOT_FOLDER_ID;

 static
 {
  TAG = new TagMetaData("communityselector",
    "Displays a community selector.");

  SELECT_ID = new RequiredTagAttribute("id",
    "The id of the select box.",
    AttributeType.STRING);

  SELECT_NAME = new RequiredTagAttribute("name",
    "The name of the select box.",
    AttributeType.STRING);
 
  ROOT_FOLDER_ID = new RequiredTagAttribute("rootfolderid",
    "The root folder. All communities in this folder and below " +
    "will be displayed.",
    AttributeType.INT);

  SELECT_CLASS = new OptionalTagAttribute("class",
    "The CSS class of the select box.",
    AttributeType.STRING, "objectText");
 }

 public HTMLElement DisplayTag()
 {
  HTMLSelect comms = new HTMLSelect(
    GetTagAttributeAsString(SELECT_NAME),
    GetTagAttributeAsString(SELECT_ID));  

  comms.SetStyleClass(GetTagAttributeAsString(SELECT_CLASS));
  recursiveAddComms(((IPTSession)GetEnvironment().GetUserSession())
   .GetCommunities(), ((IPTSession)GetEnvironment()
   .GetUserSession()).GetAdminCatalog(), comms,
   GetTagAttributeAsInt(ROOT_FOLDER_ID), "");
 
  return comms;
 }

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

 public TagType GetTagType() {
  return TagType.NO_BODY;
 }
 
 private void recursiveAddComms(IPTObjectManager commObjMgr,
  IPTAdminCatalog adminCatalog, HTMLSelect comms,
  int folderId, String prefix) {
 
  //CAB: add the communities at this level, if any
  IPTQueryResult commsToAdd = commObjMgr.SimpleQuery(folderId,
   PT_PROPIDS.PT_PROPID_NAME);
  for (int i = 0; i < commsToAdd.RowCount(); ++i) {
   comms.AddOption(new HTMLOption(
    Integer.toString(commsToAdd.ItemAsInt(i, PT_PROPIDS.PT_PROPID_OBJECTID)),
    prefix.substring(0, prefix.length() - 3)));
  }
 
  IPTAdminFolder adminFolder = adminCatalog.OpenAdminFolder(folderId, false);
  if (0 == adminFolder.QuerySubfoldersCount()) {
   return; //CAB: base case
  } else {
   IPTQueryResult subFolders = adminFolder.QuerySubfolders(
    PT_PROPIDS.PT_PROPID_OBJECTID + PT_PROPIDS.PT_PROPID_NAME
    + PT_PROPIDS.PT_PROPID_FOLDER_FOLDERTYPE,
    0,
    PT_PROPIDS.PT_PROPID_NAME,
    0,
    -1,
    null);
 
   //CAB: recurse into each subfolder
   for (int i = 0; i < subFolders.RowCount(); ++i) {
    recursiveAddComms(commObjMgr, adminCatalog, comms,
     subFolders.ItemAsInt(i, PT_PROPIDS.PT_PROPID_OBJECTID),
     prefix + subFolders.ItemAsString(i, PT_PROPIDS.PT_PROPID_NAME)
     + " : ");
   }
  }
 }
}

I think the code pretty much speaks for itself, but if you want further explanation, let me know by posting a comment.

Comments

Comments are listed in date ascending order (oldest first)

  • Iiiinteresting. This is very cool, Chris. I might be forced to highjack this and turn it into a pt:data tag 🙂 You have any thoughts on how / where you might approach caching with this? Have you seen the EOD sample tag?

    Posted by: ewwhitley on August 31, 2006 at 9:41 AM

  • Hmmm . . . caching. It’s so fast OOTB that I didn’t think about caching it. 🙂 Plus, I used it on a project where we have fewer than 100 communities, so I didn’t have any problems. I mean, we’re not using the PRC, right? I guess if you wanted to cache it you could doink around with the shared variables in the tag library (session scope), but you’ve got to worry about clearing the cache after a fixed interval of time.

    Posted by: bucchere on August 31, 2006 at 10:31 AM

  • Awesome! Thanks Chris. I just spent about 2 hours last night trying to do almost the exact same thing. This is great!

    Posted by: jturmelle on May 17, 2007 at 7:48 AM

Categories
dev2dev Featured Posts Plumtree • BEA AquaLogic Interaction • Oracle WebCenter Interaction

Searching Intrinsic ALI Properties Using the PRC

There’s a problem with the IDK PRC API for search that’s tripped up users in the dev2dev forums and that stymied me for the first time today while coding up a custom search application for one of our customers.

The problem is that there’s a hardcoded limitation in the IDK that prevents you from calling PortalField.forID if the passed in object ID is less than 100. This prevents you from searching on some really useful properties, including e-mail address! For the life of me, I can’t figure out why this limitation was imposed.

The good news is that I found a workaround. It involves a quick two-file IDK patch that entails subclassing two classes. The only catch is that you need to put the child classes in the same package as the IDK (because the parent classes have package-private constructors).

Here’s the source code that does the trick.

com.plumtree.remote.prc.search.IntrinsicPortalField.java:

package com.plumtree.remote.prc.search;

import com.plumtree.remote.prc.search.PortalField;
import com.plumtree.remote.prc.search.xp.*;

public class IntrinsicPortalField extends PortalField {
  private IntrinsicPortalField(IntrinsicXPPortalField xpField) {
    super(xpField);
  }

  public static final IntrinsicPortalField EMAIL_ADDRESS;

  static {
    EMAIL_ADDRESS = new IntrinsicPortalField(IntrinsicXPPortalField.forID(26));
  }
}

com.plumtree.remote.prc.search.xp.IntrinsicXPPortalField.java:

package com.plumtree.remote.prc.search.xp;

import com.plumtree.openfoundation.util.XPIllegalArgumentException;

public class IntrinsicXPPortalField extends XPPortalField {

  private IntrinsicXPPortalField(String name, boolean isSearchable, boolean isRetrievable) {
    super(name, isSearchable, isRetrievable);
  }

  public static IntrinsicXPPortalField forID(int propertyId) throws XPIllegalArgumentException {
    return new IntrinsicXPPortalField("ptportal.propertyid." + propertyId, true, true);
  }
}

I used e-mail address (ID = 26) as an example, but you can put any properties in there that you want. Then, when you’re setting up your search filter, just use IntrinsicPortalField instead of PortalField. For example:

IFilterClause filter = searchFactory.createOrFilterClause();
filter.addStatement(IntrinsicPortalField.EMAIL_ADDRESS, Operator.Contains, searchQuery);

Since IntrinsicPortalField is a subclass of PortalField, the PRC has no problem with it. I’ve tested this with e-mail address and it works flawlessly. I’m sure other properties will work perfectly well too.

Enjoy!

Comments

Comments are listed in date ascending order (oldest first)

  • Thank you, Chris 🙂 Now that Chris has kindly posted a workaround, any possibility of having this put into an IDK hotfix?

    Posted by: ewwhitley on August 27, 2006 at 2:39 PM

  • I second Eric’s opinion. Can you guys just remove the artificial restriction of 100 from the IDK in the next release? Seems to work fine without it and it would obviate the need for my silly patch.

    Posted by: bucchere on August 30, 2006 at 2:31 PM

  • Here is an e-mail I received from a person who attempted to use my patch:

     

    I found your blog because I am having the same problem you describe with searching Intrinsic properties. However, I am now having trouble actually “patching” the IDK. How exactly would I go about repackaging everything with these new java files included? Thank you very much for your time and help.

    Here was my response:

    Thanks for your note. Assuming that you’re building a Java web application, all you need to do is compile the patch along with all your other application code. You can put the class files for the patch in WEB-INF/classes or you can make a jar (e.g. myapp.jar) and put the class files for the IDK patch there and then drop the jar in WEB-INF/lib. You can then put everything into a .war or .ear (or not).

    The magic of the Java classloader is that all the .class files in WEB-INF/classes and all your .class files inside jars in WEB-INF/lib all end up loaded into the same memory. That means that if you have two class files in two different jars, but they’re both in the com.plumtree.remote.portlet package (meaning you have the line package com.plumtree.remote.portlet; at the top of your source files and your .java files live in com/plumtree/remote/portlet), then they’ll act like they’re in the same package. This means that you’ll have access to all package private member methods, which the patch needs in order to compile.

    Posted by: bucchere on August 30, 2006 at 7:23 PM

  • Hi mate, I think this is very helpfull but I was wondering where can I find corresponding ID’s for all standart and custom user properties, when I’m using

    forID(26) method? Thanks in advance!

    Posted by: ggeorgiev on September 19, 2006 at 1:16 AM

  • This gets you the standard (intrinsic) ones:
    select objectid, name from plumdbuser.ptproperties order by objectid where objectid < 200;
    1 Name
    2 Description
    3 Object Created
    4 Object Last Modified
    5 Open Document URL
    6 Content Type ID
    7 Plumtree Document Image UUID
    8 Content Language
    9 Content Tag
    26 Email Address
    50 Full Text Content
    60 Document Submit Content Source
    61 Document Upload Repository Server
    62 Document Upload DocID
    71 Related Communities
    72 Related Folders
    73 Related Portlets
    74 Related Experts
    75 Related Content Managers
    80 Snapshot Query Reference
    101 Keywords
    102 Subject
    103 Author
    104 Created
    105 Document Title
    106 URL
    107 Category
    111 Comments
    112 Modified
    152 Phone Number
    153 Title
    154 Department
    155 Manager
    156 Company
    157 Address
    158 Postal Code
    159 State or Province
    160 Country
    161 Employee ID
    162 City
    163 Address 2
    

    For the custom ones, change the where clause to >= 200.

    Posted by: bucchere on September 23, 2006 at 6:53 PM