Today I finally decided to see if I could find out why our suite of EO unit
tests kept failing on Windows while passing on Mac OS X and Linux (don't ask
me why I'm using Windows).
All our EO unit tests have a common parent class that has a @BeforeClass
method that makes sure the EOModel has been loaded (because in some
circumstances - such as when being run as part of a build, the model wasn't
loaded when our unit tests needed it). This method attempts to load the
model if it hasn't been loaded by first calling:

EOModelGroup.defaultGroup().modelNamed("BLAH");

to see if it has been loaded and then manually loads the group if it isn't
there. The problem was that on Windows EOModelGroup.defaultGroup() was
throwing an exception:

java.lang.IllegalArgumentException: Attempt to insert null into an
com.webobjects.foundation.NSMutableArray.
at
com.webobjects.foundation.NSMutableArray.addObject(NSMutableArray.java:239)
at
com.webobjects.eoaccess.EOModelGroup.modelGroupForLoadedBundles(EOModelGroup.java:700)
at
com.webobjects.eoaccess.EOModelGroup.globalModelGroup(EOModelGroup.java:306)
at com.webobjects.eoaccess.EOModelGroup.defaultGroup(EOModelGroup.java:333)
at
com.bbc.fmtj.cps.enterprise.eo.CPSEnterpriseTestCase.loadModel(CPSEnterpriseTestCase.java:283)

The 'attempt to insert null' was happening because the
'modelGroupForLoadedBundles()' method tries to make a list of all the loaded
bundles by first adding the 'NSBundle.mainBundle()' to a list and then
adding all of the 'NSBundle.frameworkBundles()'. The problem is that when
running a unit test on Windows NSBundle.mainBundle() returns null and
'modelGroupForLoadedBundles()' doesn't bother to check for null before
adding the 'main bundle' to the list of 'loaded bundles'.

So - why does NSBundle.mainBundle() return null on Windows but not on OS X?
- Good question! I don't pretend to understand the mystery that is NSBundle,
but here's what I think is happening.

NSBundle has a bunch of static initialisation code that runs when the
NSBundle class is loaded. This initialisation code first tries to load
(old-style .framework I assume) 'bundles' from a directory specified by the
'webobjects.user.dir' property. If this property has not been set, it then
looks in the current application's directory. If it is unable to load any
'bundles' from these locations it then asks the Java ClassLoader for a list
of URLs to all the Info.plist files found inside of any .jars that are on
the classpath.

Now I haven't 'installed' WebObjects on my Windows box (this seems like an
out-dated concept to me - I should just be able to reference the .jars) so
it is this final method of 'bundle loading' that I'm relying on. The problem
is - it is broken on non-unix platforms.

Once NSBundle has got a list of URLs to all the Info.plist files it tries to
turn this in to a list of paths to the .jar files containing the Info.plist
files.

For example, it wants to turn this:
jar:file:/C:/Documents%20and%20Settings/.../JavaEOAccess-5.4.2.jar!/Resources/Info.plist

in to this:
C:\Documents%20and%20Settings\...\JavaEOAccess-5.4.2.jar

The problem is that the method that tries to do this
(__exctractStringFromURL) isn't quite right. The
current implementation returns a String like this:
/C:/Documents%20and%20Settings/.../JavaEOAccess-5.4.2.jar
(note the leading slash and that the slashes are the wrong kind)

Later on in the initialisation, NSBundle tries to 'normalise' this path
before attempting to load the bundle from the path. When the
'NormalizeExistingBundlePath()' method is passed the malformed file path, it
attempts to create a new File object - using the malformed file path and as
a result the 'normalised' path is null.

Finally, NSBundle tries to load the actual bundle from the given path and
obviously fails to load anything from a null path. Hence no 'bundles' are
loaded at all - which begs the question - how does WebObjects even work on
Windows at all if NSBundle isn't working? (and I don't know the answer to
that question).

Fixing this should be simple. __exctractStringFromURL() needs to be
re-written to return a proper (platform appropriate) file path (perhaps by
creating a URI using the trimmed path component of the original jar: URL and
then creating a File using this URI and asking it for its getPath()).

In the meantime, I think I can work around this problem in my code by
calling NSBundle._bundleWithPathShouldCreateIsJar() myself to load all of
the bundles that NSBundle's initialisation code should've loaded anyway -
and then manually set the 'main bundle' by calling
NSBundle._setMainBundle().

So, now I've gotten to the bottom of this - I have two questions:

1. Can anyone explain why WebObjects works on Windows when I deploy a .WAR
containing all the libraries as .jars given that NSBundle seems broken on
Windows?

2. Shall I bother submitting a bug report via Radar (given that Mike fixed
the last bug I found before I'd managed to finish writing the bug report)?

Thanks,

Jake
 _______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list      (Webobjects-dev@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to