[jsword-devel] ActionFactory and ResourceBundles

Joe Walker jsword-devel@crosswire.org
Mon, 05 Apr 2004 08:36:35 +0100


Hi,

It looks good. It is a better approach than I've used so far, I think.
The only reason I've not applied it is that I'm trying to get a 0.97 
done. Which has proved to be bizarrely complex.
Just as soon as I've managed to get the version numbers sorted I'll 
apply it.
Sorry for the delay.

Joe.

DM Smith wrote:
> I have created a few new classes:
> ActionFactory, creates actions from a ResourceBundle
> CWAction, a custom action that implements delegation
> CWClassLoader, a custom class loader needed to load resource bundles
> 
> I used CW as an abbreviation for CrossWire (I could not figure out what 
> the prefix Eir meant. But I guess that it was from another project.)
> 
> I chose to implement independent classes so that they could be evaluated 
> without affecting existing code.
> 
> ActionFactory and CWAction are as I described them earlier.
> 
> To use ActionFactory create one after calling Project.instance(). This 
> allows for overrides to be gotten from ~/.jsword.
> 
> Since it is not attached to the program, to see its behavior set 
> breakpoints in ActionFactory and step away to your heart's content.
> 
> I initially started with Properties and using ResourceUtil worked like a 
> charm. It would load resources from files located in resource.jar named 
> a.b.c.xxx.properties or from a jar along a path a/b/c/xxx.properties.
> 
> However, ResourceUtil is fine for directly loading resources into 
> properties, but ResourceBundle uses its classloader to find and get 
> resources. And it tries for more than one resource. It also accepts 
> calls of the form "ActionFactory" and 
> "org.crosswire.common.swing.ActionFactory" to load the same file when 
> called from org.crosswire.common.swing classes.
> 
> But it chokes on absolute paths. The reason is that ResourceBundle 
> interprets the names and then calls on its classloader in such a way 
> that cannot find them. The net effect of this is that without providing 
> a custom classloader, the resource had to be in the same directory as 
> the class.
> 
> Bummer.
> 
> I figured we wanted to have a file 
> org.crosswire.common.swing.ActionFactory.properties in the resource 
> directory and that a developer could place a copy of it in ~/.jsword and 
> modify it.
> 
> This required a custom classloader. In reading up on Java 2 class 
> loaders, I found that it is only necessary to override findResource. I 
> also wanted to use the calling classes getResource if possible. With 
> some more reading I found a technique to get the call context. [ I still 
> need to test this in a WebStart environment to see if it works. If not I 
> need to going back to passing the calling class into the constructor for 
> the custom class loader ]
> 
> At first I used ResourceUtil to get the resources, but I found some 
> scenarios that it would not handle. The biggest was getting a 
> ResourceBundle for a class by simple name (e.g. "ActionFactory").
> When I did this it expanded it in a way that ResourceUtil was not set up 
> to handle. And then the form org.crosswire.common.swing.ActionFactory 
> was re-interpreted into that same form. I started to add code to 
> ResourceUtil, but ripped it back out. I did leave in some internal 
> refactoring that makes the class more self documenting.
> 
> I am using ResourceUtil.getHomeResource from the new classloader.
> 
> To use the classloader pass it as the third argument to 
> ResourceBundle.getBundle.
> I did it like this:
> ResourceBundle resources =
>    ResourceBundle.getBundle(getClass().getName(),
>         Locale.getDefault(),
>         new CWClassLoader());
> 
> _________________________________________________________________
> Tax headache? MSN Money provides relief with tax tips, tools, IRS forms 
> and more! http://moneycentral.msn.com/tax/workshop/welcome.asp