ljnelson

10 posts
ljnelson

CDI gotcha Blog

Posted by ljnelson Jul 23, 2010

I learned this the hard way and wanted to make sure other people could benefit.

While putting together a Java EE 6 application, I had put together a @SessionScoped bean that inherited from an abstract superclass.  Both the super- and subclass have injection points.  The subclass uses @Inject; the superclass only uses @PersistenceContext.

Now, I had added the required META-INF/beans.xml file to the jar file housing the subclass, but never thought to add it to the superclass jar file, since of course when I wrote the superclass I wasn't thinking CDI at all (and actually as it happened when I wrote the abstract superclass it was in the Java EE 5 days), so why would I have packaged it into a bean archive?

But it turns out--somewhat unintuitively--that if you inherit from a class that has an injection point in it--even if the injection point is defined in terms of Java EE and not CDI--and if that abstract class comes from a jar that is not a bean archive, then dependency injection by the CDI provider will not be applied to its injection point until you make sure that its jar file contains a META-INF/beans.xml file.

A simpler way to put this is that for any given inheritance chain, all jars in the chain containing classes with injection points must be bean archives.

(On a totally unrelated subject, the documentation links in this post are to the horrific new documentation URLs that Oracle has seen fit to impose on the Java community landscape.  Add your voice to the general din calling for the old URLs to come back after checking out the Twitter E17476 hashtag.)

I wanted to describe an interesting problem I recently faced that involved the most excellent JAX-RS specification and its Jersey implementation.  The quick overview is that we wanted the output of our JAX-RS resource classes to be client-agnostic, but we also wanted the ability to insert a decorator of sorts in between the objects we were returning and the normal JAX-RS serialization/writing process, all without relying on any vendor-supplied extensions.  This turned out to push the capabilities of the JAX-RS specification quite hard, and I thought the experience was worth writing up.  Thanks to Paul Sandoz and Marc Hadley for making appropriate noises along the way.

The Client

Many of you may be familiar with SmartGWT.  This is a truly wonderful AJAX toolkit that provides a GWT wrapper around a robust, battle-hardened Javascript library called SmartClient.  One of its many strengths is its comprehensive support for data binding by way of DataSources.

One such DataSource is the RestDataSource, which is an extremely convenient way to talk to a preexisting back end service.  Its convenience comes at a minor price: to the extent that you use it out of the box, you are bound by its request and response formats.  These are not onerous formats to support, but they do smell like SmartClient, and to the extent you want to not couple your stuff to a given vendor's requirements you might not want to hardcode your output to meet these requirements.  (There are many ways to tweak SmartGWT so that it sends and expects data in ways that you control, but I won't be covering those here.)

The Server

In a project I'm working on, we had the requirement to offer up our API via JAX-RS-compliant web services (I don't want to say REST, because I really really really really don't want to go down that rathole; that's what rest-discussis for).  We also wanted to not get into the serialization format wars--we wanted to define our resources such that they could be returned via XML or JSON, and according to whatever whims a given client might expect.

That meant (for us) that we wanted our resource class to return simple collections of objects, and leave both the XML (or JSON) serialization to JAX-RS, and--more interestingly--any wrapping or reorganizing behavior to JAX-RS as well.  That is, a given resource would not know that it was being called from a SmartGWT client, even though the SmartGWT client expects XML data in a particular format.

As it turns out, as I mentioned, the requirements imposed by the RestDataSource are pretty mild.  A SmartGWT RestDataSource can indicate, for a retrieval operation, whether it wants paging or not, and it can also specify the start index and the (exclusive) end index of the slice that it wants you to return.  Then it expects that you return a very particular XML payload; consult the RestDataSource documentation for all the details.

The Problems

So we had several problems:

  • How were we going to turn a request likefoo/bar?_startRow=0&_endRow=3&_operationType=fetchinto something not so SmartGWT-RestDataSource-ish, but preserve the semantics?
  • How were we going to take a Collection<? extends Foo>, as returned by one of our resource classes, and get it ultimately spat out in RestDataSource-friendly XML?

Handling the Request

 First, we focused on the "incoming" problem.

RestDataSource, if not otherwise configured, sends its "fetch" requests with at least three well-known request parameters:

  • _operationType=fetch
    
  • _startRow=some number
    
  • _endRow=some number
    

The concepts here are good, but the "row"-ness doesn't really fit with the way we think about things, so we decided to normalize this into a URL format that we defined.  A request like:

http://your.company.com/api/gorp?_operationType=fetch&_startRow=0&_endRow=10

would be canonically represented to our resource classes instead as:

http://your.company.com/api/gorp/paged/0,10

To do this, we employed a simple javax.servlet.Filterthat used a RequestDispatcher to forward the incoming request to the new URL.  (Note that we did not eliminate the query parameters--they are still there, but our classes never look for them, just as our classes might not look for other query parameters that other client toolkits might define.)

Describing this Filter is probably a subject for another blog entry--suffice it to say this was not hard to write, and I'm not going to cover its implementation here.

Handling the Response

Next, we focused on the "outgoing" problem.

Assuming we have a resource class that returns something, like this:

@Path("gorp")
public class GorpResource {

  @Path("paged/{pagingControl}")
  public Collection<? extends Gorp> getGorpPaged(@PathParam("pagingControl") PagingControl control) {
    // we'll fill this in in a bit
  }
}

...how do we turn a Collection<? extends Gorp> into RestDataSource-compliant XML?

RestDataSource XML, it turns out, requires a very specific format.  It looks like this:

<response>
  <status>0</status>         <!-- your response status; haven't explored the legal values -->
  <startRow>0</startRow>     <!-- the zero based index that was handed to you -->
  <endRow>10</endRow>        <!-- the exclusive index of the data you were able to return
  <totalRows>400</totalRows> <!-- an estimate of the total size of the domain that your query COULD have returned -->
  <data>
    <!-- your data goes here in any format you want -->
  </data>
</response>

(It also turns out this isn't exhaustively documented anywhere; it now is in the SmartGWT forums.)

The answer lies with the MessageBodyWriter class.

MessageBodyWriter

A MessageBodyWriter is a part of JAX-RS whose implementations are responsible for serializing a resource class's return value into a format that the client can deal with.  Normally a MessageBodyWriter is supposed to be tied closely to its output format.  It's expected, in other words, that if you want to write one of these things, you're doing so because you want to stream your payload in a particular text format, say, or you want to wrestle control away from the JAXB-driven default that JAX-RS ships with.

In our case, we actually didn't want to do this.  We simply wanted to wrap the Collection<? extends Gorp> up inside a wrapper object that would define the RestDataSource "header" and "footer" elements that I hope are evident from the sample text above.  Then in a perfect world we would take that wrapper object, our Collection<? extends Gorp> safe inside it, and hand it back to JAX-RS as though it had been returned directly from the resource class in the first place.  JAX-RS would serialize this wrapper object as it normally would, regardless of how else it had been configured, and everything would come up roses.

It takes a lot of manure to bring up roses.  So I began shoveling.

First, I defined a RestDataSource container object that I labeled--inspiringly--RestDataSourceResponse.  I've reproduced the interesting parts below:

@XmlRootElement(name = "response")
@XmlType(propOrder = { "status", "startRow", "endRow", "totalRows", "data" })
class RestDataSourceResponse implements Serializable {
  private static int defaultPageSize;
  private final int status;
  private final long startRow;
  private final long endRow;
  private final long totalRows;
  @XmlAnyElement
  @XmlElementWrapper(name = "data")
  private final Collection<?> data;
}

If I could manage to set this class's data element to our Collection<? extends Gorp>, then serializing this thing would cause SmartGWT XML to go out over the wire.

Next up, I defined a MessageBodyWriter that made use of just about every JAX-RS way of injecting things under the sun.

A bit of background.  When JAX-RS was born, dependency injection was not a new concept but had not been baked into Java EE 6 yet (see JSR 299 for all those gory details).  So JAX-RS has its own way of doing a limited degree of dependency injection.  Oversimplifying on purpose, in general you can put @Context on an instance variable somewhere, and provided it's The Right Sort Of Thing it will be injected for you.

One such Thing that I was going to need in my MessageBodyWriter was an instance of Providers, because that's how you get existing MessageBodyWriters that JAX-RS already knows about.  So high up in my MBW is an instance variable like this:

@Context
private Providers providers;

So, now armed with a way to root around in JAX-RS's innards, I had to figure out exactly what an MBW is supposed to do.

Implementing the MessageBodyWriter Contract

The first thing to note is that a MessageBodyWriter takes a type parameter.  The idea here is that you're usually going to create a MessageBodyWriter that knows how to serialize a Gorp instance, for example, and nothing else, so you're going to create a subclass of it called GorpWriter, let's say, and it's going to extends MessageBodyWriter<Gorp>:

@Provider
public class GorpWriter extends MessageBodyWriter<Gorp> {
  //...
}

But that's not what we had to do, because we wanted to handle everything.  So our MessageBodyWriter implementation had to extend MessageBodyWriter<Object>:

@Provider
public class RestDataSourceAdapter extends MessageBodyWriter<Object> {
  //...
}

(What's the @Provider annotation?  This is JAX-RS's way of indicating that a given class implements an interface.  By marking a class as a @Provider, you're saying that it is eligible for dependency injection, or, in this case, return from Providers#getMessageBodyWriter().)

isWriteable

The first thing a MessageBodyWriter has to do is to see if it can write a particular thing that is handed to it.  This is handled by its implementation of the isWriteable method.  Briefly, you're handed a return value from some resource class somewhere, along with some--but not all--of the context ultimately available to you from the request, and you get to figure out whether you can handle the thing or not.

In our case, we had to translate.  We had to translate the given object into a RestDataSourceResponse (by wrapping it), and then delegate the question to the innards of JAX-RS:

@Override
public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
  boolean returnValue = false;
  if (type != null && !(RestDataSourceResponse.class.isAssignableFrom(type))) {
    final MessageBodyWriter<RestDataSourceResponse> delegate = this.providers.getMessageBodyWriter(RestDataSourceResponse.class, RestDataSourceResponse.class, annotations, mediaType);
    returnValue = delegate != null && delegate.isWriteable(RestDataSourceResponse.class, RestDataSourceResponse.class, annotations, mediaType);
  }
  return returnValue;
}

The signature here is worth investigating.  The type argument looks to my eyes (after experimentation) like it is the actual class of the returned object from the resource class.  The genericType argument looks to be the declared return type.  Anyway, both are available to you to help you make your decision about whether you want to write instances of these types or not.

The first thing to notice here is that we check to make sure that the type in question is notRestDataSourceResponse.class.  We want to let JAX-RS's normal innards handle the writing of such things itself.

In the case that we are talking about any other class, we ask JAX-RS for a MessageBodyWriter that knows how to deal with RestDataSourceResponses.  If we haven't done anything else fancy with JAX-RS, this will be some default Jersey-supplied JAXB writer of some kind; we just care that it's non-null.  When we get such a delegate, we ask him if he is capable of writing instances of RestDataSourceResponse, which we'll be producing in our writeTo() method (detailed later).

getSize

Next up is the getSize() method.  Here a MessageBodyWriter is being asked to compute the size, in bytes, of the payload headed back to the browser.  The specification implies, but does not state directly, that by the time this method is called you can assume that your isWriteable() method has been called and has returned true.

You can punt on this method if you like, and return -1L, but I wanted to be accurate.  To be accurate, I had to take the incoming object whose size is to be calculated, surreptitiously wrap it in a RestDataSourceResponse object, and then hand that back into the innards of JAX-RS so that it can do the size calculation:

@Override
public long getSize(final Object object, final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
  long returnValue = -1L;
  final MessageBodyWriter<RestDataSourceResponse> delegate = this.providers.getMessageBodyWriter(RestDataSourceResponse.class, RestDataSourceResponse.class, annotations, mediaType);
  if (delegate != null) {
    // CreateResponse basically wraps the incoming object in a 
    // new RestDataSourceResponse
    final RestDataSourceResponse response = this.createResponse(object);
    assert response != null;
    returnValue = delegate.getSize(response, RestDataSourceResponse.class, RestDataSourceResponse.class, annotations, mediaType);
  }
  return returnValue;
}

This delegation pattern should be quite familiar.  We are, once again, asking JAX-RS to do the dirty work, pretending that the resource has actually returned a RestDataSourceResponse instead of whatever it actually returned.

Now for the fun part.

writeTo

A MessageBodyWriter has a writeTo() method that actually does the writing of the object.  As you might expect, we're going to implement this by following the same delegation strategy we did for the prior two methods.  But there's an added twist, which is that we need to somehow augment the JAXBContext that's going to tell JAXB how to serialize our response...but I'm getting ahead of myself.

Here's the writeTo method, implemented naively, which, at its heart looks similar to the other two.  I've omitted error checking and logging to keep this short; you would be nuts to do the same:

@Override
public void writeTo(final Object object,
                    final Class<?> type,
                    final Type genericType,
                    final Annotation[] annotations,
                    final MediaType mediaType,
                    final MultivaluedMap<String, Object> httpHeaders,
                    final OutputStream entityStream)
  throws IOException, WebApplicationException {
  assert type.isInstance(object);
  final MessageBodyWriter<RestDataSourceResponse> realWriter = this.providers.getMessageBodyWriter(RestDataSourceResponse.class, RestDataSourceResponse.class, annotations, mediaType);
  final RestDataSourceResponse response = this.createResponse(object);
  realWriter.writeTo(response, RestDataSourceResponse.class, RestDataSourceResponse.class, annotations, mediaType, httpHeaders, entityStream);
}

Now I say "naively", because although this skeleton is correct, JAX-RS is not going to be able to write our response.  Why not?

Welcome to the JAXB Jungle

We are going to detour out of JAX-RS land and into the thorny, tangled jungle of JAXB, which I fear might be thoroughly understood by only the mighty Kohsuke Kawaguchi.

As it turns out, in the project I was working on that drove this blog entry, we make liberal use of interfaces for a whole host of reasons--some good, some bad.  JAXB really really really really really really doesn't like interfaces, and so when you start wanting to have them turned into XML, you run into a whole heap of trouble.  The aforementioned Mr. Kawaguchi has produced the Unofficial JAXB Guide, which seems to be the only source of information on how to get JAXB to deal with interfaces, and, as you can see, it's not trivial.

Additionally, in our writeTo() method above, if we forward all control off to the "real" JAX-RS-provided MessageBodyWriter--the one that will use JAXB by default to do XML serialization--then the JAXB environment within which it runs will contain only the RestDataSourceResponse class (as well as some other boilerplate ones, like java.util.Date and the like).  Notably, it will know nothing about classes referred to by our RestDataSourceResponse, such as, for example, the class of its wrapped object (our Collection<? extends Gorp>).  Gee, it sure would be nice if the "real" MessageBodyWriter would use a JAXBContext built by us that knows all about the type(s) we just wrapped.

What to do?

Getting a JAXBContext: Implementing ContextResolver<JAXBContext>

It turns out that Jersey's implementation of the JAX-RS specification allows us to supply our own JAXBContext that knows about all sorts of types.  To offer it up, we need to provide a ContextResolver<JAXBContext> implementation, known as a "context provider", and that context provider had best be stuffed with every last concrete type we can think of at the time that we go to write our response.

When such a ContextResolver exists, Jersey's innards will grab it and use it to provide a JAXBContext for its internal operations.  Perfect!

So first, we amend our MessageBodyWriter signature so that it also implements ContextResolver<JAXBContext>:

@Provider
public class RestDataSourceAdapter extends MessageBodyWriter<Object> implements ContextResolver<JAXBContext> {
  //...
}

Conveniently enough, we're already a @Provider, so apart from implementing the getContext() method, we're all set.

Let's think for a moment what we would have to do to get a JAXBContext out of this thing.

We know we'll need to invoke the newInstance(Class...) method.  And we know we're going to need RestDataSourceResponse.class, and the classes reachable from its payload, and--

Of course, we don't have any of that inside the getContext() method.  So before we even get started we're going to have to go back to writeTo() and somehow make that information available outside of it in a thread-safe manner.

Supplying JAXBContext With Classes: Enter Instance-level ThreadLocals

What we want is a container that we can use to grab type information from writeTo() that is also available to our getContext() implementation.  But many threads may be in play.  So we need a thread-safe construct for storing all of these classes that we're going to need to build a new JAXBContext instance.

Near the top of our MBW, therefore, we do this:

private final ThreadLocal<Set<Class<?>>> types;

(It occurs to me, actually, as I write this, that this probably should be a ThreadLocal<BlockingQueue<Set<Class<?>>>>.  But that's just too many angle brackets, don't you think?  http://weblogs.java.net/sites/all/modules/fckeditor/fckeditor/editor/images/smiley/msn/regular_smile.gif  Stay tuned to this blog in case I get burned by this.)

And then, in our writeTo method, we do this:

final Set<Class<?>> typeSet = new HashSet<Class<?>>();
this.processTypes(typeSet, mediaType, type, RestDataSourceResponse.class, genericType, object.getClass());
final RestDataSourceResponse response = this.createResponse(object);
assert response != null;
final Collection<?> data = response.getData();
if (data != null) {
  this.processTypes(typeSet, mediaType, data.getClass());
}
this.types.set(typeSet);

We'll look at the processTypes() method in a moment.  The gist is that we're grabbing all the type information we possibly can (without doing something silly like introspecting classes), and we're setting it into our instance-level ThreadLocal type set.  Then, later, our getContext() method can use that variable to discover all the types it's going to need.

(The ThreadLocal has to be an instance variable and not a static field because we're interested in the thread that is working onthis particular instance of MessageBodyWriter-and-ContextResolver, not any old thread that happens to be working with any old instance.)

Now let's look at processTypes to see exactly what kind of concrete type information we can suck out of the arguments to the writeTo() method without getting too ridiculous.  The gist here is that we take in a Set of Class instances, which we use to keep track of types we've already gathered up, a couple of other boilerplate parameters, and then a variable list of Types that need to be mined for more Class instances.  We recursively plow through this pile until we're all done, and at the end of it all the Set that was passed in will contain all the class information we can possibly infer about the stuff that came back from our resource class's return value:

private final void processTypes(final Set<Class<?>> typeSet, final MediaType mediaType, final Type... types) {
  if (typeSet == null || types == null || types.length <= 0) {
    return;
  }
  for (final Type type : types) {
    if (type == null || typeSet.contains(type)) {
      continue;

    } else if (type instanceof ParameterizedType) {
      final ParameterizedType ptype = (ParameterizedType)type;
      // Recursive calls
      this.processTypes(typeSet, mediaType, ptype.getRawType());
      this.processTypes(typeSet, mediaType, ptype.getOwnerType());
      this.processTypes(typeSet, mediaType, ptype.getActualTypeArguments());

    } else if (type instanceof GenericArrayType) {
      final GenericArrayType gat = (GenericArrayType)type;
      // Recursive call
      this.processTypes(typeSet, mediaType, gat.getGenericComponentType());

    } else if (type instanceof WildcardType) {
      final WildcardType wt = (WildcardType)type;
      // Recursive calls
      this.processTypes(typeSet, mediaType, wt.getLowerBounds());
      this.processTypes(typeSet, mediaType, wt.getUpperBounds());

    } else if (type instanceof TypeVariable) {
      final TypeVariable<?> tv = (TypeVariable<?>)type;
      // Recursive call
      this.processTypes(typeSet, mediaType, tv.getBounds());
      final GenericDeclaration gd = tv.getGenericDeclaration();
      if (gd != null) {
        if (gd instanceof Class) {
          // Recursive call
          this.processTypes(typeSet, mediaType, (Class<?>)gd);
        }
        // Recursive call
        this.processTypes(typeSet, mediaType, gd.getTypeParameters());
      }

    } else if (type instanceof Class) {
      final Class<?> c = (Class<?>)type;
      if (Modifier.isAbstract(c.getModifiers()) || c.isInterface()) {
       /*                  * We'll talk about this in a moment.         */       } else {
        typeSet.add(c);
      }
    } else {
      throw new UnsupportedOperationException("I don't know how to work with a Type of type " + type.getClass());
    }
  }
}

I probably overdid this method, since I'm not sure--and none of the Javadoc says one way or the other--what kinds of Types can be supplied to writeTo.

So again, this method recursively calls itself until it finds a concrete Class type, whereupon it adds it to the set.

What about abstract types and interfaces?  Recall that these types are going to be used to build a JAXBContext, and JAXB doesn't like interfaces or abstract classes.

As it turns out, we can use the same ContextResolver magic here to allow other registered ContextResolvers to provide us with concrete classes when they are supplied with interfaces.  This is sort of like dynamically implementing an @XmlSeeAlso annotation.  So the "we'll talk about this" comment above can be replaced with:

final ContextResolver<Class> concreteClassProvider = this.providers.getContextResolver(Class.class, mediaType);
if (concreteClassProvider != null) {
  final Class<?> concreteClass = concreteClassProvider.getContext(c);
  if (concreteClass != null && !Modifier.isAbstract(concreteClass.getModifiers()) && !concreteClass.isInterface()) {
    typeSet.add(concreteClass);
  }
}

...and now if there are any ContextResolver<Class> implementations lying around on the classpath, they'll be called and will have the opportunity to offer up a concrete class that implements the supplied abstract class or interface.

Putting it All Together

Back to our own ContextResolver<JAXBContext> implementation.  OK, so we've got a place where a bunch of type information is stashed away.  Now all we have to do is use it.  Here's the naive implementation:

@Override
public JAXBContext getContext(final Class<?> ignored) {
  JAXBContext returnValue = null;
  final Set<Class<?>> types = this.types.get();
  if (types != null && !types.isEmpty()) {
    try {
      returnValue = JAXBContext.newInstance(types.toArray(new Class<?>[types.size()]));
    } catch (final JAXBException jaxbException) {
      // Here's where you'd log the error.  You WILL log the error, right?
    } finally {
      this.types.remove();
    }
  }
  return returnValue;
}

JAX-RS's innards will call this method during the JAX-RS-supplied-and-implemented rendering of our RestDataSourceResponse object.  It will probably pass in something like RestDataSourceResponse.class, but, to be frank, by this point we know more about types than even JAX-RS itself does, so we essentially just ignore the incoming parameter in favor of getting our Set of Classes instead.  Once we get our paws on these Classes, we use them to create a new JAXBContext instance, which we then return into the bowels of JAX-RS somewhere, blissfully unaware of how it's going to be used.

If anything goes wrong here, we return null, which is permitted (and encouraged) by the contract to indicate that we're just not a very useful ContextResolver<Class> after all.

Conclusion

Here's the five second version.  This blog entry has shown you how to:

  • take the return value of a resource class
  • stash away JAXB-related type information from it
  • wrap it in a reasonably arbitrary wrapper
  • ask JAX-RS to serialize the wrapper object
  • offer up a new JAXBContext customized for the particular kind of wrapper object

...all without relying on anything other than the JAX-RS specification.

I hope you found this useful.  Thanks for reading, and thanks to Paul Szndoz and Marc Hadley for a great, flexible specification and implementation.

Best,
Laird

ljnelson

Nimbus and Opacity Blog

Posted by ljnelson Jul 31, 2008

This one tripped me up, and I thought I'd post it here. I have a case where I need to pour text into a JTextField, but have that JTextField look like a JLabel. So as I type in one field, I need this "gray" second field to update, live, before the user's eyes. I also need the ability for the user to cut-and-paste values out of it. This sort of thing is reasonably common in properties panels and the like.

Now, normally all you have to do is something like this:

final JTextComponent previewComponent = new JTextField();
previewComponent.setBorder(null);
previewComponent.setEditable(false);
previewComponent.setOpaque(false);
...and you have what looks like a JLabel but what behaves like a JTextField.

 

Well, except under Nimbus, where the setOpaque(false)seems to have no effect. Turns out you need to also set the background color to a transparent color as well. Fortunately, this combination of behaviors seems to work under all the other look and feels (looks and feels?):

previewComponent.setBorder(null);
previewComponent.setEditable(false);
previewComponent.setOpaque(false);
previewComponent.setBackground(new Color(0, 0, 0, 0));


 

I hope this helps other Swing folks out.

ljnelson

Useful Swing Thing #1 Blog

Posted by ljnelson Dec 17, 2007

In Microsoft Outlook, along with probably dozens of other desktop applications, pressing ESC just about anywhere will cause the current window you're in to be cancelled. In the case of dialogs, it is as though the Cancel button were pressed; in the case of root windows, it is as though you tried to exit the application (and you usually get a warning dialog that asks you if you're sure).

For one of my applications, I wanted to get this very useful UI gesture installed once, in the right way, preferably without messing with the current look and feel, without requiring tons of custom code, and without subclassing. Here's a narrative of my journey, which also serves as a tutorial of sorts to theUIManager and UIDefaults classes.

For the impatient, here's the source code discussed in the article below: https://bricks.dev.java.net/source/browse/*checkout*/bricks/swing/jrootpanes/src/main/java/bricks/swing/jrootpanes/JRootPanes.java.

I started by looking into the UIDefaults class, since I hadn't really done much with the UIManager class and its related classes in general. To sum up, perhaps incorrectly, because I'm still not all that comfortable with it, you can think of the UIDefaults class as a hashtable containing all of the various UI attributes and properties that you can think of in the Java platform. (This is wrong because really those values come from the look and feel classes, and they're the ones who installed them, but for the moment forget about where these values came from and focus on how you get access to them.)

UIDefaults instances are maintained and shepherded around by the UIManager class, which has lots of useful static methods to work with those UIDefaultsinstances.

So, for example, if you want to see what the font is, by default, for the current look and feel's rendering of aJButton's text, you would say:

final Font font = (Font)UIManager.get("Button.font");
This is really a cover for: 
final Font font = UIManager.getDefaults().get("Button.font");
...but is usually the idiom you'll see (and it's a whole lot more convenient).

 

(How do you know what keys and values there are? Well, obviously you could iterate over the results returned byUIManager.getDefaults().keys(), but failing that the only way to see what's in there is to examine the source of, for example, the BasicLookAndFeel class. That's probably a useful thing to look at anyhow.)

Picking up where we left off: if you're lucky and your JDK is normal you'll get back a non-null Font object that some LookAndFeel put there for you. That's why when you create a new JButton its font is...well, whatever that object is (usually it's the Dialog font). That is, you didn't have to set the font on the button yourself; the value was defaulted for you by the current look and feel.

The UIManager exposes three levels of these defaults: the user defaults, the look and feel defaults, and the system defaults. This happens transparently for callers: if you callUIManager.getDefaults() you get a UIDefaultsobject back that handles these three layers for you (in much the same way, for example, as java.util.Properties allows there to be a default Properties that is consulted in the case of a missed lookup).

If you never monkey with a UIDefaults object, then effectively you, the user, have never put in any user defaults, so anything you get out of that UIDefaults object is going to be either a look and feel default (i.e. the Metal or Ocean guys decided for you that teal or silver was an awesome color) or a system default (i.e. Microsoft's take on what color title bars should be). On the other hand, if you do put something into those defaults, then your value is the one that is returned, no matter what the current look and feel or system thinks the value should be.

Why is all this important? Because if you want to do something UI-ish by default—as in, "affecting every future instance of a UI class of some kind"—then you want to start here, because this is where those default values are managed.

Now, in this case I wasn't interested in colors or fonts or text or anything like that. I was interested in key bindings: InputMaps and ActionMaps. ActionMaps store Actions indexed by, conventionally, Strings; InputMapsstore, conventionally, Strings indexed byKeyStrokes. Well, it turns out that InputMaps are available—like fonts and colors—in theUIDefaults as well, for pretty much every component. This would mean that if I could get my hands on, for example, the default ActionMap and the right kind of InputMapinstalled on JRootPane instances by default I might have a prayer of setting up a key binding for the ESCAPE key that would attempt to close the current window.

For the ActionMap—i.e. the place that stores anAction under a String key—it turns out that all JRootPane instances that have UI delegates that extend from BasicRootPaneUI (which includes pretty much all the JDK-supplied UIs as well as a good number of third-party ones) will look for an ActionMap stored in the UIManagerunder the key—surprise, surprise—"RootPane.actionMap". That means if you do this:

final ActionMap actionMap = (ActionMap)UIManager.get("RootPane.actionMap");
...you'll get back a non-null ActionMapcontaining all the Actions that JRootPanes will have if and when any is constructed.

 

InputMaps are a little more complicated. There are three of them for any given JComponent:

  • the one whose bindings are in effect when the component in question is focused
  • the one whose bindings are in effect when the component is a parent or ancestor of the focused component (and a suitable binding couldn't be found in that component's set ofInputMaps
  • the one whose bindings are in effect when nothing else worked, and all we know is that the component in question is in a focusedWindow
At first glance, if you wanted an ESCAPE press to cause aWindow to close, you might think to put the binding in the second location—the InputMap whose bindings are in effect when the JRootPane in question is an ancestor of a focused component. This would mean, for example, that if you had a text field with the cursor in it, and someone pressedESCAPE, the following logic would occur: 
  1. Does the text field have an ESCAPE binding in itsWHEN_FOCUSED_COMPONENT InputMap? No.
  2. Does one of its ancestors have an ESCAPE binding in its WHEN_ANCESTOR_OF_FOCUSED_COMPONENT InputMap? Yes (this is what would happen in the case we're describing).


 

Well, we happen to be lucky in that this little sequence of code will give us the proper InputMap to work with:

final InputMap inputMap = UIManager.get("RootPane.ancestorInputMap");


 

(I'll have more to say in a moment about why this isn't quite right—hint: what if there isn't a component in the window that has the focus?—but it's pretty good for now.)

So with these tools, we now have a place to put anAction that will close the parent Window:

  public static final void installCloseAction() {
    assert EventQueue.isDispatchThread();
    ActionMap actionMap = (ActionMap)UIManager.get("RootPane.actionMap");
    if (actionMap == null) {
      actionMap = new ActionMap();
      UIManager.put("RootPane.actionMap", actionMap);
    }
    assert actionMap != null;
    if (actionMap.get("close") == null) {
      actionMap.put("close", new AbstractAction("Close") {
          @Override public final void actionPerformed(final ActionEvent event) {
            assert event != null;
            final JRootPane rootPane = (JRootPane)event.getSource();
            assert rootPane != null;
            final Container container = SwingUtilities.getAncestorOfClass(RootPaneContainer.class, rootPane);
            if (container instanceof JInternalFrame) {
              final JInternalFrame internalFrame = (JInternalFrame)container;
              if (internalFrame.isClosable()) {
                internalFrame.doDefaultCloseAction();
              }
            } else {
              final Window window;
              if (container instanceof Window) {
                window = (Window)container;
              } else {
                window = SwingUtilities.getWindowAncestor(rootPane);
              }
              if (window != null) {
                window.dispatchEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSING));
              }          
            }
          }
        });
    }
  }


 

Assuming this method is present in a class calledJRootPanes, we can test this by using the following snippet of test code (this uses some JUnit assertions to help us out, and for brevity I've omitted all the cruft to get this running on the event dispatch thread):

JRootPanes.installCloseAction();
final JRootPane rootPane = new JRootPane();
final ActionMap actionMap = rootPane.getActionMap();
assertNotNull(actionMap);
final Action closeAction = actionMap.get("close");
assertNotNull(closeAction);


 

Neat! OK, so we have managed to affect the ActionMapprototype for all future JRootPane instances, regardless of look and feel. How are we going to trigger our "close"Action?

One way to attempt this might be to do something like the following:

  final KeyStroke escape = KeyStroke.getKeyStroke("ESCAPE");
  assert escape != null;
  final InputMap inputMap = (InputMap)UIManager.get("RootPane.ancestorInputMap");
  if (inputMap != null && inputMap.get(escape) == null) {
    inputMap.put(escape, "close");
  }
That would non-intrusively affect the bindings stored in allJRootPane instances' InputMaps that are consulted when the JRootPane in question is the ancestor of a focused component. But what about when the JRootPane is the ancestor of a bunch of components, none of which has the focus?

 

Here's where things get tricky and annoying. It turns out that there is no InputMap entry in the UIDefaults forJRootPanes for the WHEN_IN_FOCUSED_WINDOW case. This makes a certain amount of sense: InputMaps installed in such cases actually have to be associated with their component (they must be instances of ComponentInputMap, actually) and so at "defaults time" you don't have a component, so it follows that you can't have a ComponentInputMap, and so it follows that you can't install one as theWHEN_IN_FOCUSED_WINDOW InputMap. But wait a minute. Doesn't ESCAPE close the window when you pop up aJOptionPane? Why, yes, it sure does. Hmmm.

I took a spin further down in the mammothBasicLookAndFeel class and discovered a weird little entry for JOptionPane defaults:

// lines 1192-3 of BasicLookAndFeel.java follow:
"OptionPane.windowBindings", new Object[] {
"ESCAPE", "close" },
Without (further) boring you to tears I discovered that there is an accepted idiom for building up the raw materials forWHEN_IN_FOCUSED_WINDOW InputMaps (forBasicLookAndFeel-descending look and feel implementations). Sure enough, if you trace the code back, you'll discover that the BasicOptionPaneUI UI delegate (the superclass of most JOptionPane UI delegates out there) grabs the key binding raw materials in here ("ESCAPE", "close") and converts them into aWHEN_IN_FOCUSED_WINDOW InputMap. And—oops,BasicRootPaneUI doesn't do this, which means thatBasicRootPaneUI doesn't ever consult anything in theUIManager/UIDefaults machinery to make this happen or to provide an injection point.

 

(If BasicRootPaneUI were changed, it would do something like this in its implementation of createInputMap(int, JComponent):

  • Get the "RootPane.windowBindings" entry out of theUIDefaults.
  • Call LookAndFeel.makeComponentInputMap(rootPane, bindings);.
...but, well, it doesn't.)

 

Being the stubborn sort, I turned to the other giant cudgel thoughtfully given to us by the AWT guys—Toolkit.addAWTListener(). The only place to "inject" an InputMap of the proper variety—since without subclassing we can't really do it at JRootPaneconstruction time—is when a JRootPane is made displayable. The code below monitors HierarchyEvents for those containing JRootPanes that have just been made displayable, and, when it finds such a JRootPane it tries to non-intrusively modify or install theWHEN_IN_FOCUSED_WINDOW InputMap:

  private static final KeyStroke escape = KeyStroke.getKeyStroke("ESCAPE");

  private static final AWTEventListener awtListener = new AWTEventListener() {
      @Override public final void eventDispatched(final AWTEvent event) {
        assert EventQueue.isDispatchThread();
        assert event != null;
        if (event instanceof HierarchyEvent && event.getID() == HierarchyEvent.HIERARCHY_CHANGED) {
          // This event represents a change in the containment hierarcy.
          // Now let's figure out what kind.
          final HierarchyEvent hevent = (HierarchyEvent)event;
          final Component changed = hevent.getChanged();
          if (changed instanceof JRootPane && ((hevent.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) && changed.isDisplayable()) {
            // Aha!  A JRootPane has just been made displayable!
            final InputMap inputMap = ((JRootPane)changed).getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
            if (inputMap != null && inputMap.get(escape) == null) {
              inputMap.put(escape, "close");
            }
          }
        }
      }
    };

  public static final void makeEscapeCloseAllWindows() {
    assert EventQueue.isDispatchThread();
    final Toolkit toolkit = Toolkit.getDefaultToolkit();
    assert toolkit != null;
    toolkit.removeAWTEventListener(awtListener);
    installCloseAction(); // from before; see earlier in this article
    toolkit.addAWTEventListener(awtListener, AWTEvent.HIERARCHY_EVENT_MASK);
  }


 

Sure enough, if you invoke this code before you create anyJDialogs or JFrames then silently all such windows will be closeable with the ESCAPE key.

Thanks for reading.

In the prior entry, we learned that Java ships with several tools to standardize the conversion between Strings andObjects. We covered the text conversion methods ofjava.beans.PropertyEditor. We will most definitely come back to PropertyEditors, because they're about a heck of a lot more than just converting text, but let's take a detour into the dank recesses of the java.text package.

Do you smell that? That's all the dust left over from when this package was deposited by IBM and Taligent into the official Java runtime platform 'round about 1997. java.text was put in, as I understand it, primarily for internationalization (I18N) purposes, and one of the bits that landed there was thejava.text.Format class, which, although described as a way to format Locale-specific information, has nothing whatsoever to do with Locales or internationalization at all. Go figure. It does, however, have lots to do with convertingObjects into Strings and vice versa.

Format

The Format class is responsible for formattingObjects into Strings, and taking Strings and parsing them back into Objects. UnlikePropertyEditor, whose API seems to have more plumbing and rebar and wiring than my house, a Format is a relatively simple thing. Want to parse a String into anObject? Call the stateless parseObject(String)method. Want to do the reverse? Call the format(Object)method. Easy and simple. And stateless.

java.text.Format also supports lots of substring parsing and formatting, via the ParsePosition andFieldPosition classes. In general, I've found the use cases for these to be pretty thin, except where Dates are concerned, and for that someone has already written theSimpleDateFormat class, so we'll pretty much deal with them only where necessary.

Let's pick up on our previous example and write aURIFormat class:

public class URIFormat extends Format {
  public URIFormat() {
    super();
  }

  // 1
  public StringBuffer format(final Object object, final StringBuffer buffer, final FieldPosition fieldPosition) {
    // 2
    if (buffer == null) {
      throw new NullPointerException("buffer == null");
    }

    // 3
    if (fieldPosition == null) {
      throw new NullPointerException("fieldPosition == null");
    }

    // 4
    if (object == null) {
      return buffer;
    }

    // 5
    if (!(object instanceof URI)) {
      throw new IllegalArgumentException(object + " is not a URI instance");
    }

    return buffer.append(object.toString());

  }
}
This method is relatively simple to implement properly and fully defensively. Here are the details (once again, numbers below correspond to numbers in the code comments above):
  1. java.text.Format's format(Object) method calls through to this one, which subclasses must implement. The general idea here is to sanity check our arguments, then investigate the FieldPosition to see what portion of the incoming object we should format, attempt the convert-it-to-a-String operation, and return the result. For our purposes we can skip the FieldPosition argument entirely (it's conceivable that you might want to implement aURIFormat that formats, for example, only the "authority" field of a given URI but we're not going to do that here).
  2. Note that the contract of this method requires us to throw aNullPointerException if buffer is null. I don't like this, but that's our contract. I'd prefer to simply return null, since it's obvious that what you put into this method comes out the back of it, but I didn't write the contract. Why not just let the NullPointerException happen naturally instead of proactively throwing it? Because there is no message that gets output by automaticNullPointerExceptions at all, and that is just evil.
  3. Continuing through the forest ofNullPointerExceptions, we are required to throw one as well if the fieldPosition is null. Even though in the vast majority of cases you won't ever use this parameter.
  4. Now, what does the contract say about a nullObject parameter? Fortunately, and correctly, it says nothing. That could be valid input for you. Since the contract is open here, I choose to practice leave-no-trace programming, and I simply return the buffer that was handed to me. That is, hey, I chose to "format" the null URI by doing exactly nothing. You could choose to do something different here. (If you choose to follow my example here, you'll reap the benefits later on, because we will be able to pass this value straight into aPropertyEditor implementation and honor its contract as a convenient side effect.)
  5. Next, we need to handle the case where invalid input is handed to us (null is not invalid input; there are many cases where you want to handle the formatting of null values; that's why we dealt with that as a separate case). So here the contract tells us, effectively, to reject bad input with anIllegalArgumentException. But, of course, it's up to you to decide what constitutes bad input. I've chosen to err on the conservative side and say, look, this Format I'm writing works only on URI instances. Anything else that it's asked to format won't work. You could also simply do nothing and return the incoming buffer as is.
  6. Now all we have to do is (again) use the well-documented and consistent toString() method from java.net.URIand append it to the buffer parameter. Sinceappend() returns the resulting StringBuffer, we simply return that.
Again, the thought process behind this really simple method is what's important. You want to think hard about the what garbage is going to be coming in and what the caller will expect out.

 

The parseObject method is basically just the reverse, but I'll have a little more to say about it in comparison to thesetAsText() method of PropertyEditor. It also has some of the weirdest error handling conventions I've ever seen. Basically, if there is an error, you return null, and use the incoming ParsePosition object to store where the error occurred during parsing. And in the success case, you are obligated to update the supplied parsePosition's regularindex property to just past where you finished parsing.

So what if you have to return null, so you can, for example, capture and parse user input that should be converted to the null reference? It would appear, although the contract is not at all specific, that you return null, but make sure that the ParsePosition's errorIndex property is set to -1 to indicate that there is no error. Additionally, there's some intent we can read from the source code.

Diving into the Format source code, we see that the convenience method, parseObject(String), actually evaluates the return value of this method by more or less ignoring the contract entirely! It checks to see if this method has modified the parsePosition's index property. If it hasn't, then, it would appear, an error has occurred (since hey, parsing couldn't even take place), and, it turns out,regardless of this method's return value, an exception is thrown. So much for contracts, at least where Taligent iswas concerned.

OK, then; here's how we do it:

//1
public Object parseObject(final String text, final ParsePosition parsePosition) {

  // 2
  if (parsePosition == null) {
    throw new NullPointerException("parsePosition == null");
  }

  // 3
  if (parsePosition.getErrorIndex() != -1) {
    return null;
  }

  // 4
  final int startIndex = parsePosition.getIndex();
  if (startIndex < 0) {
    parsePosition.setErrorIndex(startIndex);
    return null;
  }

  // 5
  if (text == null) {
    // This is not an error; this is a valid input value.  We want to return null.
    // So we take liberties with this soft and squishy area of the contract,
    // and we set the parsePosition's beginning to a value that is different
    // from what it initially was.  No one is going to try to parse a null
    // text twice, anyhow, and this actually prevents it.
    parsePosition.setIndex(-1);
    parsePosition.setErrorIndex(-1);
    return null;
  }

  // 6
  final int textLength = text.length();
  if (textLength <= 0 || text.trim().getLength() <= 0) {
    parsePosition.setIndex(-1);
    parsePosition.setErrorIndex(-1);
    return null;
  }

  // 7
  if (startIndex >= textLength) {
    parsePosition.setErrorIndex(startIndex);
    return null;
  }

  // 8
  try {
    final URI uri = new URI(text.substring(startIndex);
    parsePosition.setIndex(textLength);
    parsePosition.setErrorIndex(-1);
    return uri;
  } catch (final URISyntaxException kaboom) {
    parsePosition.setErrorIndex(kaboom.getIndex());
    return null;
  }

}
  1. This method is the one that all work is delegated to from the simpler parseObject(String) version. You have to implement it.
  2. The contract again, annoyingly, forces us to throw aNullPointerException if there's noParsePosition.
  3. Here I check to see if the error index on the suppliedParsePosition has already been set. If it has, then it's not clear to me what I should do (the contract is silent; thanks, IBM!). I err on the side of being conservative and, since the parameter indicates that there's an error in play already, I returnnull per that part of the contract.
  4. I can't parse any text starting at a position less than zero, so we set the errorIndex of the parsePositionhere and return null as instructed.
  5. If the supplied text is null, then I interpret this to mean that the user would like a null URI in return
See if this little scenario sounds familiar.
You're rolling along on some application somewhere, and you've decided to put some information in a Propertiesfile somewhere. You realize that you're beginning to encode a lot of information in a property setting, so much so that you realize that really what you're doing is building up a rather complicatedObject. You feel like you've done this before.
Or this one:
You're working on a Swing application, and you need to validate the input from the user. Great, you say, I'll use aJFormattedTextField, and then I'll...I'll...I'll...read the...documentation...which features lots of...hmm...factories...and AbstractFormatters...and still more formats and navigation thingees and...I think I'll go get some coffee.
Or this one:
You're halfway through developing a complicated and enterprisey validation framework and you stop abruptly, realizing that there has to be a better way!
Starting with this blog entry I'd like to cover the many, many different ways to edit, format and build up different kinds ofObjects that are provided by the Java platform.

Why should you think about turning Objects intoStrings? Or Strings into Objects? Or all the other ways that a user might send input to you?

For me, the answer is that whether you're developing on the desktop or on the Web, you are constantly accepting free-form user input in the form of text. In some cases, you have control over this text, and in other cases you do not, but in all cases you often need to turn that text into things like dates, colors, fonts,java.net.URIs, custom domain objects and the like. Wouldn't it be nice to come up with a standard set of tools that would manage this kind of conversion for you in a pluggable manner? Wouldn't it be even better if most of that heavy lifting were done for you by the base platform? Well, it is.

In this entry, I'll cover java.beans.PropertyEditor

Child number two has kept me away from the blogosphere. As I write this, señor Nelson Jr. is busily learning how to access and destroy objects. Next up: garbage collection.

I found this article on JavaBlogs the other morning and while technically speaking it's an old topic, it seems one that none of us ever tire of talking about. If you tire of talking about it, then you'll probably want to move on to another blog right about now.

Briefly summarized, the article points out that many domain objects are beefed up incorrectly in order to ostensibly avoid certain pejorative labels coined by certain well-known prolific industry visionaries. Specifically, the beef often takes the form of data access methods which, as the poster wonders out loud, are usually supposed to be whisked off to some data access object, are they not? So if you move them, what are you left with? The article examines a few cases of what he considers business methods, explores some of their wrinkles and ends, rather appropriately, in my opinion, with a semi-frustrated open-ended question:

Aren't domain objects with persistence logic just really Enity [sic] Beans? Didn't we agree Entity Beans are bad? Let's keep the persistence out of the domain object and in the Dao where it belongs.


 

I think this article raises a point that we all think about when setting out to design a class library or model a domain: whatis the behavior, anyway? Most modeling and design exercises I've been involved with typically start with the structure. Members of the design or development team sit around and name domain objects and their attributes following good object-oriented practice. Deep thought ensues. More attributes are added, some are taken away. Classes are refactored. At the end of the day you have a glorified entity-relationship diagram, redubbed a class diagram, or, to use Fowler's term, a pile of anemic domain objects. Then the team sprinkles behavior over the objects, and, just as CodeMonkey indicates, more often than not it is simply pick-it-up-put-it-down logic: Account.findByDate(), Order.update(), etc.

I used to think that getting the structure right was critically important. Think deeply about your objects, make sure they have all the attributes they're going to need, move things around in the class hierarchy appropriately to reduce duplication, and there, you have your structure onto which you can drape the domain's behavior logic. Now I think this is wrongheaded (to paint it in overly black-and-white terms), and I have found that examining the behavior is a much better way to arrive at the structure. If you do this, the business logic falls out naturally.

To start with, think about some obvious but often neglected questions: for any given object with a proposed attribute x, why must that object have attribute x? What little-"p" process needs that x? Why, really, do I have to know an order's creation date? Why do I need to know a student's age?

I've found that the answers take one of three rough forms:

  1. Because someone somewhere is going to want to build some sort of general-purpose query that will involve x. We can't even begin to predict what this query will be.
  2. Because we need to make this attribute available for reports that we send to An Important Organization, Standards Body or Governmental Agency. Who knows why; that's just what we do.
  3. Because there is a business rule that dictates that things with an x in some range or meeting some criterion need to get flagged in a particular way, or exported, or associated with some other kind of thing.


 

The first item is probably the only case where you really simply have to expose your object's attributes via public getters and setters Because They Simply Have To Be There. I've also found that it's pretty rare. And finally, it also follows from this that it doesn't really matter whether your domain object has an x, because it might as well have a y, or a z, or forty three other attributes. The point is if an attribute is exposed simply for dynamic query purposes, then it hardly matters what that attribute is for, since your program isn't going to do anything with it other than show it to the person who constructed the query in the first place. So if you're designing with interfaces, keep these types of getters and setters out of your interface and put them in the underlying implementation.

The second item is fairly common. In higher education, which is the domain in which I currently find myself, the government has an interest in various kinds of data and reports that, to my eyes, consist of various combinations of obscure, unpronounceable "codes". Your first inclination would be to stop there (well, mine is): OK, the government says we need to provide a CIP code, so we'd better have a getCIPCode() method on our Program object. But stop there, and ask again: why do we need this? Maybe the correct method is, instead,provideGovernmentMandatedData(), or maybe the solution is to move these kinds of obscure, for-reports-only attributes into an outboard descriptor object, e.g.CipCodes.getFor(program).

The remaining item above is the killer. Suppose you intuitively know your Student object has to have an ageattribute. Why? What's it used for? Why do you need to know how old a student is? In a lot of scenarios, the checking of an attribute is really something else in disguise. The business rule, in other words, may be if the student does not meet state requirements for taking a class, then reject him. If that really is the business rule, then you don't really need an age accessor at all; just a meetsStateRequirementsForTakingAClass() method.

In practice, of course, returning to planet Earth, you end up having domain objects that have a mix of getters and setters and more object-oriented business methods. But I've found that arriving at these methods by way of thinking about the processes that are going to need them tends to result in cleaner, less-coupled objects than the alternative approaches that I've tried.

Thanks for reading.

Long, long, long time no post. A job change and a two-year-old will do that to you.

On today's menu: how to make a JRootPane subclass that can pop itself in and out of JInternalFrames andJFrames. Let's dive right in.

Background

Different people have different ideas on how an application should work when it can open many different documents of many different types at the same time, and, most importantly, when its value derives from being able to see those documents side-by-side (ruling out, for example, a JTabbedPane-based interface). Some like a true, old-school MDIfeel; others like there to be multiple external windows, à la Microsoft Word. My current project's customers are evenly divided on the subject.

To make everyone happy, I decided to start with a JDesktopPane-based MDI core. Then I added the ability to detach the JInternalFrames from the desktop pane and open them up as external windows—i.e. to make the window hop off theapplication desktop and onto the user desktop (and, potentially, back again). This comparatively small insight has led to a surprisingly intuitive way of managing the application's information. When you just want to look at one thing and have it be your central focus, it is easier to have it be in an external window. On the other hand, when you want to look at two things side by side, it seems easier to grasp their relationship to each other and to their containing application if they're presented inside an MDI.

The Code

The final code is available here under the MIT license; my ramblings below detail how I designed it.

The Gory Details

Since both a JInternalFrame and a JFrame areRootPaneContainers, I focused on the JRootPane class. If there were some way to pop instances of this class out of one kind of window and into another kind—preferably via the usual ActionMap machinery—I'd be in business.

If you look at the APIs for JFrame and JInternalFrame, you can see that there is not a publicly accessible way to do this. In general, this is a good thing—how many times (other than this one!) do you ever even think about the root pane let alone want to remove it or set a new one? While there is a public method for retrieving the root pane on both the JFrameand JInternalFrame classes, there is no such public setter method. So I knew that overriding JRootPane would be in order. I called myJRootPane subclass DetachableRootPane.

Implementing the detach() method

Next, I focused on what would become this subclass'detach() method. What would the state of this root pane be at the moment of detaching? What aspects of that state would be worth preserving, and what aspects would be worth throwing away?

This turned out to be a bit of a thorny problem. If aJInternalFrame is maximized, and then detached, should the resulting JFrame be maximized? Or should it be the same size on screen as the maximized JInternalFrame it "came from"? If the JInternalFrame were not resizable, then should the resulting JFramealso not be resizable? What about minimizedJInternalFrames? Should they be detachable at all?

Anyhow, at this stage in design I didn't focus too much on the answers to these questions, but more on the fact that moving aDetachableRootPane from a JInternalFrame to aJFrame and back again would require certain elements of state to be carried through the detaching process. The easiest thing to do, I figured, was to store this state using the normal client property mechanics.

Pass 1: Get the Basics Working

So, the detach() method began taking shape. Here's more or less what it looked like at this stage in the design, where I was focusing on the going-from-a-JInternalFrame-to-a-JFrame detaching process. Note that the code below is intended to represent where I was in the design process at the time; as such, it may not be correct or complete:

public void detach() {
  final Component parent = this.getParent();
  if (parent instanceof JInternalFrame) {
    // Leaving JInternalFrame; going into JFrame
    final JInternalFrame internalFrame = (JInternalFrame)parent;

    // Grab the JDesktopPane and stash it away because if we "go back" we need
    // to tell it to add() whatever JInternalFrame we "go back" to.  Note that if
    // abused, this could be the source of a memory leak.  There's probably 
    // a way to make this more robust.  In short, don't abuse it.
    final JDesktopPane pane = internalFrame.getDesktopPane();
    if (pane != null) {
      pane.remove(internalFrame);
      this.putClientProperty("desktop", pane);
    }
    
    // Store some state about the JInternalFrame we're "leaving" so that if we 
    // "come back" we can set up the new JInternalFrame just like the old one.
    // Resizability is tricky and isn't yet handled; more on this later.
    this.putClientProperty(INTERNAL_FRAME_CLOSABLE, Boolean.valueOf(internalFrame.isClosable()));
    this.putClientProperty(INTERNAL_FRAME_MAXIMIZABLE, Boolean.valueOf(internalFrame.isMaximizable()));
    this.putClientProperty(INTERNAL_FRAME_ICONIFIABLE, Boolean.valueOf(internalFrame.isIconifiable()));

    internalFrame.setVisible(false);

    // Create the new JFrame we're going to detach "into".
    final JFrame frame = new JFrame(internalFrame.getTitle()) {
      protected final void frameInit() {
        super.frameInit();
        this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        this.addWindowListener(new WindowAdapter() {
          public final void windowClosing(final WindowEvent event) {
            // Make it so that closing the external window causes us to "go
            // back into" a JInternalFrame.
            detach();
          }
        });
      }
      protected final JRootPane createRootPane() {
        return DetachableRootPane.this;
      }
    };

    // More on this later; we actually want to set bounds explicitly, but at
    // this stage in the design we'll punt.
    frame.pack();
    frame.setVisible(true);

  } else if (parent instanceof JFrame) {
    // Leaving JFrame; going into JInternalFrame
  }
}


 

At this stage, I had a DetachableRootPane that was capable of popping out of a JInternalFrame into aJFrame if its detach() method were ever called. The details to be worked out were:

  • Going the other way, i.e. detaching from a JFrame"into" a JInternalFrame
  • Setting the bounds properly so that the JFrame would appear to simply "drop into" the JDesktopPane as aJInternalFrame, and so that the JInternalFramewould appear to "pop off" the application desktop and onto the user's desktop
  • Miscellaneous state-related tracking behavior, like making sure that we monitor a JInternalFrame's resizability so that we know how to create a new one that looks just like it


 

Pass 2: Make Detaching Work Both Ways

The first thing I did was to make it so that theDetachableRootPane could go the other way. Here is the section of code from the detach() method that dealt with this issue. Again this is supposed to illustrate the design process more than it is supposed to be compilable, correct code:

    } else if (parent instanceof JFrame) {
    // Leaving JFrame; going into JInternalFrame
    final JFrame frame = (JFrame)parent;

    // See if someone (usually us) has put a JDesktopPane into our client
    // property map.  If so, then we have the required parent component to add a
    // new JInternalFrame to.  If not, well, then we're up a creek, but handle
    // that gracefully too.
    final JDesktopPane pane = (JDesktopPane)this.getClientProperty("desktop");

    final String title = frame.getTitle();

    frame.dispose();

    if (pane != null) {
      // There is indeed a JDesktopPane to add a new JInternalFrame to, so do
      // it.

      final JInternalFrame internalFrame = 
        new JInternalFrame(title,
                           true, // punt on resizability for now
                           ((Boolean)this.getClientProperty(INTERNAL_FRAME_CLOSABLE)).booleanValue(),
                           ((Boolean)this.getClientProperty(INTERNAL_FRAME_MAXIMIZABLE)).booleanValue(),
                           ((Boolean)this.getClientProperty(INTERNAL_FRAME_ICONIFIABLE)).booleanValue()) {
          protected final JRootPane createRootPane() {
            return DetachableRootPane.this;
          }
        };
      pane.add(internalFrame);

      // TODO: we'll worry about bounds in our next pass
      internalFrame.pack();

      internalFrame.setVisible(true);
    } // end of if block
  } // end of method


 

Brief Interlude: Pull In the ActionMap

At this stage in the process, I had aDetachableRootPane that could quite comfortably spawnJFrames and JInternalFrames and hop in and out of them at will, provided that the detach() method could actually be invoked. I also had a test rig that was simply calling the detach() method on a timer. This was proving to be really annoying now that the basic functionality was there. So instead of diving into cleaning up the details, I decided to work on the ActionMap- and InputMap-related code. So, in the constructor for DetachableRootPane I did this:

public DetachableRootPane() {
  super();
  final ActionMap actionMap = this.getActionMap();
  assert actionMap != null;
  actionMap.put("detach", new AbstractAction("Detach") {
      public final void actionPerformed(final ActionEvent event) {
        detach();
      }
    });
  final InputMap inputMap = this.getInputMap();
  assert inputMap != null;
  inputMap.put(KeyStroke.getKeyStroke("F2"), "detach");
}


 

Now when I pressed F2 on either aJInternalFrame or a JFrame (provided, of course, its root pane was an instance of DetachableRootPane), the frame would detach appropriately and move from one desktop to the other.

Pass 3: Get the Bounds Working

Finally it was time to go back and worry about the bounds.

Going from a JInternalFrame in a JDesktopPaneto the screen was easy enough. All I had to do was call the getLocationOnScreen() method on the "outgoing"JInternalFrame and set the bounds of the newJFrame accordingly. That part of the detach method now looked (in part) like this:

      // internalFrame is the JInternalFrame that this DetachableRootPane is
      // "leaving".  Grab its *screen* location and set the bounds of the new
      // JFrame to those bounds.  The effect is of a seamless detaching from the
      // JDesktopPane.
      final Point point = internalFrame.getLocationOnScreen();
      assert point != null;
      bounds.x = point.x;
      bounds.y = point.y;

      // ...other code; see above in this weblog post...

      frame.setBounds(bounds);


 

Going the other way was more complicated and required me to wrap my head around some of the more arcane-looking methods in SwingUtilities. What I wanted to have happen was if someone closed the JFrame I wanted its newJInternalFrame to appear in the JDesktopPane at exactly the location "underneath" the JFrame that had just been closed. After mucking around, I uncovered the convertPointFromScreen() method. The general approach, then, was to grab the screen location of the "outgoing"JFrame and convert it to the JDesktopPane's coordinate space. That part of the detach() method looked like this:

      // ...other code...

      final Rectangle bounds = frame.getBounds();
      frame.dispose();
      if (pane != null) {
        final JInternalFrame internalFrame = 
          new JInternalFrame(title,
                             true, // punt on resizability for now
                             ((Boolean)this.getClientProperty(INTERNAL_FRAME_CLOSABLE)).booleanValue(),
                             ((Boolean)this.getClientProperty(INTERNAL_FRAME_MAXIMIZABLE)).booleanValue(),
                             ((Boolean)this.getClientProperty(INTERNAL_FRAME_ICONIFIABLE)).booleanValue()) {
            protected final JRootPane createRootPane() {
              return DetachableRootPane.this;
            }
          };
        pane.add(internalFrame);
        final Point point = new Point(bounds.x, bounds.y);
        SwingUtilities.convertPointFromScreen(point, pane);
        bounds.x = point.x;
        bounds.y = point.y;
        internalFrame.setBounds(bounds);
        internalFrame.setVisible(true);


 

Pass 4: Cleaning Up and Putting It All Together

This looked pretty good. F2 on aJInternalFrame caused the frame to pop off onto the user's desktop as a JFrame at exactly its previous location on screen. F2 again caused that same JFrame to sink down onto the JDesktopPane wherever it happened to be.

The last bit of work to do involved the resizability of the original JInternalFrame. It turns out that when aJInternalFrame is maximized, it effectively sets itsresizable property to false. This is a bug, because of course there are two semantic properties at work here: the current resizability of the JInternalFrame, and the structural resizability of the JInternalFrame. It is true that when a frame of any kind is maximized, typically you can't resize it, but that says nothing about whether it's resizable once it has returned to its normal bounds. To put it in terms of this project, I wanted to monitor the structural resizability of the JInternalFrame and monitor it for any change; that way I could store that property's value accurately in my DetachableRootPane for use later—e.g. if theDetachableRootPane detaches into a JFrame and back, when it comes back we want to make sure that theJInternalFrame it comes back into is indistinguishable from the JInternalFrame it left.

This, fortunately, sounds like a simple job for a PropertyChangeListener. The extra wrinkle is that we want to manage the PropertyChangeListener in theDetachableRootPane, but install it on theDetachableRootPane's parent. Because the parent has to be present for this to work, we can't do the installation work in theDetachableRootPane constructor; we have to do it in the addNotify() method instead. This means we have to be careful to also remove it in the removeNotify() method:

  private boolean parentIsResizable;
  private PropertyChangeListener pcl;

  public void addNotify() {
    super.addNotify();
    final Container parent = this.getParent();
    boolean addPropertyChangeListener = true;
    if (parent instanceof JInternalFrame) {
      final JInternalFrame parentFrame = (JInternalFrame)parent;
      this.parentIsResizable = parentFrame.isResizable();
    } else if (parent instanceof JFrame) {
      final JFrame parentFrame = (JFrame)parent;
      this.parentIsResizable = parentFrame.isResizable();
    } else {
      addPropertyChangeListener = false;
    }
    if (addPropertyChangeListener) {
      this.pcl = new PropertyChangeListener() {
          public final void propertyChange(final PropertyChangeEvent event) {
            if (event != null && "resizable".equals(event.getPropertyName())) {
              final Boolean value = (Boolean)event.getNewValue();
              parentIsResizable = value != null && value.booleanValue();
            }
          }
        };
      parent.addPropertyChangeListener("resizable", this.pcl);
    }
  }

  public void removeNotify() {
    if (this.pcl != null) {
      final Container parent = this.getParent();
      assert parent != null;
      parent.removePropertyChangeListener("resizable", this.pcl);
    }
    super.removeNotify();
  }


 

Then I went back and amended the parts of my detach()method that "punted" on resizability to take into account the real value of the property:

        if (parent instanceof JInternalFrame) {
          // ...other code...
          this.putClientProperty(INTERNAL_FRAME_RESIZABLE, new Boolean(this.parentIsResizable));
          // ...other code...
        } else if (parent instanceof JFrame) {
          // ...other code...
          final JInternalFrame internalFrame =
            new JInternalFrame(title,
                               ((Boolean)this.getClientProperty(INTERNAL_FRAME_RESIZABLE)).booleanValue(),
                               //...
        }


 

Finally, as my last bit of cleanup, I added a fewstatic methods for creating both JFrames andJInternalFrames with their root panes set to an instance of DetachableRootPane. Here's one example:

  public static JInternalFrame createJInternalFrame(final String title, final boolean resizable, final boolean closable, final boolean maximizable, final boolean iconifiable) {
    return new JInternalFrame(title, resizable, closable, maximizable, iconifiable) {
        protected final JRootPane createRootPane() {
          return new DetachableRootPane();
        }
      };
  }


 

The Wrapup

This was a fun exercise, largely because it was well-bounded, solved a real UI problem, and is really quite reusable in the large. My respect for the Swing architects who carefully allowed me to get this much rope has just gone up another notch.

Thanks for reading, and I'll see you at JavaOne.

Here's a fun hack.

I (like everyone else in the world) have a collection of utilities I take with me from job to job. It's served me well for several years now. These utilities are simple, domain-independent things: classes to copy files, find out what jar file a class is loading from, that sort of thing.

Many of the classes in this utility collection jar havemain() methods, and could conceivably serve as the Main-Class in an executable jar somewhere. But I don't want to get into the business of fragmenting my utility jar. What to do?

Feeling hackish, I put together a class that itself is installed as the Main-Class in a jar file, but which consults a file (also in the jar) to let it know what class to actuallyuse as the main class. The hackish part is that all you have to do is rename the jar file for the "right" Main-Class to be selected. Here's the (quick, dirty, uncommented) code:

import java.io.InputStream;
import java.io.IOException;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import java.net.URL;

import java.util.Properties;

public final class Main {

  private Main() {
    super();
  }

  public static final void main(final String[] args) throws Throwable {
    final URL location;
    final String classLocation = Main.class.getName().replace('.', '/') + ".class";
    final ClassLoader loader = Main.class.getClassLoader();
    if (loader == null) {
      location = ClassLoader.getSystemResource(classLocation);
    } else {
      location = loader.getResource(classLocation);
    }
    String token = null;
    if (location != null && "jar".equals(location.getProtocol())) {
      String urlString = location.toString();
      if (urlString != null) {
        final int lastBangIndex = urlString.lastIndexOf("!");
        if (lastBangIndex >= 0) {
          urlString = urlString.substring("jar:".length(), lastBangIndex);
          if (urlString != null) {
            final int lastSlashIndex = urlString.lastIndexOf("/");
            if (lastSlashIndex >= 0) {
              token = urlString.substring(lastSlashIndex + 1);
            }
          }
        }
      }
    }
    if (token != null) {
      InputStream stream = null;
      try {
        if (loader == null) {
          stream = ClassLoader.getSystemResourceAsStream("mainClasses");
        } else {
          stream = loader.getResourceAsStream("mainClasses");
        }
        if (stream != null) {
          final Properties properties = new Properties(System.getProperties());
          properties.load(stream);
          final String mainClassName = properties.getProperty(token + ".main.class");
          if (mainClassName != null) {
            final Class mainClass = Class.forName(mainClassName);
            if (mainClass != null) {
              final Method mainMethod = mainClass.getDeclaredMethod("main", new Class[] { args.getClass() });
              if (mainMethod != null && Void.TYPE.equals(mainMethod.getReturnType())) {
                final int modifiers = mainMethod.getModifiers();
                if (Modifier.isPublic(modifiers) &&
                    Modifier.isStatic(modifiers)) {
                  try {
                    mainMethod.invoke(null, new Object[] { args });
                  } catch (final InvocationTargetException kaboom) {
                    throw kaboom.getTargetException();
                  }
                }
              }
            }
          }
        }
      } finally {
        if (stream != null) {
          try {
            stream.close();
          } catch (final IOException ignore) {
            // ignore
          }
        }
      }
    }
  }

}


 

There are two interesting bits to this. The first is the way that a class' location is returned as a URL. Suppose you have a class, com.foo.bar.Baz. And suppose that class is present in a jar file whose (Windows, let's say) path isC:\x\y\z.jar. Then if you say:

URL url =
classLoader.getResource("com/foo/bar/Baz.class");
...the URL you'll get back is: 
jar:file:C:/x/y/z.jar!/com/foo/bar/Baz.class
This format, which is documented as part of the java.net.JarURLConnection class, is remarkably useful. The URL format, as you can see, gives you both the directory structure of the jar file itself, as well as the location of the jar file. In the code above, we use this URL format to figure out what the unqualified name of the jar file is--the "basename": 
String urlString = location.toString();
if (urlString != null) {
  final int lastBangIndex = urlString.lastIndexOf("!");
  if (lastBangIndex >= 0) {
    urlString = urlString.substring("jar:".length(), lastBangIndex);
    if (urlString != null) {
      final int lastSlashIndex = urlString.lastIndexOf("/");
      if (lastSlashIndex >= 0) {
        token = urlString.substring(lastSlashIndex + 1);
      }
    }
  }
}
Here we simply lop off everything from the "jar:" part up to and including the last slash (well, the last slash before the "!" delimiter). Then we lop off everything from (and including) the "!" delimiter to give us the "basename" of the jar file, which, in our contrived example, would simply bez.jar.

 

Armed with that as a key, we now go looking for a well-known file inside the jar. I simply called it "mainClasses". It is a simple Properties dump, and is accessible via the usual getResourceAsStream() machinery:

stream =
loader.getResourceAsStream("mainClasses");
(If you want to be really careful, you would actually open the jar file yourself and extract this entry from it. The problem with the way we've done it here is that if there is any file in the classpath named mainClasses that appears before the jar file in question, it will be selected instead.)

 

If we found it, we load a new Properties object from it, where, hopefully, the keys are jar "basenames", and the values are fully qualified names of classes, each of which will serve as that jar file's Main-Class. Here's an example of the contents of that file:

z.jar: com.foo.bar.Xyzzy
q.jar: com.foo.bar.Frobnicator
This file indicates that if the jar is named z.jar, then its main class will be com.foo.bar.Xyzzy. If, on the other hand, the jar is named q.jar, then its main class will be com.foo.bar.Frobnicator.

 

The rest of the code is simply the process of loading the correct class via reflection and seeing if it has a public static void main(String[]) method on it. Note that the only kind of Exception we explicitly catch isInvocationTargetException so that if something goes wrong in the delegate main class, it will look (as much as possible) as though that class were directly invoked.

Obviously this dirty little hack will only scratch an itch if it makes sense to copy the jar file to multiple places with different names. In my case it does, because my utility jar file is very small.

Hello, first of all. It's an honor to be part of the java.net weblogging community.

A discussion concerning component reuse brought me back to my philosophy major days in college. Who knew British empiricism could help you with object design?

The discussion was (is) about, loosely, how best to design an object or a component for reuse. You want to get the hypothetical object down to what's important across domains, to trim away the fat, but, as a competing concern, to make it rich enough that people won't have to reinvent the wheel. Slimming down your object also provides incentive for someone else to use it, provided you don't slim it down so far that it's impractical.

What is this "fat" we're talking about? In general, it's concerns or aspects or facets of the real-world thing that our object is modeling. There are many facets about a hypothetical and reusable Person object (and the set of all persons that it models), for example, that could be included, ranging from names to measurements to relationships with others. Which of these groups of attributes is necessary for a Person to be aPerson? Which is superfluous? What is thePerson apart from these groups of attributes?

John Locke examined these issues in An Essay Concerning Human Understanding. Ruthlessly paraphrased, and butchered somewhat for this weblog's purposes, he argues that when you hear someone mention a person, you frame a "complication or collection" of "simple ideas" in your mind that "go constantly together", such as height, weight, general shape, etc. In Locke's view we humans in the normal unexamined course of our lives don't imagine these ideas simply existing on their own, so we subconsciously posit the existence of a "substratum" that they bind to, although strictly speaking we never entirely know what we're talking about. Locke calls this substratum substance, and, believe it or not, it's one way of thinking about object reuse, whether it exists or not.

A couple of choice quotes from Locke on this whole subject:

If any one should be asked, what is the subject wherein colour or weight inheres, he would have nothing to say, but the solid extended parts; and if he were demanded, what is it that solidity and extension adhere in, he would... [answer] SOMETHING, HE KNEW NOT WHAT.
...[O]ur...ideas of substances...have always the confused idea of something to which they belong...and therefore when we speak of any sort of substance, we say it is a thing having such or such qualities; as body is a thing that is extended, figured, and capable of motion; spirit, a thing capable of thinking; and so hardness, friability, and power to draw iron, we say, are qualities to be found in a loadstone. These...intimate that the substance is supposed always SOMETHING BESIDES the extension, figure, solidity, motion, thinking, or other observable ideas, though we know not what it is.


 

Let's apply Locke to object design and see where it leads. Suppose he's right: a person is simply a collection of related attributes or facets that adhere to some substratum. Let's model this explicitly.

Backing up for a second for contrast, the normal way you would design a domain-independent Person object, assuming for the moment you could, would be to pick some attributes that you thought were sufficiently general and important to be included in this superclass (and here's where you run into problems). Here, for the sake of brevity, is my silly offering:

public class Person {
  
  // My selection for an attribute that is important enough to
  // be included in this reusable class.  Yours could be different.
  // Of course that means we'll probably end up not reusing each other's
  // classes.  Hmm.
  private String name;

  public Person() {
    super();
  }

  public String getName() {
    return this.name;
  }

  public void setName(final String name) {
    this.name = name;
  }

  // etc.

}


 

Of course, picking what those attributes are is what makes reusable object design so tough.

Locke, if he were an object designer, would be a rebel. He would probably start with the attributes--the collections of simple ideas--and graft them on to some simple "substance" object that would be deliberately designed to be more or less inscrutable. As it turns out, this is a much better way to achieve object reuse across domains. Maybe an extreme version of his code would look something like this:

public interface IKnowNotWhat {
  // If only we knew what it was!
}

public class Person implements IKnowNotWhat {

  public Person() {
    super();
  }

}

public abstract class CollectionOfSimpleIdeas {
    
  protected final IKnowNotWhat substance;

  public CollectionOfSimpleIdeas(final IKnowNotWhat substance) {
    super();
    this.substance = substance;
    if (substance == null) {
      throw new BritishEmpiricismMisunderstoodException();
    }
  }

  public IKnowNotWhat getSubstance() {
    return substance;
  }

}

public class PersonNames extends CollectionOfSimpleIdeas {
  
  private String familiarName;

  public PersonNames(final IKnowNotWhat substance) {
    super(substance);
  }

  public String getFamiliarName() {
    return this.familiarName;
  }

  public void setFamiliarName(final String familiarName) {
    this.familiarName = familiarName;
  }

  // etc.

}

public class PersonMeasurements extends CollectionOfSimpleIdeas {
    
  private int heightInInches;

  public PersonMeasurements(final IKnowNotWhat substance) {
    super(substance);
  }

  public int getHeight() {
    return this.heightInInches;
  }

  public void setHeight(final int height) {
    this.heightInInches = height;
  }

  // etc.

}


 

The key insight here is that people aren't interested in a reusable Person object at all. They are interested in different combinations of facets that all concern a person in a particular domain. A financial services programmer doesn't care about a person's weight, but that might be very important to the programmer of an insurance system. Explicitly modeling a person this way--inside out--lets each domain of attributes stand alone, but also lets them play nicely together when bound together. What's important is that the attributes are reused, not the substratum.

All of this philosophy has made my brain hurt. I'm going to go get some lunch (although I know not what).

Filter Blog

By date: