NetLogo / LevelSpace

This is the LevelSpace extension repository. LevelSpace allows you to run NetLogo models |: from inside NetLogo models :|
Other
19 stars 8 forks source link

ls:reset does not close down child models of child models #19

Closed arthurhjorth closed 10 years ago

arthurhjorth commented 10 years ago

I've been working on this for a while now, but I've run into a strange problem. It seems like Java doesn't recognize classes of Extensions in the ExtensionsManager. Not sure how to explain it, but here is my setup and code (I replaced print statements with comments).

I load a GUI model of a custom Wolf Sheep Predation that loads the LevelsSpace extension, and then loads Bounce Example.nlogo (i.e. the latter is the child of the former, and the former is the child of the main App.) My goal is to have all models be closed at the call of this method:

    static void killChildModels( HashMap<Integer, LevelsModelAbstract> models){
        // Iterate through child models, iterate through their child models, and kill them all.
        for (LevelsModelAbstract aModel : models.values()){
            if (aModel instanceof LevelsModelComponent){
                LevelsModelComponent theModel = (LevelsModelComponent)aModel;
                Iterable<ClassManager> extensions = theModel.myWS.workspace().getExtensionManager().loadedExtensions();
                for (ClassManager cm : extensions){
// This is where things get weird. If I print cm.getClass.toString() I get "LevelsSpace". But the object still never passes the conditional below.
// If I cast cm as LevelsSpace, I get ClassCastException "Cannot cast LevelsSpace as LevelsSpace"
                    if (cm instanceof LevelsSpace){
                        LevelsSpace ls = (LevelsSpace)cm;
                        HashMap<Integer, LevelsModelAbstract> theModels = ls.myModels;
                        killChildModels(theModels);
                        resetModels(ls);            
                    }
                }
                theModel.kill();
            }
          }

Does anyone have any idea what could be causing this?

SethTisue commented 10 years ago

Sounds like classloader problems — the same .class file on disk, loaded into memory twice in two different class loaders, results in two Class objects that have the same name, but are not actually the same class, since they belong to different classloaders.

And NetLogo always loads each extension in its own private classloader, so that's why there are multiple loaders involved here.

One way to get around this would be to do your instanceof check other calls using reflection. It's kind of a hack, but you might decide it's the shortest path to working code.

SethTisue commented 10 years ago

It's not clear to me what a "real" fix might consist of. I guess it would involve finding some way to convince ExtensionManager to use the same class loader for a child models as it's already using for the parent. I don't know much about the internals of ExtensionManager; other people did the heavy lifting and setting up the class loader stuff in there, and I've never had to seriously mess with it.

arthurhjorth commented 10 years ago

Oh, that explains it. The problem isn't really the instanceof check, because I could just do a string comparison - it is that I can't cast the object so that I can use it. I'll try and use Reflection to get the class of the object and see if that works. Reflection might reference the classloader of the class that uses it though, but we'll see. It won't be super fast, but this is only for when people close models, which shouldn't be that frequently.

Thanks a lot!