RichFaces “columns” tag doesn’t display columns

The RichFaces manual show this example on using the “columns” tag:

<rich:dataTable value="#{capitalsBean.capitals}" var="cap">
<rich:columns value="#{capitalsBean.labels}" var="col" index="index">

<h:outputText value="#{cap[index]}" />

</rich:columns>

</rich:dataTable>

There is no corresponding sample Java code.

That’s very unfortunate, since in the absence of any better documentation, the natural thing to expect is that “capitalBeans.labels” would be a DataModel object, just as it is for the table itself. And it isn’t.

The model for columns is a simple ordered collection, such as a List.

JPA and overlapping complex keys

If at all possible, you should avoid setting up databases with complex keys (or compound keys, as they are sometimes known). It makes life a whole lot more complex, and in a world where everyone already says you’re taking too long because “All You Have To Do Is…”, complex isn’t a good thing.

Sometimes, however, there’s just no choice.

I have one app that has a table with 2 complex-key relationships. The table’s primary key is (idA, idB) and there’s a foreign key reference to another table item with a key of (idA, idC). The table’s key is an embedded object of type “table1Id” and the foreign reference is a lazy-load “table2” object.

The full details are here.

JPA and Fixed-length text fields in databases

Lucky me. I took over a system whose database is awash with compound keys. The more I work with this stuff, the more justification I find for always having a simple sequence key as the primary key, despite the apparent extra overhead.

I’ve had a long-running problem where parent-child (one-to-many) relationships weren’t able to fetch their children. At best, the collection came up empty. At worst, accessing the children collection property of the parent would throw a NullPointerException because the “bag” property of the PersistentBagCollection wasn’t initialized.

I spent a LOT of time with Hibernate’s trace-level logging turned on and the only thing I could determine was that the children were, in fact, being correctly loaded and formed into a collection, but the collection wasn’t the collection that was bound to the parent – instead the child collection was being silently lost.

One thing that did look suspicious, however was that the trace showed two collections found. I went round and round with this, added various bits of code that made objects more identifiable in the debugger, and finally – at long last ended up with a set of trace messages showing the owner primary keys of these collections.

Lo, and behold! One of them showed trailing spaces in the key field values, the other did not.

Trailing spaces in ORM fields have much the same effect and being sloppy with upper/lower case in Java filenames under Windows. Sometimes you get away with it, but not always. Because of the trailing spaces, the two primary keys didn’t compare equal, the “real” collection couldn’t be attached to its parent, and data was lost.

I suppose that this problem could be avoided by proper implementation of the equals() and hashCode() methods of the offending primary key objects, but I found it easier to simply force the key objects to be space-padded and to be more careful about what I passed to search functions, since the ultimate problem lies in the fact that sending a space-trimmed value to a Hibernate JPA query will match and return a space-padded object.

The ultimate cure for this would probably be to use fixed-size character arrays instead of String objects, but there are several problems here as well:

  1. Java’s static type checking doesn’t cover mismatches on array size.
  2. Individual characters in a character array can be null
  3. The ORM class-generating tools I use render fixed character fields as Strings, not arrays
  4. I’m not actually sure that either JPA or Hibernate support character array properties. I have enough grief trying to portably represent boolean and enum values.

Tomcat ignores FORM based authentication, uses BASIC authentication

Here’s a week of my life down the drain. I’m setting up a new production site with Apache AJP fronting Tomcat and when the Tomcat webapp needs authentication, it ignores the FORM authentication directive, presenting a BASIC authentication dialog, instead!

I thought it had to do with AJP and SSL tunneling. It didn’t. I finally confirmed that by firing up a text-only browser (this is a remote hosting location with no GUI) and bypassing the firewall, Apache, AJP and everything but Tomcat itself.

The culprit turned out to be insidious. I was overriding an authentication option in order to fine-tune client caching. However, the examples I’d been pilfering from were using BASIC authentication, not FORM authentication. So when I added the config option, it was the Tomcat Valve for BasicAuthenticator.

