Monday, June 30, 2008

Amazon Unbox Review

Synopsis: Amazon Unbox is too lacking in the convenience and features departments to justify the price. But if they address some of the missing features and switched to a Netflix-style, all-you-can-eat subscription model, then they could really compete.


Recently had an excuse to try out Amazon Unbox on our Tivo. The whole family was sick this past weekend and we had already watched our Netflix movie and everything Tivo had recorded for us :) So we thought, why not download a movie from Amazon Unbox?

I had already linked my Amazon account with our Tivo account, so ordering the movie just required that I enter a 5 digit pin number. There was a snafu with the credit card I was using, but once I got that resolved, the movie started downloading almost immediately. Only problem was that it took a LOOOONNNNGGGG time for it to download (we have a supposedly 10mbps cable DSL connection). By long, I mean in the range of about 6-8 hours. Since we started this just before dinner, there was no chance we could watch this that night. We had to wait until the next day.

The movie shows up in the Now Playing list as soon as it starts downloading. There is no estimate of how long it will take to complete, but Tivo does tell you how many minutes of the movie have been downloaded. The Amazon Unbox site doesn't tell you how many minutes total the movie is, but I was able to find this information via Netflix. Thus, you can do back of the envelope calculation of how long the download will take. (By the way, I believe the total download size was 2.4 GB).

The way Amazon Unbox works is that you have 24 days to keep the downloaded rental, but once you start watching it, you have 24 hours before it will be deleted. Tivo's provides a warning message when you start watching the movie and if you stop watching Tivo tells you exactly when the movie will be deleted. So that's all very good.

We started watching the movie, and the picture quality is top notch, easily DVD quality. It's just like a recorded TV program; there are no chapters, no menus, just the movie from beginning to end. So that's one strike against Amazon Unbox, that you don't get the bonus features or options or navigability of a DVD. Since the characters in this particular movie speak with a Scottish accent, we wanted to put on subtitles. Nope, can't do it. How about closed captioning? Amazingly, no, closed captioning doesn't work either. This is pretty much a deal breaker for me.

The movie cost $3.99, about the same as rental from a local video store. Is the value worth the price? No, I don't think so. Speaking of the local video store, it would have been faster to have walked to it to rent a DVD, and we would have had a richer viewing experience on top of it, for about the same price. And it can't begin to compete with the tremendous value of the Netflix service.

I have some advice for the Amazon Unbox team (they're waiting with bated breath for it, I know). They really need to add support for closed captioning, at the very least, but if they could figure out a way to add in the other features that one typically expects with a DVD that would definitely bolster their offering. The long download time could be mitigated if the download occurred automatically, in the background. What I'm thinking of here is a subscription service and that one could have a Netflix like queue with their Amazon Unbox account and when they finish with one movie and delete it, the next movie could start downloading immediately. This would mean a turnaround time measured in hours instead of the Netflix turnaround time which is measured in days.

Saturday, June 28, 2008

Getting open square bullets in OpenOffice.org

Sometimes it's nice to create a bulleted list with a check box like bullet, e.g., creating a packing list. Seems like in Word there's a quick option for selecting this style of bullets, but not so in OpenOffice. To change a bulleted list to have a check box style bullet:
  1. Select the bulleted list and in the menu go to Format > Bullets and Numbering...
  2. In the dialog that comes up, select the Options tab.
  3. Click the ... button labeled Character. Under Ubuntu, the default font for my document is DejaVu Sans and the subset of characters displayed is the Geometric Shapes. If you scroll up or down a bit, you can see a few different open square type bullets that can be used as check boxes.

Sunday, June 22, 2008

Eclipse Pluto Server Plug-in, v. 0.2.5 (now on Google Code)

Just finished moving the Pluto Server Plug-in code over to its own project page, pluto-server-plugin, on Google Code. I created a new minor release, v. 0.2.5, just to switch over the update site to the one on Google Code. Switching over a plug-in's update site took some experimentation, but here's how I did it (for future reference and for anyone else needing to change a plug-in's update site)
  1. Change the Update Site URL for the Feature to the new update site.
  2. Bump the Feature version and do a new release to the old site. Users with an older release of the plug-in will get the update which will point them to the new update site for future releases.
  3. Change the Update Site URL in the Update Site's site.xml file. Publish the update site to the new update site location.
So hopefully it will be a seamless update for Pluto Server Plug-in users.

Sunday, June 01, 2008

Pluto Server Plug-in: Could not publish to server NPE bug

Some users of the Pluto server plug-in have had trouble getting a simple plug-in to deploy. They receive an error message:
Could not publish to the server.
java.lang.NullPointerException

Unfortunately, I've not been able to recreate the "Could not publish to the server" error. I have created a new, clean Eclipse workspace and install, and went through the tutorial and it all still works.

For those who are experiencing problems, I've added a test portlet eclipse project that you can import and try to deploy to Pluto. Some other troubleshooting ideas
  • Make sure that you have a portlet.xml along with your portlet code. That will be necessary. See the eclipse project above for a simple example.
  • When first doing the deploy, you'll need to be connected to the internet so that the Maven ant tasks can download the pluto dependencies. This is only required the first time you use the plug-in to deploy to a pluto server.
  • Check ECLIPSE_WORKSPACE/.metadata/.log. It may have more details regarding the exact nature of the error. If you find anything, please do share it with me; I'd love to get to the bottom of this bug.
In other Pluto Server Plug-in related business, I hope to have the code moved over to Google Code sometime this month.

Sunday, October 21, 2007

Pluto Server Plug-in Screencast Posted

Just finished the screencast promised for the Pluto Server Plug-in. It's fairly simple, goes through installing the plug-in and then creating a simple "hello world" type portlet and deploying and displaying it in Pluto.

Unfortunately, the size of the screencast, produced with Wink, comes it at a little over 10MB. I need to figure out how to keep these guys from getting so bloated. Using color palette now, has reduced the file size to just under 5MB.

Sunday, September 30, 2007

Eclipse Pluto Server Plug-in, v. 0.2.4

I've been working on an Eclipse Plug-in recently to provide a server definition for the Pluto portal driver. Let me explain why. To start with, I should say that I've been using Pluto 1.1.x with Eclipse for a while, since it comes bundled with Tomcat, it makes a nice, lightweight rapid development environment for working with portlets. And Pluto keeps getting better. In the latest release (v. 1.1.4), changes to the portal pages and layouts are persisted across server restarts.

Alas, there has always been the annoyance that Eclipse wants to deploy your web application as-is to the server, including the web.xml, and Pluto, like all portal servers, expects special servlet mappings to be in place for portlets in your portlet application. For me this has meant having to rewrite the web.xml for when I want to deploy to Pluto in Eclipse, but then changing it back for when I want to change a servlet definition (or deploy to a different portal server, e.g., GridSphere). So that is the problem that this Eclipse plug-in solves. The web.xml rewriting is taken care of by the plug-in at deployment time.

I've put up a quick web page for those interested. For the impatient, create a new remote site in the update manager with this url: http://www.extreme.indiana.edu/~machrist/projects/pluto-server-plugin/updates

I've started work on a screencast to show how this thing works, and hopefully I'll get that posted next week.

Tuesday, July 24, 2007

PURSe Portlets 1.1.0 Released

Just finished tagging and releasing a new version of the PURSe Portlets. There are several great things in this release. The one I am most proud of is also the one least apparent to the casual observer: the PURSe portlets are now synced up with the mainline PURSe codebase. In PURSe Portlets 1.0.x, I had made modifications to the PURSe 1.0 release, but now I am a PURSe committer and I've committed some of those modifications and bugfixes into the PURSe trunk. Also, I've developed an install script for PURSe that will install PURSe and it's dependencies, including a minimal deployment of Globus. This should take a lot of the pain out of getting started with PURSe for the new user.

Additional new features include:
  • Added an AJAX "Check availability of username" to registration portlet
  • Forgot password portlet now resets the user's password instead of revealing it
  • Administrative portlet has a paged table of users
  • Administrative portlet also allows to add a single user or multiple users at once
  • Portal/portlet "branding" greatly simplified and aligned with PURSe
See the release notes for more information, or the download page to get it now.

Monday, July 09, 2007

Apache MaxClients reached, all connections in CLOSE_WAIT

Our Extreme Lab web server (Solaris running Apache 1.3) has recently developed a problem. Twice yesterday it got into a state where it had hit its MaxClients limit (of 128, apparently) and then was unable to service any further requests. Running netstat -f inet showed the all existing connections were in the CLOSE_WAIT stage. I can't tell at this stage if there is some denial of service attack going on or just a problem with the server preventing it from finally closing these connections.

Update: Googling around, and with some help from our local Unix guru, Rob Henderson, I found out that if you have connections stuck in the CLOSE_WAIT stage, this usually indicates that the server side is having trouble closing the connection. Rob says that usually when he sees this problem it is because of an NFS server being down. You get requests for something on that NFS mount, and the process hangs there for a long time. With that NFS server remounted, things are much better. Whereas before I was seeing steadily increasing numbers of CLOSE_WAIT connections, I now see none.

Thursday, June 14, 2007

JSF Portlet Fun: Request Scoped Beans Don't Survive From Action to Render Phase

Not sure if this is Pluto specific, but request scoped beans basically don't work as expected since they get initialized on the action phase and then again on the render phase. This is with MyFaces 1.1.4 and Pluto 1.1.3. At least with GridSphere 2.1.5 and MyFaces 1.0.9, I know that request scoped beans do work. See http://issues.apache.org/jira/browse/MYFACES-788 for more information. The workaround is basic and actually standard from the Portlet API point of view. The idea is that for any thing that you want to carry over from the action phase to the render phase, then just add this as a render parameter. In this example, a field of the request scoped bean called "success" is a boolean. So in the action method, I do this:

// add the success field to a render parameter so it will be available in
// the render phase. See constructor as well.
ExternalContext extContext = FacesContext.getCurrentInstance().getExternalContext();
if (extContext.getResponse() instanceof ActionResponse) {
ActionResponse actionResponse = (ActionResponse) extContext.getResponse();
actionResponse.setRenderParameter("success", Boolean.toString(success));
}


Then in the contructor for the bean I have:
// Pull success value from render parameter set in action phase, i.e., submit()
ExternalContext extContext = facesContext.getExternalContext();
Map params = extContext.getRequestParameterMap();

if (params.containsKey("success")) {
this.success = Boolean.parseBoolean((String)params.get("success"));
}

Wednesday, June 06, 2007

TeraGrid 2007, Day 3

In the Software Provider Forum, I gave a talk on PURSe (using slides provided to me by Rachana Anathakrishnan) and also about the PURSe portlets work I've been doing.



In the presentation I give a sketch of a roadmap for a PURSe portlets 1.1 release, which should be out within a month.

TeraGrid 2007, Day 2

Stu Martin and I gave a talk on GRAM Auditing and integration of it with the LEAD Portal.

Tuesday, June 05, 2007

TeraGrid 2007, Day 1

During most of the first day at TeraGrid 07, I was involved in the TeraGrid Institute tutorial, a kind of "TeraGrid for beginners" tutorial. We did a demo/tutorial using the LEAD Portal as a way of demonstrating how a science gateway can make effective use of TeraGrid, how the sum of TeraGrid is greater than it's individual parts. A screencast we developed in support of the tutorial is available.

Near the end of the day, I had a talk about the LEAD Portal for the "Build your own science gateway" tutorial. It's titled The LEAD Portal: An OGCE based weather science gateway, and you can see it below.



I talked with Kent Milfeld briefly afterward about the difficulty in supporting and managing production and development deployments.

Tuesday, May 22, 2007

Pluto and creation of UserInfo Map

Pluto throws a NullPointerException if there is an attribute defined in your portlet.xml that it does not recognize. Happened to me because I generally have the non-standard attribute "user.name" which is the way to get a GridSphere user's username (as opposed to PortletRequest.getRemoteUser()).

Thursday, April 19, 2007

Creating a Portlet Preferences JSF VariableResolver

One of the really cool things about JSF is that just about every piece of it is extensible. One thing I've been playing with recently is the VariableResolver and PropertyResolver parts of the JSF API. I want to create a VariableResolver that is specific to portlet development, so that, for example, when you want to create an EDIT page where a user can specify a preferred nickname, it would look something like this:

<h:inputText value="#{portletPreferences.nickname}">

And that's it, because custom Variable and Property resolvers take care of the getting and setting for us, no backing beans required.

Turns out this is fairly easy to do. I started with Cagatay Civici's excellent blog/article on creating a custom VariableResolver and PropertyResolver. The first thing to do was to create the VariableResolver. The trick here is that VariableResolvers follow a "Chain of Responsibility" design pattern, and the parent resolver is passed in via the constructor of the child resolver. This is an extremely important point. I rushed ahead on the PortletPreferences part of the variable resolving and forgot to implement the chained part, and you get some interesting results when you do this. Suddenly, your managed beans just silently are unresolvable. No error messages, nothing in the logs, and in fact, it appears at first that they are working, but they do not. So just a heads up, that if you create your own JSF resolvers, do the chaining part first and look out for disappearing managed beans.

So here's how my initial PortletVariableResolver looked:


public class PortletVariableResolver extends VariableResolver {

private static final org.apache.log4j.Logger log = org.apache.log4j.Logger
.getLogger(PortletVariableResolver.class);

public static final String PORTLET_PREFERENCES = "portletPreferences";
private VariableResolver originalResolver;

public PortletVariableResolver(VariableResolver originalResolver) {
this.originalResolver = originalResolver;
}
@Override
public Object resolveVariable(FacesContext facesContext,
String variableName)
throws EvaluationException {

try {
if (PORTLET_PREFERENCES.equals(variableName)) {
PortletRequest portletRequest =
(PortletRequest)facesContext
.getExternalContext().getRequest();
return portletRequest.getPreferences().getMap();
}
} catch (Exception e) {
throw new EvaluationException(
"Failed to resolve variable [" +
variableName + "]", e);
}
return originalResolver.resolveVariable(facesContext,
variableName);
}

}


So here we're resolving the "portletPreferences" variable to the Map of the user's portlet preferences. This is nice, but it only gets you read access to the portlet preferences. For write access as well, we'll need a custom PropertyResolver. First, let's change our variable resolver so that it returns the PortletPreferences object, instead of the map:


if (PORTLET_PREFERENCES.equals(variableName)) {
PortletRequest portletRequest =
(PortletRequest)facesContext
.getExternalContext().getRequest();
return portletRequest.getPreferences();
}


Now for the PropertyResolver.


public class PortletPropertyResolver extends PropertyResolver {

private static final org.apache.log4j.Logger log = org.apache.log4j.Logger
.getLogger(PortletPropertyResolver.class);

private PropertyResolver originalPropertyResolver;

public PortletPropertyResolver(PropertyResolver propertyResolver) {
this.originalPropertyResolver = propertyResolver;
}

@Override
public Class getType(Object obj, int index) throws EvaluationException,
PropertyNotFoundException {
if (obj instanceof PortletPreferences) {
throw new PropertyNotFoundException("Cannot reference PortletPreferences by index notation");
} else {
return this.originalPropertyResolver.getType(obj, index);
}
}

@Override
public Class getType(Object obj, Object obj1) throws EvaluationException,
PropertyNotFoundException {
if (obj instanceof PortletPreferences) {
return String.class;
} else {
return this.originalPropertyResolver.getType(obj, obj1);
}
}

@Override
public Object getValue(Object obj, int index) throws EvaluationException,
PropertyNotFoundException {
if (obj instanceof PortletPreferences) {
throw new PropertyNotFoundException("Cannot reference PortletPreferences by index notation");
} else {
return this.originalPropertyResolver.getValue(obj, index);
}
}

@Override
public Object getValue(Object obj, Object obj1)
throws EvaluationException, PropertyNotFoundException {
if (obj instanceof PortletPreferences) {
// TODO: support access to String[] preference values
PortletPreferences prefs = (PortletPreferences) obj;
String prefName = (String) obj1;
return prefs.getValue(prefName, null);
} else {
return this.originalPropertyResolver.getValue(obj, obj1);
}
}

@Override
public boolean isReadOnly(Object obj, int index)
throws EvaluationException, PropertyNotFoundException {
if (obj instanceof PortletPreferences) {
throw new PropertyNotFoundException("Cannot reference PortletPreferences by index notation");
} else {
return this.originalPropertyResolver.isReadOnly(obj, index);
}
}

@Override
public boolean isReadOnly(Object obj, Object obj1)
throws EvaluationException, PropertyNotFoundException {
if (obj instanceof PortletPreferences) {
PortletPreferences prefs = (PortletPreferences) obj;
String prefName = (String) obj1;
return prefs.isReadOnly(prefName);
} else {
return this.originalPropertyResolver.isReadOnly(obj, obj1);
}
}

@Override
public void setValue(Object obj, int index, Object obj1)
throws EvaluationException, PropertyNotFoundException {
if (obj instanceof PortletPreferences) {
throw new PropertyNotFoundException("Cannot reference PortletPreferences by index notation");
} else {
this.originalPropertyResolver.setValue(obj, index, obj1);
}

}

@Override
public void setValue(Object obj, Object obj1, Object value)
throws EvaluationException, PropertyNotFoundException {
if (obj instanceof PortletPreferences) {
PortletPreferences prefs = (PortletPreferences) obj;
String prefName = (String) obj1;
String prefValue = (String) value;
try {
prefs.setValue(prefName, prefValue);
prefs.store();
} catch (Exception e) {
log.error("Unable to set portlet preference [" + prefName + "] to value [" + prefValue + "]", e);
}
} else {
this.originalPropertyResolver.setValue(obj, obj1, value);
}
}

}


I've highlighted the main two methods here, getValue and setValue, and I think you'll find them fairly straightforward. The other issue is that we have to implement the array index part of the property resolving, and we just throw an exception in this case (although, as a further exercise, we might want to implement this so that we can reference multi-valued preferences).

At this point, you just need to register the VariableResolver and PropertyResolver in your faces-config.xml file, like so:


<application>
<property-resolver>
resolver.PortletPropertyResolver
</property-resolver>
<variable-resolver>
resolver.PortletVariableResolver
</variable-resolver>
</application>


That's it.

Thursday, March 29, 2007

Credential renewal with MyProxy and JGlobus

I'm currently working on create a service as part of the LEAD project that will be responsible for kept grid credentials fresh for the duration of a workflow or some other user process. I've been experimenting with the capabilities of MyProxy and JGlobus to this end.

First of all, the MyProxy site has an excellent page on various grid credential renewal issues and MyProxy. So to begin, I want to set my credential renewer service as a "default renewer" in the MyProxy configuration. I do this by adding

default_renewers "DN of my renewing service"

to the myproxy-server.config file. Now my renewing service can renew credentials stored in this MyProxy server. Next, I store a proxy in MyProxy without a passphrase, so that MyProxy can use it for proxy renewal.

myproxy-init -n -s myproxy-server.mydomain.org -l myusername

The -n option says to store the proxy without a passphrase. Now I can renew this proxy with

myproxy-logon -s myproxy-server.mydomain.org -a /tmp/aging_proxy \
-l myusername -o /tmp/refreshed_proxy

In the previous command, -a specifies the proxy that we want to renew. For this to work, you either need to have loaded a proxy credential of the renewing service, or you need to set the X509_USER_CERT and X509_USER_KEY environment variables to the locations of the certificate and unencrypted key of your renewing service. And to do MyProxy renewal using the JGlobus API, it looks like this:

MyProxy myproxy = new MyProxy(myproxyHost, myproxyPort);
GetParams getParams = new GetParams();
getParams.setUserName(username);
getParams.setLifetime(24*60*60);
getParams.setAuthzCreds(userCred);

GSSCredential renewedCredential = myproxy.get(serviceCred, getParams);

Note that you need a valid MyProxy username as well as a still valid proxy credential. To load the service credential, do this:

GlobusCredential globusCred = new GlobusCredential(pathToServiceCert,
pathToServiceKey);
GSSCredential gssCred = new GlobusGSSCredentialImpl(globusCred,
GSSCredential.INITIATE_AND_ACCEPT);
An important thing to keep in mind (which I forgot halfway through this process) is that the credential stored in MyProxy cannot have a passphrase protecting it for it to be used to renew a proxy credential. We make use of the grid credential storage feature of MyProxy in the LEAD project, and for this to work with credential renewal, we first have to unencrypt the private key of the grid credential. Use openssl to do this:

openssl rsa -in ~/.globus/userkey.pem -out ~/.globus/userkey1.pem

Then store your credential to MyProxy with this key:

myproxy-store -s myproxy.mydomain.org -l myusername -y .globus/userkey1.pem

Now you'll be able to use this MyProxy credential for proxy renewal.

Wednesday, March 28, 2007

Setting the var attribute of dataTable in a facelet, part II

This time I take a completely different approach to this problem. The problem, by the way, is how to apply default Java Portlet API (JSR-168) CSS styles to the dataTable component. As shown in the previous post, this is complicated in Facelets by the fact that we can't just simply pass through a value for the var attribute of the dataTable component. But, this time that won't be an issue because we can simply extend the way Facelets handles the dataTable component and more directly apply the CSS styles we want.

After grepping through the Facelets source code, I discovered that HtmlComponentHandler is the TagHandler associated with the dataTable component (and all other HTML components as well). So I extended this with my own class, HtmlDataTableHandler:


package portletfacelets;

import javax.faces.component.UIComponent;
import javax.faces.component.html.HtmlDataTable;

import com.sun.facelets.FaceletContext;
import com.sun.facelets.tag.jsf.ComponentConfig;
import com.sun.facelets.tag.jsf.html.HtmlComponentHandler;

public class HtmlDataTableHandler extends HtmlComponentHandler {

public HtmlDataTableHandler(ComponentConfig config) {
super(config);
}

@Override
protected void onComponentCreated(FaceletContext ctx,
UIComponent c, UIComponent parent) {
super.onComponentCreated(ctx, c, parent);
if (c instanceof HtmlDataTable) {
HtmlDataTable table = (HtmlDataTable) c;
if (table.getFooterClass() == null) {
table.setFooterClass("portlet-section-footer");
}
if (table.getHeaderClass() == null) {
table.setHeaderClass("portlet-section-header");
}
if (table.getRowClasses() == null) {
table.setRowClasses("portlet-section-body," +
"portlet-section-alternate");
}
if (table.getStyleClass() == null) {
table.setStyleClass("portlet-section-body");
}
}
}

}


I check if the classes are null, to allow the user of the TagHandler to override the styles, but that's about it. The facelets taglib entry looks like this:


<tag>
<tag-name>dataTable2</tag-name>
<component>
<component-type>javax.faces.HtmlDataTable</component-type>
<renderer-type>javax.faces.Table</renderer-type>
<handler-class>portletfacelets.HtmlDataTableHandler</handler-class>
</component>
</tag>


I'm not sure which method I prefer, but I'm leaning toward the former, since it seems simpler and I like the idea of keeping things like CSS styles in a template file than in Java code, but in some sense this second approach also seems to be the cleaner one.

Running Portlets on Pluto from within Eclipse

The Pluto team recently released version 1.1.0 and as part of that release they include a very useful Pluto + Tomcat bundle that makes it very easy to get started. So I've been playing around with this and trying to figure out how to get it to work with Eclipse + WTP, and initially I ran into some issues. By default, WTP wants to take your application server and copy its files to a temporary location in which to deploy your webapp. However, this is fairly disastrous for portlet development because WTP doesn't copy everything and leaves behind important stuff like shared/lib, the portal webapp, etc. So I was trying to figure out what was left behind and manually copying the missing bits into the temporary location... aargh! And then I discovered a nice little feature of WTP. There is this "Run modules directly from the workspace" checkbox in the server settings for your app server, and unchecking this has the effect that Eclipse will deploy your web applications to the location of your app server and not some temporary location! So anyways, here are my notes on how to get this to work.

First, you need to register the Pluto 1.1/Tomcat 5.5.20 bundle with Eclipse as a Tomcat 5.5 server. Select File > New > Other and select Server, then click Next (or, if in the J2EE perspective, right click in the Servers View and select New > Server). Select a server type of "Tomcat v5.5", and click Next. Name it whatever you like, I call mine "Apache Tomcat v5.5 - Pluto". For "Tomcat installation directory:" browse to the location of where you installed the pluto bundle.




Click Finish. Now, we need to modify our Server definition for pluto just a bit, so go to the Servers view (if you don't have it, just switch to the J2EE perspective, or add the view with Window > Show View > Other..., then select Servers). Double click on your Pluto server defined there. Uncheck the "Run modules directly from the workspace" checkbox. We want Eclipse to run Pluto from the location of the pluto-bundle, so that it picks up the pluto webapp, shared/lib, and other bits that are needed because we are working with portlets.




Now, in order to run your portlet in Pluto from within Eclipse using WTP, you need to have your web.xml "pluto-ified". The Pluto guys have an Ant task called "assemble" which can do this for you, that's what I use. Just have it update your web.xml file (probably want to make a backup first). I've been working with a hello-world JSF Facelets sample portlet recently, here's how I get it running in Pluto:

Now we just need to get our application running on Pluto/Tomcat as before (right click on the Project, select Run As > Run on Server). The difference this time is that we will access our application through Pluto. In your web browser, go to http://localhost:8080/pluto. Login as user pluto, password pluto. In the upper left hand corner there is a label called "Navigation:". Mouse over this label to get a popup menu. Then select Pluto Admin from this list. Under Pluto Pages, select Pluto Admin from the drop down list. Then under Portlet Applications, select hello-world-jsf-facelets in the first drop down, and then in the second drop down select HelloWorldSamplePortlet, and click Add Portlet.



Now, the portlet should be there at the bottom of the page.



Note that the portlet pages configuration doesn't persist across restarts of the Pluto server. Consult the Pluto documentation if you want to persist the portal pages layout and configuration.

Now that our portlet is deployed to Pluto from within Eclipse, we can develop our portlet and have the changes immediately reflected in Pluto!

Thursday, March 22, 2007

Setting the var attribute of dataTable in a facelet

I'm currently working on a small little facelets suite to aid in portlet development. Initially, the idea is just to focus on making correct use of the portlet CSS styles implicit. So, for example, the following is the kind of code needed to set the portlet tables styles for the h:dataTable JSF component:


<h:dataTable value="#{mybean.myData}" var="row"
styleClass="portlet-section-body"
rowClasses="portlet-section-body,portlet-section-alternate"
headerClass="portlet-section-header">


So, I'd like to replace that with a facelet instead, so that it looks like this:


<p:dataTable value="#{mybean.myData}" var="row">


And the facelet would be defined once in a .xhtml file as:


<ui:composition>
<h:dataTable value="#{value}" var="#{var}"
styleClass="portlet-section-body"
rowClasses="portlet-section-body,portlet-section-alternate"
headerClass="portlet-section-header">
<ui:insert />
</h:dataTable>
</ui:composition>


However, this doesn't quite work. The problem is that it tries to set the var attribute of the dataTable component to a value expression, but dataTable requires a simple string for var. So we have to figure out some way of getting the var variable from the facelet into the dataTable component. What I came up with was a custom TagHandler that evaluates the value expression and then sets the var property of the dataTable component directly:


package portletfacelets;

import java.io.IOException;

import javax.el.ELException;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.component.html.HtmlDataTable;

import com.sun.facelets.FaceletContext;
import com.sun.facelets.FaceletException;
import com.sun.facelets.tag.TagAttribute;
import com.sun.facelets.tag.TagConfig;
import com.sun.facelets.tag.TagHandler;

public class SetVarHandler extends TagHandler {

private final TagAttribute var;

public SetVarHandler(TagConfig config) {
super(config);
this.var = this.getAttribute("var");
}
public void apply(FaceletContext ctx, UIComponent parent)
throws IOException, FacesException, FaceletException, ELException {

System.out.println("var=" + this.var.getValue(ctx));

if (parent instanceof HtmlDataTable) {
HtmlDataTable table = (HtmlDataTable) parent;
table.setVar(this.var.getValue(ctx));
}


this.nextHandler.apply(ctx, parent);
}

}


I add this to my facelets taglib.xml file as such:


<tag>
<tag-name>setVar</tag-name>
<handler-class>portletfacelets.SetVarHandler</handler-class>
</tag>


And then use it like this in my facelet:


<ui:composition>
<h:dataTable value="#{value}"
styleClass="portlet-section-body"
rowClasses="portlet-section-body,portlet-section-alternate"
headerClass="portlet-section-header">
<p:setVar var="#{var}" />
<ui:insert />
</h:dataTable>
</ui:composition>


Voila! However, seeing as this might come in handy in future instances, I wondered if I couldn't make something a bit more generic. So I created this TagHandler:


package portletfacelets;

import java.io.IOException;
import java.lang.reflect.Method;

import javax.el.ELException;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;

import com.sun.facelets.FaceletContext;
import com.sun.facelets.FaceletException;
import com.sun.facelets.tag.TagAttribute;
import com.sun.facelets.tag.TagConfig;
import com.sun.facelets.tag.TagHandler;

public class SetValueHandler extends TagHandler {

private final TagAttribute methodName;
private final TagAttribute value;

public SetValueHandler(TagConfig config) {
super(config);
this.methodName = this.getRequiredAttribute("methodName");
this.value = this.getRequiredAttribute("value");
}
public void apply(FaceletContext ctx, UIComponent parent)
throws IOException, FacesException, FaceletException, ELException {
try {

Method m = parent.getClass().getMethod(this.methodName.getValue(ctx),
new Class[]{String.class});
m.invoke(parent, new Object[]{this.value.getValue(ctx)});

} catch (Exception e) {
e.printStackTrace();
}

this.nextHandler.apply(ctx, parent);
}

}


So I'm using reflection to call any method on the parent JSF component and passing in the specified value. Then I use it in my facelet like so:


<ui:composition>
<h:dataTable value="#{value}"
styleClass="portlet-section-body"
rowClasses="portlet-section-body,portlet-section-alternate"
headerClass="portlet-section-header">
<p:setValue methodName="setVar" value="#{var}" />
<ui:insert />
</h:dataTable>
</ui:composition>


There you have it. Turns out there is more than one way to skin this cat, so I'll also share in a future blog post a different approach to this issue.

How to watch Google Videos on your DivX compatible DVD player

Google has put videos of their Tech Talks series online and I was interested in seeing if I could play these on my SD-3990 Toshiba DVD player which is DivX compatible. All you need to do is:
  1. Find the video at Google Video. Click the download link.
  2. The Google Video Player comes up and starts buffering the video. It saves the video into your My Videos/Google Video directory, or perhaps in a different location, you can check the preferences to see where the location is.
  3. The saved movie file has an extension .gvi (note, this is different than the saved Google Video Player, .gvp, file). Rename the extension to .avi.
  4. Burn this file to a CD-R disc. And it's ready to play.
Last night I used this technique to watch part of Bram Moolenaar's (of Vim fame) talk on 7 Habits For Effective Text Editing 2.0 on my DVD player from the comfort of my living room.

Wednesday, March 07, 2007

Custom Tomcat Server Location coming in Eclipse WTP 2.0

Web Tools Platform 2.0 M5 News

This looks very cool (scroll down to "Configure Tomcat's Paths"), especially for us portlet developers. It's been challenging using Eclipse WTP to run and debug portlets because WTP wants to take the Tomcat installation you provide, copy the server parts to a temporary location, and then deploy your webapp there. So it doesn't matter if the Tomcat installation you tell WTP to use has GridSphere or Pluto installed, they won't be there in the temporary location (neither the portal webapps nor shared/lib). Of course, you can manually copy the missing bits into the temp location, restart the server in Eclipse and get something working, but only after much consternation. So I happily welcome this new development and look forward to using WTP 2.0 when a final release is made.