Re: Runtime Error: how to find cayenne.xml (LONG)

From: Holger Hoffstätte (holge..izards.de)
Date: Wed Feb 26 2003 - 16:13:07 EST

  • Next message: Andrus Adamchik: "ObjectStyle Servers Maintenance"

    I've spent some time thinking about the current situation with config
    setup and have some things to share. Please excuse the long-winded mail.

    The orginal problem by Paul:

    > >>fault. ClassLoader.getResource() does not look inside packages and will
    > >>only find files at the root of classpath entries, unless the resource is
    > >>specified with a path and that's something we currently can't do. There
    > >>are ways around that (I just implemented a nifty solution that does 99% of
    > >>what you want) but one obscure problem remains, so for the time being I
    > >>recommend you put the config files where they work.

    I suggested using new DefaultConfiguration(new File("/full/path")) but
    that does not work as expected either.

    Andrus Adamchik wrote:
    > I think such code will be very useful for things like locating DbAdapter
    > implementations and/or JDBC driver implementations in the outside jar
    > files in the Modeler. But scanning all packages to locate project

    My thoughts! I used something similar in WO once to dynamically iterate
    through all classes of a WOFramework and find plugins. It was actually
    very fast (~2 sec) when combined with a lookup cache. It's a shame
    something like this (or better) is not part of the standard classes.

    > configuration file (cayenne.xml) seems a bit risky and adds
    > non-deterministic factor to an already convoluted CLASSPATH/ClassLoader mix.

    Yes, and after experimenting further with Paul's problem I concluded that
    simply scanning all packages is not only error-prone with duplicates, but
    also it won't even work correctly, since Package.getPackages() only
    returns packages that have been used by a classloader to load classes.
    Even Package.getPackage("not.yet.loaded") does not trigger its loading,
    you have to access an existing class inside that package. No workaround.

    Soo..I really thought about what Paul wanted to achieve. Until now it
    never occurred to me that having the config files on the top level of the
    project would be a problem, but it really is the norm if you think
    further. There's no good reason not to have several configuration 'sets'
    for a single application on the classpath at the same time, and picking
    one e.g. at startup; this can be achieved by simply using packages (which
    are nothing but namespaces, just like directories) and a single class in
    that package (to enable package loading). This class just needs to be an
    empty subclass of my new PackageConfiguration that I just checked into my
    sandbox. Please have a look at it.
    Usage is simple: Configuration.initSharedConfig(new
    MyPackageConfiguration()) where MyPackageConfiguration is an empty
    subclass in its own package, together with all the config files. No other
    changes are necessary. Unfortunately, until I had this working, I had to
    look at all related classes and figure out exactly how they were working -
    with the current classes 'simply subclassing' is IMHO really obscure. :(
    But it can be fixed easily, even without breaking anything! :)

    There are several things that make config customization for the beginner
    harder than necessary. getDomainConfig() and getMapConfig(location) both
    return an InputStream, which is source-agnostic (good) but unfortunately
    makes it hard to find out where the data actually comes from. This is
    necessary because DriverDataSourceFactory makes the assumption that the
    DataSource with a "name" can be loaded from exactly that same file 'in the
    current directory'. This can be solved by overriding 'load(String)' (again
    see the sandbox) but it took me ~2 hours just to find out who is loading
    what, when, where, why and how, and that I have to provide my own
    DriverDataSourceFactory subclass in addition to a custom config.
    Another problem for subclassers might be that DefaultConfiguration IMHO
    does too many things at once - both classpath loading and file loading.
    Classpath loading only looks into the toplevel of the classpath and all
    jars. File loading does not work at all (!) because the DataSource won't
    be found, this is actually the same bug as already described (no path
    information available in RuntimeLoadDelegate.shouldLoadDataNode()). We
    could just delete this Constructor and functionality or fix it (better).
    DefaultConfiguration's projectFile() and projectDir() both return File,
    which makes it hard to fix the classpath loading. Using a single mechanism
    (most likely URL, in my sandbox I've used Strings) might be easier to use
    and is definitely easier to subclass: for example if you want to name your
    project file differently than "cayenne.xml" you don't have to rewrite the
    full loading mechanism.
    ResourceLocator could be enhanced later with a 'recursive' flag that does
    the deep search over all known packages, as I did at first.

    Please have a look at the two (very short!) classes in sandbox/holger/..
    and let me know what you think. Having all config files in a project
    package is IMHO very common, works nicely and will not put off newbies
    because they cannot get the config working the way they expect to. It's
    unfortunate that we don't have NSBundle and all its niftyness, but that
    doesn't mean we shouldn't solve the problem. ;-)

    thanks for reading,
    Holger



    This archive was generated by hypermail 2.0.0 : Wed Feb 26 2003 - 16:16:05 EST