Turns out that if you do that, the BasicAuthenticator silently takes over, overriding the FORM authentication configuration in web.xml.

The cure: use the FormAuthenticator Valve!

Making Apache mod_rewrite and the ajp Tomcat connector work together

Apache’s URL rewriting facility can be used to shape URLs piped to Tomcat, but it’s not as simple as it seems.

The basic action is straightforward. First incoming URLs are processed by mod_rewrite, then they are matched up against JkMount definitions to find the appropriate Tomcat connector to use.

The problem is, what Tomcat sees isn’t what you’d expect to have come out from the rewrite process.

What actually happens is that before being submitted to mod_rewrite, the incoming URL is converted to server-relative form. After rewriting, the resulting URL is then reassembled, including the parts that Tomcat doesn’t want or need. Including the Apache DocumentBase path.

To prevent this problem, just qualify the last rewrite rule in the process with the “PT” directive to let Apache know to “Pass Through” the URL without reassembly. So, for example:

RewriteRule ^/$ /mywebapp/index.jsp [L,PT]
JkMount /mywebapp/* ajp13

Howto: Installing TracDiscussion

TracDiscussion allows adding discussion forums to the Trac (http://edgewall.org) system. Unfortunately, there’s not a lot of info on how to to it.

In a perfect world, you’d just issue the command “easy_install TracDiscussion” and bam-you’re-done. In this world, not yet, anyway.

Installing TracDiscussion requires installing some prerequisite packages. If you’re running Trac 11 or later, you don’t need to install the web administraction package, since it’s now part of Trac. Yes, they say that, but rather ambiguously, so this is an unqualified statement. Secondly, you need a spam filter. You’re dealing with email, spam is now about 90% of all email, so you need a filter.

Installing the actual TracDiscussion plugin isn’t all that hard even lacking a functional direct easy_install. Just go to the SVN archives, pick the project directory whose name matches your trac version and use the Subversion export command to download it. Then run easy_install against the downloaded directory.

That’s only half the battle. At that point, restart your HTTP server to reload the Trac configuration. You should see the TracDiscussion plugin listed on the admin plugins page. Select all options. In particular, select the installation option. If you don’t select that one, the extra tables won’t get added to your database.

You’ll probably get an error page backin instructing you to run the trac-admin update command. Do that from a command shell. If using PostgreSQL it will probably tell you to disable database backup on the upgrade. Hopefully you’re doing regular PostgreSQL backups anyway, but you might want to kick off a manual PostgreSQL backup before doing the Trac upgrade just for good luck’s sake.

You may want to edit the [notifications ]section of your trac.ini file in order to configure email.Then restart HTTP. And backup the newly-updated database.

With luck, “discussion” will now appear as one of the items on your Trac menubar and there will be a Discussions management section on the sidebar menu of the admin page.

Android – Projection Maps explained

Android comes with the SQLite database. Although it’s a lighweight DBMS, its authors are sufficiently proud of it that instead of listing what features it has, they prefer to list what features it doesn’t!

In addition to the SQL support we all know and love, Android includes some SQLite features that aren’t really well explained. First and foremost of which is the SQL Query Builder. Although nominally it’s a way to construct SQL without “knowing SQL”, I suspect its more important feature is that it makes it easier to construct SQL from context URIs.

One item, however, which has proven to be really frustrating is the Projection Map. Actually, I’m not even where they pulled the term “projection” from, but Projection Maps have been a real poser for me. I finally broke down and looked at the SQLQueryBuilder source code. A reminder: a ProjectionMap is a Map<String, String>.

Here’s what I found.

If you read the JavaDocs, you’d get the impression that Projection Maps effectively provide a “poor man’s database view” where you can simplify selection request items (projections). In actuality, it’s not so straightforward. Or maybe it is, but the explanation isn’t. It doesn’t help that the literature is rife with projection maps whose sole purpose in life seems to be to convert something to itself: “table1._id” => “table1._id” and so forth.

First of all, you don’t even need a projection map unless you want one. Just don’t set it. If you take that approach, the projection names are processed “as is”, exactly as passed in via the projection array.

Secondly, even the projection array is optional. If you pass null, it generates a “SELECT *” operation. In general, that’s not recommended in SQL, because of the breakage that can occur if columns are added/removed, or re-ordered, but that option is still available if you need it.

Thirdly, the projection map, if supplied applies only to the selection list! In other words, you could map “store_name” => “bookstore.storename”, and it would result in “select bookstore.storename …”, but if you supplied “store_name” in the “order by” query builder parameter or one of the other qualified parameters, you can potentially end up with bad SQL.

Fourth, the projection map can be overruled. If the key part of a projection entry contains an “as” clause, the value part is ignored and the key part is inserted into the resulting SQL. For example “parrot as polly” => “cracker” will generate “SELECT parrot as polly …” and no crackers. The “as” can be either all upper-case or all lower-case (not mixed case) and must have at least one space before and after the word “as”.

Fifth, if you provide a projection map and no projection array, the projection map’s value entries are used to generate the select item list and the projection map’s keys are ignored. In other words, “parrot as polly” => “cracker” will result in “SELECT cracker, but only if no projection array (zero elements or null) is supplied. It’s not a safe thing, however to make a map like “parrot as polly” => “cracker as graham”.  At the moment, that would result in “SELECT cracker as graham”, but only if no select array was supplied. But that’s probably an artifact, not an intended behavior.

Sixth, and finally, If you do provide both a projection map and a projection array, each and every element in the array must appear as a key in the map! Any omissions will cause the SQLQueryBuilder to throw an IllegalArgumentException (Invalid column) when it attempts to build the SQL. That one’s mentioned in the JavaDocs, by the way.

Now you know. And so do I.

Android ListViews with Checkboxes

Android has a very useful way of organizing lists: the ListView. However, it doesn’t seem to work right when you include a checkbox in the list!

Actually, it turns out that it works exactly right, but only if you understand what “right” actually is.

The normal behavior of a ListView item is that you can highlight it and click it and the whole item line participates. Not so when the line includes a checkbox. Why? http://www.mail-archive.com/android-developers@googlegroups.com/msg11390.html

Which says, in brief: If a Listview contains an item that can receive focus, that item receives events instead of the listview.

It turns out that that is literally true. Set the “focusable” attribute on the CheckBox to false and the magic returns: http://stackoverflow.com/questions/1121192/android-custom-listview-unable-to-click-on-items . Don’t forget to manually set the “checked” attribute when you handle the list item click!

Here’s an interesting bit of code: http://www.anddev.org/checkbox_text_list___extension_of_iconified_text_tutorial-t771.html

Using the Apache OpenJPA command-line tools

Stuff that’s unfortunately not concentrated into a convenient ready-to-use example in the Apache OpenJPA docs. But that’s what this blog is all about!

When using the reverse engineering, schema, and other tools directly from a shell script (not Ant or Maven), the default place to get datasource definitions and related options is in META-INF/persistence.xml. This file is mandatory even in cases where you don’t actually connect to the database, such as generating Java source from an XML schema (reverse generation).

Because the tools are using a validating parser, a schema name is REQUIRED. Example, supplying the JPA schema via the xmlns attribute:

<?xml version="1.0"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
  <persistence-unit name="openjpa">
    <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
    <properties>
      <property name="openjpa.ConnectionURL" value="jdbc:hsqldb:tutorial_database"/>
      <property name="openjpa.ConnectionDriverName" value="org.hsqldb.jdbcDriver"/>
      <property name="openjpa.ConnectionUserName" value="sa"/>
      <property name="openjpa.ConnectionPassword" value=""/>
      <property name="openjpa.Log" value="DefaultLevel=WARN, Tool=INFO"/>
      <property name="openjpa.jdbc.DBDictionary" value="StoreCharsAsNumbers=false"/>
    </properties>
  </persistence-unit>
</persistence>


CAUTION:
You should delete the orm.xml file when re-running these tools. Otherwise they will use the old copy, which may not be in sync with your current efforts.

1 2 3 4 5