2014-04-15 7 views
7

मैं रनटाइम पर जावा में ग्रोवी को संकलित करने में सक्षम हूं और इसे डेटाबेस में संग्रहीत करता हूं और इसे खींचता हूं। मैं ग्रोवी क्लास को संकलित नहीं कर सकता अगर इसमें आंतरिक कक्षाएं या आंतरिक enum है। क्या किसी ने सफलतापूर्वक ग्रोवी कोड को संकलित किया है और इसमें आंतरिक कक्षाएं/enums शामिल हैं और स्क्रिप्ट को कक्षा नाम से खींचने में सक्षम हैं?जावा में रनटाइम पर ग्रोवी क्लास संकलित करें

उदाहरण के लिए, मैं नीचे दिखाया गया "टेस्ट" स्क्रिप्ट लोड करना चाहता हूं जिसमें आंतरिक कक्षाएं हैं और रन टाइम पर स्क्रिप्ट चलाएं।

संकलक कोड:

Class groovyClass = app.getGroovyScript(className, compiledScript); 
TestScript script = (TestScript) groovyClass.newInstance(); 
System.out.println(script.getMessage()); 

ग्रूवी स्क्रिप्ट:

import com.groovy.groovy.TestScript 

class Test implements TestScript { 

    String getMessage() { 
     [1..10].each(){ 
      println it 
     } 
     return "Jello" 
    } 
} 
स्क्रिप्ट चलाने के लिए

public Class getGroovyScript(final String className, final byte[] script) { 
    Class clazz = null; 

    try (GroovyClassLoader classLoader = new GroovyClassLoader(this.getClass().getClassLoader())) { 
     clazz = classLoader.defineClass(className, script); 
    } catch (IOException e) { 
    } catch (Exception e) { 
    } 

    return clazz; 
} 

कोड:

public byte[] compileGroovyScript(final String className, final String script) { 
    byte[] compiledScriptBytes = null; 
    CompilationUnit compileUnit = new CompilationUnit(); 
    compileUnit.addSource(className, script); 
    compileUnit.compile(Phases.CLASS_GENERATION); 

    for (Object compileClass : compileUnit.getClasses()) { 
     GroovyClass groovyClass = (GroovyClass) compileClass; 
     compiledScriptBytes = groovyClass.getBytes(); 
    } 

    return compiledScriptBytes; 
} 

कोड स्क्रिप्ट बाहर निकलने के लिए

+0

आप संकलन से वर्गों पर पुनरावृत्ति करते हैं, लेकिन आप अंतिम श्रेणी 'compiledScriptBytes = groovyClass.getBytes() से केवल बाइट्स लौटते हैं;' मुझे नहीं पता कि यह मामला है, लेकिन यह एक संभावित बग की तरह दिखता है। – airborn

+0

वैसे मैंने सभी कक्षाओं में पुनरावृत्ति की कोशिश की और उन्हें एक बाइट [] में संग्रहित करने की कोशिश की [लेकिन यह ग्रोवी क्लास प्राप्त करने और इसे मेरे जावा इंटरफेस में कास्टिंग करते समय काम नहीं करता था। – ColinMc

उत्तर

5

यह आप अपने आप को संकलन क्यों कर रहे हैं वर्णन से स्पष्ट नहीं है। तुम सिर्फ ग्रूवी तो यह तुम्हारे लिए क्या कर सकते हैं, तो पूरी बात सिर्फ कुछ इस तरह करने के लिए सरल किया जा सकता है:

String script = // string containing the script you want to parse 

GroovyClassLoader groovyClassLoader = new GroovyClassLoader(); 
Class theParsedClass = groovyClassLoader.parseClass(script); 
+1

मैंने उल्लेख किया कि मैं ग्रोवी स्क्रिप्ट को संकलित करना चाहता हूं और उन्हें डेटाबेस में रखना चाहता हूं। इसका कारण प्रदर्शन के लिए है, इसलिए जब मैं एक स्क्रिप्ट चलाने के लिए चाहता हूं तो मुझे इसे कई बार संकलित करने की आवश्यकता नहीं है। स्क्रिप्ट प्रत्येक मिनट चलती हैं और हर मिनट एक स्क्रिप्ट संकलित अक्षम होती है। – ColinMc

+0

अगर मुझे स्क्रिप्ट को संकलित करने और उन्हें स्टोर करने की आवश्यकता नहीं है, तो यह काम करेगा। – ColinMc

+0

यदि हर मिनट आप डेटाबेस से बाइट्स पुनर्प्राप्त कर रहे हैं और फिर कक्षाओं में उन बाइट्स को चालू करने के लिए defineClass() को कॉल कर रहे हैं तो आप शायद उस पर सुधार कर सकते हैं। क्या आपका आवेदन ऐसा है कि ऐसा कोई कारण है कि आप कक्षा लोडर तक नहीं पहुंच सकते हैं जो हर बार आपको स्क्रिप्ट क्लास तक पहुंच की आवश्यकता होती है? जब तक आप ऐसा कर सकते हैं तब तक आप definClass() चीज़ को कक्षा में अपने बाइट्स को हर बार चलाने के लिए इसे चलाने के लिए बाध्य करने से बच सकते हैं। आप इसे एक बार और फिर हर बार कर सकते हैं जब आपको उस स्क्रिप्ट को चलाने की आवश्यकता होती है, कक्षा कक्षा लोडर में पहले से ही उपलब्ध होगी। –

0

इस सादगी यहाँ की खातिर निपटने किसी भी त्रुटि forgoes, लेकिन यह शायद तुम क्या चाहते है:

public byte[] compileGroovyScript(final String className, final String script) { 
    byte[] compiledScriptBytes = null; 
    CompilationUnit compileUnit = new CompilationUnit(); 
    compileUnit.addSource(className, script); 
    compileUnit.compile(Phases.CLASS_GENERATION); 

    List classes = compileUnit.getClasses(); 
    GroovyClass firstClass = (GroovyClass)classes.get(0); 
    compiledScriptBytes = firstClass.getBytes(); 

    return compiledScriptBytes; 
} 
0

अपनी आवश्यकताओं के आधार पर, आप इनर क्लासों के लिए पहुँच प्रदान करना चाह सकते हैं और तुम कर सकते हो कि साथ कुछ इस तरह जो बजाय प्रथम श्रेणी संभालने के मेल खाने वाले नाम वर्ग पाता है:

public byte[] compileGroovyScript(final String className, final String script) { 
    byte[] compiledScriptBytes = null; 
    CompilationUnit compileUnit = new CompilationUnit(); 
    compileUnit.addSource(className, script); 
    compileUnit.compile(Phases.CLASS_GENERATION); 

    for (Object compileClass : compileUnit.getClasses()) { 
     GroovyClass groovyClass = (GroovyClass) compileClass; 
     if(className.equals(groovyClass.getName())) { 
      compiledScriptBytes = groovyClass.getBytes(); 
      break; 
     } 

    } 

    return compiledScriptBytes; 
} 
0

मैंने इसकी जांच अपने आप चला रहा हूँ लेकिन सिर्फ रनटाइम पर एक मांग पर जावा संकलक किया, मैं आप एक ही मुद्दे में चल रहे हैं विश्वास है कि मैं इस कोड में हल

https://github.com/deanhiller/webpieces/tree/master/runtimecompile/src/main/java/org/webpieces/compiler/api

webpieces/runtimecompile एक फिर से प्रयोग करने योग्य मांग पर जावा ग्रहण संकलक का उपयोग कर संकलक है।

अब, ग्रूवी के लिए, मुझे लगता है कि आप इस मामले

1. you compile ONE script 
2. this results in 'multiple' class file objects (I think) just like mine did 
3. This is where you need to store EACH in the database SEPARATELY 
4. Then you need a classloader that tries to lookup the 'inner classes' when jvm asks for it 
5. finally you do a yourclassLoader.loadApplicationClass (much like the one in CompileOnDemandImpl.java in the project above 
6. To be clear, step 5 causes step 4 to happen behind the scenes (and that is what is confusing). 

आप परीक्षण का मामला AnonymousByteCacheTest, यह काफी ऐसा ही कुछ कर रहा है के माध्यम से कदम तो चल रहे हैं।

आपको उस प्रोजेक्ट पर निर्माण चलाने के लिए कुछ भी इंस्टॉल करने की आवश्यकता नहीं है, बस इसे क्लोन करें और "./gradlew test" और पास हो जाएगा और "./gradlew ग्रहण" या "./gradlew विचार" और यह उत्पन्न करता है आईडीई फाइलें ताकि आप इसके माध्यम से कदम उठा सकें।

यह बहुत ही समान है। मैं अपने आप के बाद काम कर रहे groovy संस्करण पाने की कोशिश कर रहा हूँ।

3

ठीक है यह थोड़ा देर हो सकता है लेकिन उम्मीद है कि यह अगले व्यक्ति की मदद करेगा। मुझे लगता है कि आपको प्रत्येक ग्रोवी क्लास के लिए एक सूची सहेजने की आवश्यकता है और फिर cl.defineClass और अंत में cl.loadClass। मुझे लगता है कि जब कभी मैं सोर्स() जोड़ता हूं, तो मैं कभी-कभी कक्षाओं की सूची में संकलित करता हूं, मैं एक वर्ग जोड़ता हूं और फिर उस फ़ाइल से सभी जेनरेट किए गए वर्गों पर लूप करता हूं।

यह cl.defineClass कॉल की

GroovyClassLoader cl = new GroovyClassLoader(); 
    CompilationUnit compileUnit = new CompilationUnit(); 
    compileUnit.addSource(scriptCode.getClassName(), scriptCode.getScriptSourceCode()); 
    compileUnit.compile(Phases.CLASS_GENERATION); 
    compileUnit.setClassLoader(cl); 

    GroovyClass target = null; 
    for (Object compileClass : compileUnit.getClasses()) { 
     GroovyClass groovyClass = (GroovyClass) compileClass; 
     cl.defineClass(groovyClass.getName(), groovyClass.getBytes()); 
     if(groovyClass.getName().equals(scriptCode.getClassName())) { 
      target = groovyClass; 
     } 
    } 

    if(target == null) 
     throw new IllegalStateException("Could not find proper class"); 

    return cl.loadClass(target.getName()); 

ध्यान में रखना जो classloader में वर्ग डालता है तो जब (हालांकि मैं बचत और बाद में पुन: लोड प्रयास नहीं किया है) कोड मैं वर्तमान में चल रहा है यह देखा जाता है (enum या आंतरिक वर्ग), यह वहाँ होगा।

और इसलिए अब मुझे लगता है कि आपको अपना खुद का वर्ग लोडर बनाने की आवश्यकता नहीं है (हालांकि आप बेकार परिभाषित क्लास से बचें जब तक कि यह आपके अपने क्लासलोडर के साथ जरूरी न हो, जो उपयोगी और अधिक प्रदर्शनकारी हो)।

संबंधित मुद्दे