groovy
  1. groovy
  2. GROOVY-3133

please, provide a groovy.lang.GroovyClassLoader.ClassCollector.generatedClass atribute getter

    Details

    • Type: Improvement Improvement
    • Status: Open Open
    • Priority: Trivial Trivial
    • Resolution: Unresolved
    • Affects Version/s: 1.5.7
    • Fix Version/s: None
    • Component/s: class generator
    • Labels:
      None
    • Environment:
      doesn't matter
    • Number of attachments :
      0

      Description

      I'm writing a Class that extends groovy.lang.GroovyClassLoader, just to make a personalized Loader (that will load classes from database, rather than the filesystem).
      In my subclass I had to override a parseClass() method from GroovyClassloader and I wish to reutilize the pre-existing groovy.lang.GroovyClassLoader.ClassCollector class, to collect my generated class, but there is no getter for the "generatedClass" atribute in this class!

      I know I can do this on my own, but I will have to re-pack it (since I use the groovy-all-xxx.jar) every time I want to upgrade to the latest version!

      Please, you could make a getter for it or at least, turn this protected instead of private in the following versions.
      Will made life easier to extend this class without rewritting a couple of things!

        Activity

        Hide
        blackdrag blackdrag added a comment -

        I don't get a couple of things ...

        • why do you have to overwrite parseClass in a way hat you don't call the original method and
        • why can't you set a ResourceLoader?
        • why do you have to repack?
        Show
        blackdrag blackdrag added a comment - I don't get a couple of things ... why do you have to overwrite parseClass in a way hat you don't call the original method and why can't you set a ResourceLoader? why do you have to repack?
        Hide
        Marcius da Silva da Fonseca added a comment - - edited

        I have a couple of class sources in a table on the database.

        In a very simple example:
        if I have a "Class_A" that depends on "Class_B", and I do...

        ParseClass("Class_A");

        ...groovy will see that Class_A depends on Class_B, but if a had not set a classpath nor added a URL with "addURL(myURL)", groovy will not be able to resolve de dependency(ies).
        I can't say to a GroovyClassLoader "my classpath is this table in my database" nor do something like addSource(an sourcecode got from my table).

        The interface GroovyResourceLoader only has one method that returns a URL, but my classes are in DB... how could it help-me? I wanted to give a classname to it and receive the actual sourcecode, since my classes are in database, not in the filesystem...

        So, I solved this creating a class that I called GroovyDBClassLoader (extends GroovyClassLoader) that overwrites parseClass() in a way that when I pass a classname to it, it will get the sourcecode for the given classname from the database, fetch its dependencies and parse everything (including the dependencies) in the same CompilationUnit and return the generatedClass.

        To do this, I needed access the "generatedClass" attribute in the groovy.lang.GroovyClassLoader.ClassCollector, but it is privated...

        Well, so I got the groovy sources, modified it creating a getter to "generatedClass" at groovy.lang.GroovyClassLoader.ClassCollector, and repack it in a groovy-all-xxx.jar

        here is a resume of my GroovyDBClassLoader:

        public class GroovyDBClassLoader extends GroovyClassLoader {
            
            // ... here lies some omited code ....
            
            public Class parseDBClass(String classname, boolean shouldParseDeps,
                    boolean shouldCacheSource) throws ClassNotFoundException
            {
                synchronized (sourceCache) {
                    Class answer = (Class) sourceCache.get(classname);
                    if (answer != null) {
                        return answer;
                    }
        
                    // Was neither already loaded nor compiled,
                    // so compile and add to cache.
        
                    // get the source-code(s)
                    Map<String, GroovyCodeSource> codeSources;
        
                    if (shouldParseDeps) {
                        // will load all dependant classes from database, starting from the root "classname"
                        codeSources = resourceLoader.loadSourceAndDeps(classname);
                    } else {
                        // will load only the root "classname"
                        // usefull when the class we want to parse is self-contained...
                        codeSources = new HashMap<String, GroovyCodeSource>();
                        codeSources.put(classname, resourceLoader.loadSource(classname));
                    }
        
                    // the root class will be the aswer of this method, but the dependencies will be
                    // loaded as well (and put in the classCache)
                    GroovyCodeSource mainCodeSource = codeSources.remove(classname);
        
                    // create a compilation unit
                    CompilationUnit unit = createCompilationUnit(config,
                            mainCodeSource.getCodeSource());
        
                    // add the main source. su points to the root class
                    SourceUnit su = unit.addSource(mainCodeSource.getName(),
                            mainCodeSource.getInputStream());
        
                    // add the dependencies
                    Collection<GroovyCodeSource> dependencies = codeSources.values();
                    for (GroovyCodeSource cs : dependencies) {
                        // if some of the dependencies is loaded already, dont parse it again...
                        if (classCache.get(cs.getName()) == null) {
                            unit.addSource(cs.getName(), cs.getInputStream());
                        }
                    }
        
                    // create a class collector to get the classes after compilation
                    GroovyClassLoader.ClassCollector collector = createCollector(unit, su);
                    unit.setClassgenCallback(collector);
        
                    // set the compilation goal phase
                    int goalPhase = Phases.CLASS_GENERATION;
                    if (config != null && config.getTargetDirectory() != null) {
                        goalPhase = Phases.OUTPUT;
                    }
        
                    // compile
                    unit.compile(goalPhase);
        
                    // the class to return
                    // HERE IS WERE I NEEDED TO ACCESS THE "generatedClass" FIELD!!!
                    answer = collector.getGeneratedClass();
        
                    // if the class to return is cacheable, put it into the cache.
                    if (mainCodeSource != null && mainCodeSource.isCachable()) {
                        sourceCache.put(classname, answer);
                    }
        
                    // make all dependencies avaliable
                    Collection<Class> loadedClasses = collector.getLoadedClasses();
                    for (Class clazz : loadedClasses) {
                        setClassCacheEntry(clazz);
        
                        // if the dependency classes are cacheable, put them into the cache.
                        String clazzname = clazz.getName();
                        GroovyCodeSource cs = codeSources.remove(clazzname);
                        if (cs != null && cs.isCachable()) {
                            sourceCache.put(clazzname, clazz);
                        }
                    }
                    return answer;
                }
            }
            // ... some more code ...
        }
        

        as you can see, I use a ResourceLoader, but this one is not the GroovyResourceLoader (cause it returns only URLs). I've written my on DatabaseResourceLoader...

        Well... here is a resume of what I'm trying to do (with sample codes):
        http://www.groovy-forum.org/viewtopic.php?t=511&sid=847d91df6a3a95ca53875c4bdfd07d99

        thanks

        Show
        Marcius da Silva da Fonseca added a comment - - edited I have a couple of class sources in a table on the database. In a very simple example: if I have a "Class_A" that depends on "Class_B", and I do... ParseClass("Class_A"); ...groovy will see that Class_A depends on Class_B, but if a had not set a classpath nor added a URL with "addURL(myURL)", groovy will not be able to resolve de dependency(ies). I can't say to a GroovyClassLoader "my classpath is this table in my database" nor do something like addSource(an sourcecode got from my table). The interface GroovyResourceLoader only has one method that returns a URL, but my classes are in DB... how could it help-me? I wanted to give a classname to it and receive the actual sourcecode, since my classes are in database, not in the filesystem... So, I solved this creating a class that I called GroovyDBClassLoader (extends GroovyClassLoader) that overwrites parseClass() in a way that when I pass a classname to it, it will get the sourcecode for the given classname from the database, fetch its dependencies and parse everything (including the dependencies) in the same CompilationUnit and return the generatedClass. To do this, I needed access the "generatedClass" attribute in the groovy.lang.GroovyClassLoader.ClassCollector, but it is privated... Well, so I got the groovy sources, modified it creating a getter to "generatedClass" at groovy.lang.GroovyClassLoader.ClassCollector, and repack it in a groovy-all-xxx.jar here is a resume of my GroovyDBClassLoader: public class GroovyDBClassLoader extends GroovyClassLoader { // ... here lies some omited code .... public Class parseDBClass( String classname, boolean shouldParseDeps, boolean shouldCacheSource) throws ClassNotFoundException { synchronized (sourceCache) { Class answer = ( Class ) sourceCache.get(classname); if (answer != null ) { return answer; } // Was neither already loaded nor compiled, // so compile and add to cache. // get the source-code(s) Map< String , GroovyCodeSource> codeSources; if (shouldParseDeps) { // will load all dependant classes from database, starting from the root "classname" codeSources = resourceLoader.loadSourceAndDeps(classname); } else { // will load only the root "classname" // usefull when the class we want to parse is self-contained... codeSources = new HashMap< String , GroovyCodeSource>(); codeSources.put(classname, resourceLoader.loadSource(classname)); } // the root class will be the aswer of this method, but the dependencies will be // loaded as well (and put in the classCache) GroovyCodeSource mainCodeSource = codeSources.remove(classname); // create a compilation unit CompilationUnit unit = createCompilationUnit(config, mainCodeSource.getCodeSource()); // add the main source. su points to the root class SourceUnit su = unit.addSource(mainCodeSource.getName(), mainCodeSource.getInputStream()); // add the dependencies Collection<GroovyCodeSource> dependencies = codeSources.values(); for (GroovyCodeSource cs : dependencies) { // if some of the dependencies is loaded already, dont parse it again... if (classCache.get(cs.getName()) == null ) { unit.addSource(cs.getName(), cs.getInputStream()); } } // create a class collector to get the classes after compilation GroovyClassLoader.ClassCollector collector = createCollector(unit, su); unit.setClassgenCallback(collector); // set the compilation goal phase int goalPhase = Phases.CLASS_GENERATION; if (config != null && config.getTargetDirectory() != null ) { goalPhase = Phases.OUTPUT; } // compile unit.compile(goalPhase); // the class to return // HERE IS WERE I NEEDED TO ACCESS THE "generatedClass" FIELD!!! answer = collector.getGeneratedClass(); // if the class to return is cacheable, put it into the cache. if (mainCodeSource != null && mainCodeSource.isCachable()) { sourceCache.put(classname, answer); } // make all dependencies avaliable Collection< Class > loadedClasses = collector.getLoadedClasses(); for ( Class clazz : loadedClasses) { setClassCacheEntry(clazz); // if the dependency classes are cacheable, put them into the cache. String clazzname = clazz.getName(); GroovyCodeSource cs = codeSources.remove(clazzname); if (cs != null && cs.isCachable()) { sourceCache.put(clazzname, clazz); } } return answer; } } // ... some more code ... } as you can see, I use a ResourceLoader, but this one is not the GroovyResourceLoader (cause it returns only URLs). I've written my on DatabaseResourceLoader... Well... here is a resume of what I'm trying to do (with sample codes): http://www.groovy-forum.org/viewtopic.php?t=511&sid=847d91df6a3a95ca53875c4bdfd07d99 thanks
        Hide
        Marcius da Silva da Fonseca added a comment -

        Oh... I forgot to say that I don't ovewrite a parseClass() method at all.
        My extended Class only adds some more parseClass() methods (leaving the originals as they are) to add more functionality to the original GroovyClassLoader.

        Show
        Marcius da Silva da Fonseca added a comment - Oh... I forgot to say that I don't ovewrite a parseClass() method at all. My extended Class only adds some more parseClass() methods (leaving the originals as they are) to add more functionality to the original GroovyClassLoader.
        Hide
        blackdrag blackdrag added a comment -

        first about URL. Your URL could be something like "db://table/index" and if you give in a custom URLStreamHandler it will work. See http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html#URL(java.net.URL,%20java.lang.String,%20java.net.URLStreamHandler) and http://java.sun.com/j2se/1.4.2/docs/api/java/net/URLStreamHandler.html This way it is possible to wire in database access.

        You could also directly use the CompilationUnit. It is useful to use the GroovyClassLoader, but only when most calls are not to parseClass, but to loadClass and you want Groovy to load a script automatically on demand.

        Show
        blackdrag blackdrag added a comment - first about URL. Your URL could be something like "db://table/index" and if you give in a custom URLStreamHandler it will work. See http://java.sun.com/j2se/1.4.2/docs/api/java/net/URL.html#URL(java.net.URL,%20java.lang.String,%20java.net.URLStreamHandler ) and http://java.sun.com/j2se/1.4.2/docs/api/java/net/URLStreamHandler.html This way it is possible to wire in database access. You could also directly use the CompilationUnit. It is useful to use the GroovyClassLoader, but only when most calls are not to parseClass, but to loadClass and you want Groovy to load a script automatically on demand.
        Hide
        blackdrag blackdrag added a comment -

        the groovy forum url seems not to be working anymore

        Show
        blackdrag blackdrag added a comment - the groovy forum url seems not to be working anymore

          People

          • Assignee:
            blackdrag blackdrag
            Reporter:
            Marcius da Silva da Fonseca
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated: